RealtimeSession: execute server-side tools at response.done
This commit is contained in:
@@ -25,6 +25,7 @@ public class RealtimeSession
|
||||
private Conversation? _conversation;
|
||||
private EndReason _endReason = EndReason.Unknown;
|
||||
private bool _endRequested;
|
||||
private readonly List<(string CallId, string Name, string ArgsJson)> _pendingCalls = new();
|
||||
|
||||
private readonly System.Threading.Channels.Channel<byte[]> _uplink =
|
||||
System.Threading.Channels.Channel.CreateBounded<byte[]>(
|
||||
@@ -135,14 +136,77 @@ public class RealtimeSession
|
||||
await _log.AppendAssistantAsync(_conversation.Id, text!, ct);
|
||||
break;
|
||||
}
|
||||
case "response.function_call_arguments.done":
|
||||
{
|
||||
var callId = (string?)evt["call_id"] ?? "";
|
||||
var name = (string?)evt["name"] ?? "";
|
||||
var args = (string?)evt["arguments"] ?? "{}";
|
||||
_pendingCalls.Add((callId, name, args));
|
||||
break;
|
||||
}
|
||||
case "response.done":
|
||||
{
|
||||
await _output.WriteEnvelopeAsync(
|
||||
new JsonObject { ["type"] = "assistant_done" }, ct);
|
||||
|
||||
if (_pendingCalls.Count == 0) break;
|
||||
var calls = _pendingCalls.ToList();
|
||||
_pendingCalls.Clear();
|
||||
|
||||
if (calls.Any(c => c.Name == "end_session"))
|
||||
{
|
||||
_endReason = EndReason.Tool;
|
||||
_endRequested = true;
|
||||
break;
|
||||
}
|
||||
|
||||
foreach (var c in calls)
|
||||
{
|
||||
await ExecuteToolAsync(c.CallId, c.Name, c.ArgsJson, ct);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ExecuteToolAsync(string callId, string name, string argsJson, CancellationToken ct)
|
||||
{
|
||||
var tool = _registry.Get(name);
|
||||
string outputJson;
|
||||
if (tool is null)
|
||||
{
|
||||
outputJson = $$"""{"ok":false,"error":"unknown tool: {{name}}"}""";
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
using var argsDoc = System.Text.Json.JsonDocument.Parse(argsJson);
|
||||
var ctx = new DeviceContext(_deviceId, _conversation!.Id, _channel);
|
||||
var res = await tool.ExecuteAsync(
|
||||
new ToolInvocation(callId, argsDoc.RootElement.Clone()), ctx, ct);
|
||||
outputJson = res.Output.GetRawText();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
outputJson = System.Text.Json.JsonSerializer.Serialize(new
|
||||
{
|
||||
ok = false,
|
||||
error = ex.Message,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (_conversation is not null)
|
||||
{
|
||||
await _log.AppendToolAsync(_conversation.Id, name, argsJson, outputJson, ct);
|
||||
}
|
||||
|
||||
await _upstream.SendJsonAsync(RealtimeEvents.FunctionCallOutput(callId, outputJson), ct);
|
||||
await _upstream.SendJsonAsync(RealtimeEvents.ResponseCreate(), ct);
|
||||
}
|
||||
|
||||
private async Task ForwardAudioDeltaAsync(JsonObject evt, CancellationToken ct)
|
||||
{
|
||||
var b64 = (string?)evt["delta"];
|
||||
|
||||
Reference in New Issue
Block a user