fix(talk): accept optional realtime item ids

This commit is contained in:
Vincent Koc
2026-08-02 09:14:34 +08:00
parent b2d8f0d7ba
commit a648d58d76
2 changed files with 34 additions and 14 deletions
+29 -3
View File
@@ -906,7 +906,35 @@ describe("WebRtcSdpRealtimeTalkTransport", () => {
transport.stop();
});
it("requires response, item, call, name, and argument identities before executing tools", async () => {
it("accepts completed tool calls without optional response and item ids", async () => {
stubAnswerSdpFetch();
const request = vi.fn(async (method: string) => {
if (method === "talk.client.steer") {
return { ok: true, mode: "status" };
}
throw new Error(`unexpected request: ${method}`);
});
const transport = createOpenAiTransport({ request });
await transport.start();
dispatchCompletedToolCall(FakePeerConnection.instances[0], {
responseId: null,
itemId: null,
name: REALTIME_VOICE_AGENT_CONTROL_TOOL_NAME,
arguments: JSON.stringify({ text: "status" }),
});
await waitForFast(() =>
expect(request).toHaveBeenCalledWith("talk.client.steer", {
sessionKey: "main",
text: "status",
mode: "status",
}),
);
transport.stop();
});
it("requires call, name, and arguments before executing tools", async () => {
stubAnswerSdpFetch();
const request = vi.fn();
const transport = createOpenAiTransport({ request });
@@ -914,8 +942,6 @@ describe("WebRtcSdpRealtimeTalkTransport", () => {
await transport.start();
const peer = FakePeerConnection.instances[0];
for (const overrides of [
{ responseId: null, callId: "missing-response" },
{ itemId: null, callId: "missing-item" },
{ callId: null, itemId: "missing-call" },
{ name: null, callId: "missing-name", itemId: "missing-name" },
{ arguments: null, callId: "missing-args", itemId: "missing-args" },
+5 -11
View File
@@ -26,7 +26,7 @@ import {
} from "./realtime-talk-webrtc-support.ts";
type CompletedToolCall = {
itemId: string;
itemId?: string;
name: string;
callId: string;
args: string;
@@ -497,13 +497,7 @@ export class WebRtcSdpRealtimeTalkTransport implements RealtimeTalkTransport {
private handleCompletedResponse(event: RealtimeServerEvent): void {
const response: unknown = event.response;
if (
!isRecord(response) ||
response.status !== "completed" ||
typeof response.id !== "string" ||
!response.id.trim() ||
!Array.isArray(response.output)
) {
if (!isRecord(response) || response.status !== "completed" || !Array.isArray(response.output)) {
return;
}
for (const output of response.output) {
@@ -514,11 +508,11 @@ export class WebRtcSdpRealtimeTalkTransport implements RealtimeTalkTransport {
) {
continue;
}
const itemId = typeof output.id === "string" ? output.id.trim() : "";
const itemId = typeof output.id === "string" ? output.id.trim() || undefined : undefined;
const callId = typeof output.call_id === "string" ? output.call_id.trim() : "";
const name = typeof output.name === "string" ? output.name.trim() : "";
const args = typeof output.arguments === "string" ? output.arguments : "";
if (!itemId || !callId || !name || !args.trim()) {
if (!callId || !name || !args.trim()) {
continue;
}
if (
@@ -595,7 +589,7 @@ export class WebRtcSdpRealtimeTalkTransport implements RealtimeTalkTransport {
}
}
private async handleDescribeViewToolCall(callId: string, itemId: string): Promise<void> {
private async handleDescribeViewToolCall(callId: string, itemId?: string): Promise<void> {
this.emitTalkEvent({
type: "tool.call",
callId,