






















@@ -1,7 +1,9 @@
11import { randomUUID } from "node:crypto";
22import {
33ActivityHandling,
4+Behavior,
45EndSensitivity,
6+FunctionResponseScheduling,
57Modality,
68StartSensitivity,
79TurnCoverage,
@@ -20,8 +22,14 @@ import type {
2022RealtimeVoiceProviderConfig,
2123RealtimeVoiceProviderPlugin,
2224RealtimeVoiceTool,
25+RealtimeVoiceToolResultOptions,
26+} from "openclaw/plugin-sdk/realtime-voice";
27+import {
28+convertPcmToMulaw8k,
29+mulawToPcm,
30+REALTIME_VOICE_AGENT_CONSULT_TOOL_NAME,
31+resamplePcm,
2332} from "openclaw/plugin-sdk/realtime-voice";
24-import { convertPcmToMulaw8k, mulawToPcm, resamplePcm } from "openclaw/plugin-sdk/realtime-voice";
2533import { normalizeResolvedSecretInputString } from "openclaw/plugin-sdk/secret-input";
2634import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
2735import { createGoogleGenAI } from "./google-genai-runtime.js";
@@ -288,11 +296,17 @@ function buildRealtimeInputConfig(
288296}
289297290298function buildFunctionDeclarations(tools: RealtimeVoiceTool[] | undefined): FunctionDeclaration[] {
291-return (tools ?? []).map((tool) => ({
292-name: tool.name,
293-description: tool.description,
294-parametersJsonSchema: tool.parameters,
295-}));
299+return (tools ?? []).map((tool) => {
300+const declaration: FunctionDeclaration = {
301+name: tool.name,
302+description: tool.description,
303+parametersJsonSchema: tool.parameters,
304+};
305+if (tool.name === REALTIME_VOICE_AGENT_CONSULT_TOOL_NAME) {
306+declaration.behavior = Behavior.NON_BLOCKING;
307+}
308+return declaration;
309+});
296310}
297311298312function parsePcmSampleRate(mimeType: string | undefined): number {
@@ -306,6 +320,8 @@ function isMulawSilence(audio: Buffer): boolean {
306320}
307321308322class GoogleRealtimeVoiceBridge implements RealtimeVoiceBridge {
323+readonly supportsToolResultContinuation = true;
324+309325private session: GoogleLiveSession | null = null;
310326private connected = false;
311327private sessionConfigured = false;
@@ -448,7 +464,11 @@ class GoogleRealtimeVoiceBridge implements RealtimeVoiceBridge {
448464this.sendUserMessage(greetingPrompt);
449465}
450466451-submitToolResult(callId: string, result: unknown): void {
467+submitToolResult(
468+callId: string,
469+result: unknown,
470+options?: RealtimeVoiceToolResultOptions,
471+): void {
452472if (!this.session) {
453473return;
454474}
@@ -462,19 +482,34 @@ class GoogleRealtimeVoiceBridge implements RealtimeVoiceBridge {
462482return;
463483}
464484try {
485+const isConsultTool = name === REALTIME_VOICE_AGENT_CONSULT_TOOL_NAME;
486+const functionResponse: FunctionResponse = {
487+id: callId,
488+ name,
489+response:
490+result && typeof result === "object" && !Array.isArray(result)
491+ ? (result as Record<string, unknown>)
492+ : { output: result },
493+};
494+if (isConsultTool) {
495+functionResponse.scheduling = FunctionResponseScheduling.WHEN_IDLE;
496+if (options?.willContinue === true) {
497+functionResponse.willContinue = true;
498+}
499+} else if (options?.willContinue === true) {
500+this.config.onError?.(
501+new Error(
502+`Google Live continuation is only supported for ${REALTIME_VOICE_AGENT_CONSULT_TOOL_NAME}`,
503+),
504+);
505+return;
506+}
465507this.session.sendToolResponse({
466-functionResponses: [
467-{
468-id: callId,
469- name,
470-response:
471-result && typeof result === "object" && !Array.isArray(result)
472- ? (result as Record<string, unknown>)
473- : { output: result },
474-},
475-],
508+functionResponses: [functionResponse],
476509});
477-this.pendingFunctionNames.delete(callId);
510+if (options?.willContinue !== true) {
511+this.pendingFunctionNames.delete(callId);
512+}
478513} catch (error) {
479514this.config.onError?.(
480515error instanceof Error ? error : new Error("Failed to send Google Live function response"),
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。