@openclaw/ai 2026.9.6 → 2026.9.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{anthropic-DZQ_7gD1.mjs → anthropic-CTXJlfnS.mjs} +37 -66
- package/dist/{anthropic-payload-policy-DyfwikYJ.d.mts → anthropic-payload-policy-NIPVYXPq.d.mts} +7 -5
- package/dist/{anthropic-stream-reducer-D6ZI1wwj.mjs → anthropic-stream-reducer-DrsBCsFY.mjs} +192 -261
- package/dist/{api-registry-C2T3lTV7.d.mts → api-registry-D-nbmSqr.d.mts} +2 -2
- package/dist/{azure-openai-responses-DS86GYzq.mjs → azure-openai-responses-BBxv4QVI.mjs} +22 -33
- package/dist/{base64-CPmadnUf.mjs → base64-C1PsYcRQ.mjs} +15 -9
- package/dist/{diagnostics-CH8j2wIG.mjs → diagnostics-CJaB6g8J.mjs} +3 -3
- package/dist/diagnostics.d.mts +4 -4
- package/dist/diagnostics.mjs +2 -2
- package/dist/{env-api-keys-bktO00EJ.mjs → env-api-keys-CyX4VKGO.mjs} +7 -5
- package/dist/event-stream-CMQfX_yr.d.mts +1 -0
- package/dist/{event-stream-CjnIZZst.d.mts → event-stream-DOPFYVqs.d.mts} +2 -2
- package/dist/{event-stream-D8PARQfL.mjs → event-stream-vd9UnCAU.mjs} +10 -0
- package/dist/event-stream.d.mts +2 -2
- package/dist/event-stream.mjs +1 -1
- package/dist/{google-BZJFjhuC.mjs → google-B3HEK0Ag.mjs} +8 -11
- package/dist/google-interactions-vMlEfPAA.mjs +574 -0
- package/dist/{google-messages-DB7GKkDk.mjs → google-messages-Dz2dAnGo.mjs} +35 -71
- package/dist/{google-shared-Bok4I6zB.mjs → google-shared-B58i3c8H.mjs} +43 -47
- package/dist/{google-vertex-3eVNR2qW.mjs → google-vertex-DYFXzEbh.mjs} +16 -30
- package/dist/{host-yTvBrYM2.mjs → host-DwBmNMCC.mjs} +29 -188
- package/dist/{host-policy-DZO3oPPk.mjs → host-policy-wK-ay_RK.mjs} +2 -14
- package/dist/{host-DH-Z-LDb.d.mts → host-rxlNaBwm.d.mts} +9 -3
- package/dist/{index-C25RT6bP.d.mts → index-D6KsSE74.d.mts} +28 -8
- package/dist/index.d.mts +7 -7
- package/dist/index.mjs +6 -6
- package/dist/internal/anthropic.d.mts +8 -7
- package/dist/internal/anthropic.mjs +5 -5
- package/dist/internal/openai-completions-compat.d.mts +1 -1
- package/dist/internal/openai-completions-compat.mjs +1 -1
- package/dist/internal/openai-responses-payload-policy.d.mts +1 -1
- package/dist/internal/openai-responses-payload-policy.mjs +2 -2
- package/dist/internal/openai.d.mts +31 -8
- package/dist/internal/openai.mjs +13 -13
- package/dist/internal/runtime.d.mts +31 -70
- package/dist/internal/runtime.mjs +10 -65
- package/dist/internal/shared.d.mts +14 -12
- package/dist/internal/shared.mjs +5 -4
- package/dist/internal/tool-schema.d.mts +1 -1
- package/dist/internal/tool-schema.mjs +2 -2
- package/dist/{mistral-C_13dVBn.mjs → mistral-7wDWcjfF.mjs} +46 -93
- package/dist/{openai-chatgpt-responses-sTxPl4zM.mjs → openai-chatgpt-responses-Bc7NZrQR.mjs} +26 -30
- package/dist/{openai-completions-BO6D_hrB.mjs → openai-completions-CHnwIV-W.mjs} +14 -12
- package/dist/{openai-completions-compat-CUollwl6.mjs → openai-completions-compat-BJf57ZxN.mjs} +3 -5
- package/dist/{openai-completions-compat-DfJG1wSO.d.mts → openai-completions-compat-qJjmqQ71.d.mts} +3 -3
- package/dist/{openai-completions-stream-BpuJ0ZOA.mjs → openai-completions-stream-dju_5F3P.mjs} +101 -173
- package/dist/{openai-prompt-cache-CjV_2IRI.mjs → openai-prompt-cache-BTMs80_O.mjs} +1 -1
- package/dist/{openai-prompt-cache-C4RHdN4n.d.mts → openai-prompt-cache-CouErLh7.d.mts} +2 -2
- package/dist/{openai-provider-client-BP8J1aeg.mjs → openai-provider-client-G8Otsq2Q.mjs} +2 -2
- package/dist/{openai-reasoning-effort-ash8dPrd.mjs → openai-reasoning-effort-C_0p3psG.mjs} +5 -4
- package/dist/{openai-responses-BJ_IEoMU.mjs → openai-responses-DbPe3-Py.mjs} +12 -11
- package/dist/openai-responses-compaction-replay-DiYn-gFM.d.mts +12 -0
- package/dist/{openai-responses-compaction-window-BiNCT0bS.mjs → openai-responses-compaction-window-CYGCjAQz.mjs} +5 -5
- package/dist/{openai-responses-contracts-DWrfMODE.mjs → openai-responses-contracts-CLxnqS9p.mjs} +1 -1
- package/dist/{openai-responses-contracts-B1ENJFpI.d.mts → openai-responses-contracts-D1iZ0njF.d.mts} +4 -4
- package/dist/{openai-responses-prompt-observer-internal-DPHWgbBH.mjs → openai-responses-prompt-observer-internal-BX3QTNSv.mjs} +12 -12
- package/dist/{openai-responses-request-lifecycle-DFuyO1pQ.mjs → openai-responses-request-lifecycle-Fsag-IGZ.mjs} +1 -1
- package/dist/{openai-responses-shared-DvSqtJ6_.mjs → openai-responses-shared-CIEHiKx7.mjs} +312 -122
- package/dist/{openai-responses-terminal-usage-BNFbvY9f.d.mts → openai-responses-terminal-usage-BE7lf7nW.d.mts} +2 -2
- package/dist/{openai-stop-reason-Drnn_6Qj.mjs → openai-stop-reason-8s70vDXj.mjs} +2 -12
- package/dist/{openai-tool-schema-ynZBgqW-.d.mts → openai-tool-schema-a5ysBBga.d.mts} +0 -6
- package/dist/{openai-tool-schema-D4BMcj2D.mjs → openai-tool-schema-vsl-cE-n.mjs} +243 -383
- package/dist/{openai-transport-params-DZKOTSfH.mjs → openai-transport-params-DyFJ4cgR.mjs} +60 -105
- package/dist/{positive-integer-BiUxrd71.mjs → positive-integer-CjN5OJbH.mjs} +1 -1
- package/dist/{provider-error-CRhW7lkJ.mjs → provider-error-CwT_j2v0.mjs} +43 -4
- package/dist/{provider-options-DZYq53lj.d.mts → provider-options-Df7ov0K1.d.mts} +2 -2
- package/dist/{provider-replay-context-DPr3w20a.mjs → provider-replay-context-Bs5aWE1d.mjs} +1 -1
- package/dist/{provider-transcript-transform-BTocjCRs.mjs → provider-transcript-transform-DjA5Fk2P.mjs} +17 -2
- package/dist/{provider-transport-turn-state-lrXEFkYr.mjs → provider-transport-turn-state-Dw7OtYUs.mjs} +1 -1
- package/dist/{provider-types-w1rEqrGP.d.mts → provider-types-d4XnoZMd.d.mts} +3 -3
- package/dist/provider-types.d.mts +6 -6
- package/dist/providers.d.mts +2 -2
- package/dist/providers.mjs +15 -11
- package/dist/{reasoning-tag-text-partitioner-Di38crhI.mjs → reasoning-tag-text-partitioner-B6FIIBt6.mjs} +47 -71
- package/dist/{record-coerce-DwRYMj3t.mjs → record-coerce-CNDovvft.mjs} +1 -1
- package/dist/simple-options-BkDPMuxw.mjs +349 -0
- package/dist/{src-DwGYYyZp.mjs → src-H4aXcFuX.mjs} +43 -25
- package/dist/{stream-CREqxHgU.mjs → stream-DDsCBgd-.mjs} +1 -7
- package/dist/{stream-first-event-timeout-BeptfRL6.mjs → stream-first-event-timeout-B9o0S4SZ.mjs} +1 -1
- package/dist/{tool-schema-json-projection-CD9c_fK8.mjs → tool-schema-json-projection-Ct8SeBeJ.mjs} +63 -23
- package/dist/{transport-stream-shared-B5RioB_Q.mjs → transport-stream-shared-BU5pwY6W.mjs} +62 -28
- package/dist/{transport-stream-shared-B7Z9oCYg.d.mts → transport-stream-shared-yYhXa4kX.d.mts} +12 -6
- package/dist/{transport-utils-03Qi0CN2.mjs → transport-utils-CqXzku3R.mjs} +3 -4
- package/dist/transports.d.mts +36 -32
- package/dist/transports.mjs +160 -216
- package/dist/types-CB-_vNyI.d.mts +1 -0
- package/dist/{types-qZFbaDbK.d.mts → types-CLLR3eB4.d.mts} +3 -1
- package/dist/types.d.mts +6 -6
- package/dist/types.mjs +4 -4
- package/dist/{validation-Dw7cb6BV.mjs → validation-BFZc40Ig.mjs} +80 -54
- package/dist/{validation-DhQhmt6t.d.mts → validation-Cat2wQsc.d.mts} +1 -1
- package/dist/validation.d.mts +1 -1
- package/dist/validation.mjs +1 -1
- package/package.json +5 -5
- package/dist/assistant-output-DM1JBNs1.mjs +0 -16
- package/dist/event-stream-C0nsgbj2.d.mts +0 -1
- package/dist/simple-options-np1XPuhm.mjs +0 -175
- package/dist/types-DTvddOfm.d.mts +0 -1
- package/dist/utf16-slice-CvGodqok.mjs +0 -29
|
@@ -0,0 +1,574 @@
|
|
|
1
|
+
import { t as AssistantMessageEventStream } from "./event-stream-vd9UnCAU.mjs";
|
|
2
|
+
import { a as asRecord, i as asOptionalRecord, s as readStringField } from "./record-coerce-CNDovvft.mjs";
|
|
3
|
+
import { n as getAiTransportHost, r as resolveAiTransportHeaderSentinels, w as calculateCost } from "./host-DwBmNMCC.mjs";
|
|
4
|
+
import { t as buildGuardedModelFetch } from "./host-policy-wK-ay_RK.mjs";
|
|
5
|
+
import { b as transportAbortError, c as failTransportStream, g as parseTerminalToolCallArguments, h as notifyProviderStreamOpened, j as sanitizeSurrogates, m as notifyProviderHttpResponse, w as parseJsonPreservingUnsafeIntegers } from "./transport-stream-shared-BU5pwY6W.mjs";
|
|
6
|
+
import { C as stripSystemPromptCacheBoundary, n as buildBaseOptions } from "./simple-options-BkDPMuxw.mjs";
|
|
7
|
+
import { n as getEnvApiKey } from "./env-api-keys-CyX4VKGO.mjs";
|
|
8
|
+
import { n as createAssistantOutput, t as transformProviderMessages } from "./provider-transcript-transform-DjA5Fk2P.mjs";
|
|
9
|
+
import { n as buildGoogleInteractionsSimpleThinking } from "./google-shared-B58i3c8H.mjs";
|
|
10
|
+
import { createRequire } from "node:module";
|
|
11
|
+
//#region packages/ai/src/providers/google-interactions-request.ts
|
|
12
|
+
const DEFAULT_GOOGLE_API_BASE_URL = "https://generativelanguage.googleapis.com";
|
|
13
|
+
let packageVersionMemo;
|
|
14
|
+
function resolvePackageVersion() {
|
|
15
|
+
if (packageVersionMemo) return packageVersionMemo;
|
|
16
|
+
if (typeof process !== "undefined" && process.env?.OPENCLAW_VERSION) {
|
|
17
|
+
packageVersionMemo = process.env.OPENCLAW_VERSION;
|
|
18
|
+
return packageVersionMemo;
|
|
19
|
+
}
|
|
20
|
+
try {
|
|
21
|
+
const require = createRequire(import.meta.url);
|
|
22
|
+
for (const candidate of [
|
|
23
|
+
"../package.json",
|
|
24
|
+
"../../package.json",
|
|
25
|
+
"../../../package.json",
|
|
26
|
+
"../../../../package.json"
|
|
27
|
+
]) try {
|
|
28
|
+
const parsed = asRecord(require(candidate));
|
|
29
|
+
const name = readStringField(parsed, "name");
|
|
30
|
+
const version = readStringField(parsed, "version");
|
|
31
|
+
if (version && (name === "openclaw" || name === "@openclaw/ai")) {
|
|
32
|
+
packageVersionMemo = version;
|
|
33
|
+
return packageVersionMemo;
|
|
34
|
+
}
|
|
35
|
+
} catch {}
|
|
36
|
+
} catch {}
|
|
37
|
+
packageVersionMemo = "0.0";
|
|
38
|
+
return packageVersionMemo;
|
|
39
|
+
}
|
|
40
|
+
function resolveGoogleApiClientHeaders(params) {
|
|
41
|
+
const hostHeaders = getAiTransportHost().resolveProviderRequestHeaders({
|
|
42
|
+
provider: "google",
|
|
43
|
+
api: params?.api ?? params?.model?.api ?? "google-generative-ai",
|
|
44
|
+
baseUrl: params?.baseUrl ?? DEFAULT_GOOGLE_API_BASE_URL,
|
|
45
|
+
model: params?.model
|
|
46
|
+
});
|
|
47
|
+
if (hostHeaders?.["x-goog-api-client"]) return hostHeaders;
|
|
48
|
+
return {
|
|
49
|
+
...hostHeaders,
|
|
50
|
+
"x-goog-api-client": `openclaw/${resolvePackageVersion()}`
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function convertToolResultContent(content) {
|
|
54
|
+
if (!Array.isArray(content)) return content;
|
|
55
|
+
return content.map((item) => {
|
|
56
|
+
if (item.type === "image") return {
|
|
57
|
+
type: "image",
|
|
58
|
+
mime_type: item.mimeType,
|
|
59
|
+
data: item.data
|
|
60
|
+
};
|
|
61
|
+
if (item.type === "text") return {
|
|
62
|
+
type: "text",
|
|
63
|
+
text: sanitizeSurrogates(item.text)
|
|
64
|
+
};
|
|
65
|
+
return item;
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
function convertMessages(model, context) {
|
|
69
|
+
const steps = [];
|
|
70
|
+
const messages = transformProviderMessages(context.messages, model);
|
|
71
|
+
for (const message of messages) {
|
|
72
|
+
if (message.role === "user") {
|
|
73
|
+
if (typeof message.content === "string") {
|
|
74
|
+
steps.push({
|
|
75
|
+
type: "user_input",
|
|
76
|
+
content: [{
|
|
77
|
+
type: "text",
|
|
78
|
+
text: sanitizeSurrogates(message.content) || " "
|
|
79
|
+
}]
|
|
80
|
+
});
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
const content = message.content.map((item) => item.type === "text" ? {
|
|
84
|
+
type: "text",
|
|
85
|
+
text: sanitizeSurrogates(item.text) || " "
|
|
86
|
+
} : {
|
|
87
|
+
type: "image",
|
|
88
|
+
mime_type: item.mimeType,
|
|
89
|
+
data: item.data
|
|
90
|
+
});
|
|
91
|
+
steps.push({
|
|
92
|
+
type: "user_input",
|
|
93
|
+
content: content.length > 0 ? content : [{
|
|
94
|
+
type: "text",
|
|
95
|
+
text: " "
|
|
96
|
+
}]
|
|
97
|
+
});
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (message.role === "toolResult") {
|
|
101
|
+
steps.push({
|
|
102
|
+
type: "function_result",
|
|
103
|
+
call_id: message.toolCallId,
|
|
104
|
+
name: message.toolName || "tool",
|
|
105
|
+
result: convertToolResultContent(message.content),
|
|
106
|
+
is_error: message.isError
|
|
107
|
+
});
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (message.stopReason === "error") continue;
|
|
111
|
+
if (message.content.length === 1 && message.content[0]?.type === "text" && message.content[0].text.includes("[assistant turn failed before producing content]")) continue;
|
|
112
|
+
let pendingText = [];
|
|
113
|
+
const flushText = () => {
|
|
114
|
+
if (pendingText.length === 0) return;
|
|
115
|
+
steps.push({
|
|
116
|
+
type: "model_output",
|
|
117
|
+
content: [{
|
|
118
|
+
type: "text",
|
|
119
|
+
text: pendingText.join("\n\n")
|
|
120
|
+
}]
|
|
121
|
+
});
|
|
122
|
+
pendingText = [];
|
|
123
|
+
};
|
|
124
|
+
for (const block of message.content) {
|
|
125
|
+
if (block.type === "thinking") {
|
|
126
|
+
flushText();
|
|
127
|
+
if (block.thinkingSignature) steps.push({
|
|
128
|
+
type: "thought",
|
|
129
|
+
signature: block.thinkingSignature,
|
|
130
|
+
...block.thinking.trim() ? { summary: [{
|
|
131
|
+
type: "text",
|
|
132
|
+
text: sanitizeSurrogates(block.thinking)
|
|
133
|
+
}] } : {}
|
|
134
|
+
});
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
if (block.type === "text") {
|
|
138
|
+
if (block.text.trim()) pendingText.push(sanitizeSurrogates(block.text));
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
flushText();
|
|
142
|
+
if (block.thoughtSignature && block.thoughtSignature !== "skip_thought_signature_validator" && !message.content.some((candidate) => candidate.type === "thinking")) steps.push({
|
|
143
|
+
type: "thought",
|
|
144
|
+
signature: block.thoughtSignature
|
|
145
|
+
});
|
|
146
|
+
steps.push({
|
|
147
|
+
type: "function_call",
|
|
148
|
+
id: block.id,
|
|
149
|
+
name: block.name,
|
|
150
|
+
arguments: block.arguments ?? {}
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
flushText();
|
|
154
|
+
}
|
|
155
|
+
return steps;
|
|
156
|
+
}
|
|
157
|
+
function buildGoogleInteractionsParams(model, context, options = {}) {
|
|
158
|
+
const optionRecord = asRecord(options);
|
|
159
|
+
if (optionRecord.cachedContent || optionRecord.cached_content) throw new Error("Explicit prompt caching ('cachedContent') is not supported with the Gemini Interactions API. The Interactions API handles caching implicitly on the server.");
|
|
160
|
+
if (optionRecord.videoMetadata || optionRecord.video_metadata) throw new Error("video_metadata clipping offsets are not supported with the Gemini Interactions API.");
|
|
161
|
+
const generationConfig = {};
|
|
162
|
+
if (options.temperature !== void 0) generationConfig.temperature = options.temperature;
|
|
163
|
+
if (options.maxTokens !== void 0) generationConfig.max_output_tokens = options.maxTokens;
|
|
164
|
+
if (options.stop && options.stop.length > 0) generationConfig.stop_sequences = options.stop;
|
|
165
|
+
if (options.thinking) {
|
|
166
|
+
generationConfig.thinking_summaries = options.thinking.enabled ? "auto" : "none";
|
|
167
|
+
const thinkingLevel = options.thinking.level ?? (options.thinking.enabled ? void 0 : buildGoogleInteractionsSimpleThinking(model, { reasoning: "off" }).level);
|
|
168
|
+
if (thinkingLevel) generationConfig.thinking_level = thinkingLevel.toLowerCase();
|
|
169
|
+
}
|
|
170
|
+
if (options.toolChoice) generationConfig.tool_choice = options.toolChoice;
|
|
171
|
+
const tools = context.tools?.map((tool) => ({
|
|
172
|
+
type: "function",
|
|
173
|
+
name: tool.name,
|
|
174
|
+
description: tool.description || "",
|
|
175
|
+
parameters: asRecord(tool.parameters)
|
|
176
|
+
}));
|
|
177
|
+
return {
|
|
178
|
+
model: model.id,
|
|
179
|
+
input: convertMessages(model, context),
|
|
180
|
+
...context.systemPrompt ? { system_instruction: sanitizeSurrogates(stripSystemPromptCacheBoundary(context.systemPrompt)) } : {},
|
|
181
|
+
...tools && tools.length > 0 ? { tools } : {},
|
|
182
|
+
...Object.keys(generationConfig).length > 0 ? { generation_config: generationConfig } : {},
|
|
183
|
+
store: false,
|
|
184
|
+
stream: true
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
//#endregion
|
|
188
|
+
//#region packages/ai/src/providers/google-interactions-shared.ts
|
|
189
|
+
/**
|
|
190
|
+
* Gemini Interactions API adapter and lifecycle for OpenClaw.
|
|
191
|
+
* POST /v1beta/interactions
|
|
192
|
+
*/
|
|
193
|
+
function resolveGoogleInteractionsApiKey(model, options, apiKey) {
|
|
194
|
+
return apiKey || options?.apiKey || getEnvApiKey(model.provider) || process.env.GEMINI_API_KEY || process.env.GOOGLE_API_KEY || "";
|
|
195
|
+
}
|
|
196
|
+
function logGoogleInteractionsDebug(message, data) {
|
|
197
|
+
getAiTransportHost().logDebug("google-interactions", () => ({
|
|
198
|
+
message,
|
|
199
|
+
data
|
|
200
|
+
}));
|
|
201
|
+
}
|
|
202
|
+
function isGoogleInteractionsRequestBody(value) {
|
|
203
|
+
const record = asOptionalRecord(value);
|
|
204
|
+
if (!record) return false;
|
|
205
|
+
return typeof record.model === "string" && Array.isArray(record.input) && typeof record.store === "boolean" && typeof record.stream === "boolean";
|
|
206
|
+
}
|
|
207
|
+
async function runGoogleInteractionsLifecycle(params) {
|
|
208
|
+
const { stream, model, output, options, context, nextToolCallId } = params;
|
|
209
|
+
let reader;
|
|
210
|
+
try {
|
|
211
|
+
const host = getAiTransportHost();
|
|
212
|
+
const unresolvedApiKey = resolveGoogleInteractionsApiKey(model, options, params.apiKey);
|
|
213
|
+
const apiKey = host.resolveSecretSentinel(unresolvedApiKey);
|
|
214
|
+
if (!apiKey.trim()) throw new Error(`No API key for provider: ${model.provider}`);
|
|
215
|
+
let body = buildGoogleInteractionsParams(model, context, options);
|
|
216
|
+
const nextBody = await options?.onPayload?.(body, model);
|
|
217
|
+
if (nextBody !== void 0) {
|
|
218
|
+
if (!isGoogleInteractionsRequestBody(nextBody)) throw new Error("Google Interactions onPayload returned an invalid request body");
|
|
219
|
+
body = nextBody;
|
|
220
|
+
}
|
|
221
|
+
let baseUrl = (model.baseUrl || "https://generativelanguage.googleapis.com/v1beta").trim().replace(/\/+$/, "");
|
|
222
|
+
if (/^https:\/\/generativelanguage\.googleapis\.com$/i.test(baseUrl)) baseUrl = "https://generativelanguage.googleapis.com/v1beta";
|
|
223
|
+
const url = `${baseUrl}/interactions?alt=sse`;
|
|
224
|
+
const googleClientHeaders = resolveGoogleApiClientHeaders({
|
|
225
|
+
baseUrl,
|
|
226
|
+
api: "google-generative-ai",
|
|
227
|
+
model
|
|
228
|
+
});
|
|
229
|
+
const headers = resolveAiTransportHeaderSentinels({
|
|
230
|
+
"Content-Type": "application/json",
|
|
231
|
+
"x-goog-api-key": apiKey,
|
|
232
|
+
"Api-Revision": "2026-05-20",
|
|
233
|
+
...googleClientHeaders,
|
|
234
|
+
...model.headers,
|
|
235
|
+
...options?.headers
|
|
236
|
+
}) ?? {
|
|
237
|
+
"Content-Type": "application/json",
|
|
238
|
+
"x-goog-api-key": apiKey
|
|
239
|
+
};
|
|
240
|
+
logGoogleInteractionsDebug("request", {
|
|
241
|
+
method: "POST",
|
|
242
|
+
url,
|
|
243
|
+
model: model.id,
|
|
244
|
+
inputSteps: body.input.length,
|
|
245
|
+
tools: body.tools?.length ?? 0
|
|
246
|
+
});
|
|
247
|
+
const response = await buildGuardedModelFetch({
|
|
248
|
+
...model,
|
|
249
|
+
baseUrl
|
|
250
|
+
}, options?.timeoutMs, { sanitizeSse: true })(url, {
|
|
251
|
+
method: "POST",
|
|
252
|
+
headers,
|
|
253
|
+
body: JSON.stringify(body),
|
|
254
|
+
signal: options?.signal
|
|
255
|
+
});
|
|
256
|
+
await notifyProviderHttpResponse({
|
|
257
|
+
options,
|
|
258
|
+
response,
|
|
259
|
+
model
|
|
260
|
+
});
|
|
261
|
+
logGoogleInteractionsDebug("response", {
|
|
262
|
+
status: response.status,
|
|
263
|
+
statusText: response.statusText
|
|
264
|
+
});
|
|
265
|
+
if (!response.ok) {
|
|
266
|
+
const errorText = await response.text();
|
|
267
|
+
logGoogleInteractionsDebug("request failed", { status: response.status });
|
|
268
|
+
throw new Error(`Google Interactions API error HTTP ${response.status}: ${errorText}`);
|
|
269
|
+
}
|
|
270
|
+
if (!response.body) throw new Error("Google Interactions API returned empty response body");
|
|
271
|
+
reader = response.body.getReader();
|
|
272
|
+
await notifyProviderStreamOpened({
|
|
273
|
+
options,
|
|
274
|
+
cancelStream: () => reader?.cancel()
|
|
275
|
+
});
|
|
276
|
+
stream.push({
|
|
277
|
+
type: "start",
|
|
278
|
+
partial: output
|
|
279
|
+
});
|
|
280
|
+
const decoder = new TextDecoder();
|
|
281
|
+
let buffer = "";
|
|
282
|
+
let currentBlockType = null;
|
|
283
|
+
let currentBlockIndex = -1;
|
|
284
|
+
let currentToolCall = null;
|
|
285
|
+
let currentToolArgs = "";
|
|
286
|
+
let latestThoughtSignature;
|
|
287
|
+
let latestUsage;
|
|
288
|
+
const endCurrentBlock = () => {
|
|
289
|
+
if (currentBlockType === "text") {
|
|
290
|
+
const block = output.content[currentBlockIndex];
|
|
291
|
+
const textBlock = block?.type === "text" ? block : void 0;
|
|
292
|
+
stream.push({
|
|
293
|
+
type: "text_end",
|
|
294
|
+
contentIndex: currentBlockIndex,
|
|
295
|
+
content: textBlock?.text ?? "",
|
|
296
|
+
partial: output
|
|
297
|
+
});
|
|
298
|
+
} else if (currentBlockType === "thinking") {
|
|
299
|
+
const block = output.content[currentBlockIndex];
|
|
300
|
+
const thinkingBlock = block?.type === "thinking" ? block : void 0;
|
|
301
|
+
if (thinkingBlock && !thinkingBlock.thinkingSignature && latestThoughtSignature) thinkingBlock.thinkingSignature = latestThoughtSignature;
|
|
302
|
+
stream.push({
|
|
303
|
+
type: "thinking_end",
|
|
304
|
+
contentIndex: currentBlockIndex,
|
|
305
|
+
content: thinkingBlock?.thinking ?? "",
|
|
306
|
+
partial: output
|
|
307
|
+
});
|
|
308
|
+
latestThoughtSignature = void 0;
|
|
309
|
+
} else if (currentBlockType === "toolCall" && currentToolCall) {
|
|
310
|
+
if (currentToolArgs.trim()) currentToolCall.arguments = parseTerminalToolCallArguments(currentToolArgs);
|
|
311
|
+
stream.push({
|
|
312
|
+
type: "toolcall_end",
|
|
313
|
+
contentIndex: currentBlockIndex,
|
|
314
|
+
toolCall: currentToolCall,
|
|
315
|
+
partial: output
|
|
316
|
+
});
|
|
317
|
+
currentToolCall = null;
|
|
318
|
+
currentToolArgs = "";
|
|
319
|
+
}
|
|
320
|
+
currentBlockType = null;
|
|
321
|
+
};
|
|
322
|
+
const startTextBlock = (type, text = "") => {
|
|
323
|
+
endCurrentBlock();
|
|
324
|
+
currentBlockIndex = output.content.length;
|
|
325
|
+
output.content.push(type === "text" ? {
|
|
326
|
+
type,
|
|
327
|
+
text
|
|
328
|
+
} : {
|
|
329
|
+
type,
|
|
330
|
+
thinking: text,
|
|
331
|
+
...latestThoughtSignature ? { thinkingSignature: latestThoughtSignature } : {}
|
|
332
|
+
});
|
|
333
|
+
stream.push({
|
|
334
|
+
type: type === "text" ? "text_start" : "thinking_start",
|
|
335
|
+
contentIndex: currentBlockIndex,
|
|
336
|
+
partial: output
|
|
337
|
+
});
|
|
338
|
+
if (text) stream.push({
|
|
339
|
+
type: type === "text" ? "text_delta" : "thinking_delta",
|
|
340
|
+
contentIndex: currentBlockIndex,
|
|
341
|
+
delta: text,
|
|
342
|
+
partial: output
|
|
343
|
+
});
|
|
344
|
+
return type;
|
|
345
|
+
};
|
|
346
|
+
const startToolCallBlock = (source, args = {}) => {
|
|
347
|
+
endCurrentBlock();
|
|
348
|
+
currentBlockIndex = output.content.length;
|
|
349
|
+
const name = readStringField(source, "name") ?? "tool";
|
|
350
|
+
const toolCall = {
|
|
351
|
+
type: "toolCall",
|
|
352
|
+
id: readStringField(source, "id") ?? nextToolCallId(name),
|
|
353
|
+
name,
|
|
354
|
+
arguments: args
|
|
355
|
+
};
|
|
356
|
+
currentToolArgs = Object.keys(args).length > 0 ? JSON.stringify(args) : "";
|
|
357
|
+
output.content.push(toolCall);
|
|
358
|
+
stream.push({
|
|
359
|
+
type: "toolcall_start",
|
|
360
|
+
contentIndex: currentBlockIndex,
|
|
361
|
+
partial: output
|
|
362
|
+
});
|
|
363
|
+
return toolCall;
|
|
364
|
+
};
|
|
365
|
+
let streamDone = false;
|
|
366
|
+
let sawCompletion = false;
|
|
367
|
+
while (!streamDone) {
|
|
368
|
+
const { done, value } = await reader.read();
|
|
369
|
+
if (done) break;
|
|
370
|
+
buffer += decoder.decode(value, { stream: true });
|
|
371
|
+
const lines = buffer.split("\n");
|
|
372
|
+
buffer = lines.pop() ?? "";
|
|
373
|
+
for (const line of lines) {
|
|
374
|
+
const trimmed = line.trim();
|
|
375
|
+
if (!trimmed || !trimmed.startsWith("data:")) continue;
|
|
376
|
+
const dataStr = trimmed.slice(5).trim();
|
|
377
|
+
if (dataStr === "[DONE]") {
|
|
378
|
+
streamDone = true;
|
|
379
|
+
break;
|
|
380
|
+
}
|
|
381
|
+
let event;
|
|
382
|
+
try {
|
|
383
|
+
event = asRecord(parseJsonPreservingUnsafeIntegers(dataStr));
|
|
384
|
+
} catch {
|
|
385
|
+
continue;
|
|
386
|
+
}
|
|
387
|
+
const eventType = event.event_type || event.type;
|
|
388
|
+
logGoogleInteractionsDebug("stream event", { eventType: typeof eventType === "string" ? eventType : "unknown" });
|
|
389
|
+
if (eventType === "error") {
|
|
390
|
+
const providerError = asOptionalRecord(event.error);
|
|
391
|
+
const message = readStringField(providerError, "message") ?? "Google Interactions stream failed";
|
|
392
|
+
throw Object.assign(new Error(message), {
|
|
393
|
+
code: readStringField(providerError, "code"),
|
|
394
|
+
type: "google_interactions_stream_error"
|
|
395
|
+
});
|
|
396
|
+
} else if (eventType === "step.delta") {
|
|
397
|
+
const delta = asOptionalRecord(event.delta);
|
|
398
|
+
const deltaType = delta?.type;
|
|
399
|
+
if (deltaType === "text") {
|
|
400
|
+
const text = readStringField(delta, "text") ?? "";
|
|
401
|
+
if (text) {
|
|
402
|
+
if (currentBlockType !== "text") currentBlockType = startTextBlock("text");
|
|
403
|
+
const block = output.content[currentBlockIndex];
|
|
404
|
+
if (!block || block.type !== "text") throw new Error("Google Interactions text delta has no active text block");
|
|
405
|
+
block.text += text;
|
|
406
|
+
stream.push({
|
|
407
|
+
type: "text_delta",
|
|
408
|
+
contentIndex: currentBlockIndex,
|
|
409
|
+
delta: text,
|
|
410
|
+
partial: output
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
} else if (deltaType === "thought" || deltaType === "thought_summary" || deltaType === "raw_thought") {
|
|
414
|
+
let thinkingText = "";
|
|
415
|
+
if (typeof delta?.text === "string") thinkingText = delta.text;
|
|
416
|
+
else if (typeof delta?.content === "string") thinkingText = delta.content;
|
|
417
|
+
else if (Array.isArray(delta?.content)) thinkingText = delta.content.map((content) => readStringField(asOptionalRecord(content), "text") ?? "").join("");
|
|
418
|
+
else thinkingText = readStringField(asOptionalRecord(delta?.content), "text") ?? "";
|
|
419
|
+
if (thinkingText) {
|
|
420
|
+
if (currentBlockType !== "thinking") currentBlockType = startTextBlock("thinking");
|
|
421
|
+
const block = output.content[currentBlockIndex];
|
|
422
|
+
if (!block || block.type !== "thinking") throw new Error("Google Interactions thought delta has no active thought block");
|
|
423
|
+
block.thinking += thinkingText;
|
|
424
|
+
stream.push({
|
|
425
|
+
type: "thinking_delta",
|
|
426
|
+
contentIndex: currentBlockIndex,
|
|
427
|
+
delta: thinkingText,
|
|
428
|
+
partial: output
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
} else if (deltaType === "thought_signature" || delta && typeof delta.signature === "string" && !delta.text) {
|
|
432
|
+
const signature = readStringField(delta, "signature") ?? "";
|
|
433
|
+
if (signature) {
|
|
434
|
+
latestThoughtSignature = signature;
|
|
435
|
+
if (currentBlockType === "thinking") {
|
|
436
|
+
const block = output.content[currentBlockIndex];
|
|
437
|
+
if (block?.type === "thinking") block.thinkingSignature = signature;
|
|
438
|
+
} else {
|
|
439
|
+
const lastThinking = output.content.findLast((block) => block.type === "thinking");
|
|
440
|
+
if (lastThinking && !lastThinking.thinkingSignature) lastThinking.thinkingSignature = signature;
|
|
441
|
+
else if (!lastThinking) output.content.push({
|
|
442
|
+
type: "thinking",
|
|
443
|
+
thinking: "",
|
|
444
|
+
thinkingSignature: signature
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
} else if (deltaType === "arguments" || deltaType === "arguments_delta") {
|
|
449
|
+
const argText = readStringField(delta, "arguments") ?? readStringField(delta, "text") ?? "";
|
|
450
|
+
if (currentBlockType !== "toolCall") {
|
|
451
|
+
currentToolCall = startToolCallBlock(delta);
|
|
452
|
+
currentBlockType = "toolCall";
|
|
453
|
+
}
|
|
454
|
+
const streamedToolName = readStringField(delta, "name");
|
|
455
|
+
if (streamedToolName && currentToolCall && (!currentToolCall.name || currentToolCall.name === "tool")) currentToolCall.name = streamedToolName;
|
|
456
|
+
const streamedToolCallId = readStringField(delta, "id");
|
|
457
|
+
if (streamedToolCallId && currentToolCall) currentToolCall.id = streamedToolCallId;
|
|
458
|
+
currentToolArgs += argText;
|
|
459
|
+
stream.push({
|
|
460
|
+
type: "toolcall_delta",
|
|
461
|
+
contentIndex: currentBlockIndex,
|
|
462
|
+
delta: argText,
|
|
463
|
+
partial: output
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
} else if (eventType === "step.start") {
|
|
467
|
+
const step = asOptionalRecord(event.step);
|
|
468
|
+
if (step?.type === "thought") {
|
|
469
|
+
const stepSignature = readStringField(step, "signature");
|
|
470
|
+
if (stepSignature) latestThoughtSignature = stepSignature;
|
|
471
|
+
let initialThinking = "";
|
|
472
|
+
if (Array.isArray(step.summary)) initialThinking = step.summary.map((content) => readStringField(asOptionalRecord(content), "text") ?? "").join("");
|
|
473
|
+
if (currentBlockType !== "thinking") currentBlockType = startTextBlock("thinking", initialThinking);
|
|
474
|
+
} else if (step?.type === "model_output") currentBlockType = startTextBlock("text", Array.isArray(step.content) ? step.content.map((content) => readStringField(asOptionalRecord(content), "text") ?? "").join("") : "");
|
|
475
|
+
else if (step?.type === "function_call") {
|
|
476
|
+
currentToolCall = startToolCallBlock(step, asRecord(step.arguments));
|
|
477
|
+
currentBlockType = "toolCall";
|
|
478
|
+
}
|
|
479
|
+
} else if (eventType === "step.stop") {
|
|
480
|
+
latestUsage = asOptionalRecord(event.usage) ?? latestUsage;
|
|
481
|
+
endCurrentBlock();
|
|
482
|
+
} else if (eventType === "interaction.completed" || eventType === "interaction.complete") {
|
|
483
|
+
sawCompletion = true;
|
|
484
|
+
const interaction = asOptionalRecord(event.interaction) ?? event;
|
|
485
|
+
const usage = asOptionalRecord(interaction.usage) ?? latestUsage ?? {};
|
|
486
|
+
const promptTokens = Number(usage.total_input_tokens ?? 0);
|
|
487
|
+
const cacheRead = Number(usage.total_cached_tokens ?? 0);
|
|
488
|
+
const candidatesTokens = Number(usage.total_output_tokens ?? 0);
|
|
489
|
+
const thoughtTokens = Number(usage.total_thought_tokens ?? 0);
|
|
490
|
+
const toolUseTokens = Number(usage.total_tool_use_tokens ?? 0);
|
|
491
|
+
const outputTokens = candidatesTokens + thoughtTokens;
|
|
492
|
+
const totalTokens = Number(usage.total_tokens ?? promptTokens + outputTokens + toolUseTokens);
|
|
493
|
+
output.usage = {
|
|
494
|
+
input: Math.max(0, promptTokens - cacheRead) + toolUseTokens,
|
|
495
|
+
output: outputTokens,
|
|
496
|
+
cacheRead,
|
|
497
|
+
cacheWrite: 0,
|
|
498
|
+
cacheTelemetry: { state: "available" },
|
|
499
|
+
totalTokens,
|
|
500
|
+
cost: {
|
|
501
|
+
input: 0,
|
|
502
|
+
output: 0,
|
|
503
|
+
cacheRead: 0,
|
|
504
|
+
cacheWrite: 0,
|
|
505
|
+
total: 0
|
|
506
|
+
}
|
|
507
|
+
};
|
|
508
|
+
if (model.cost) calculateCost(model, output.usage);
|
|
509
|
+
const status = typeof interaction.status === "string" ? interaction.status : "completed";
|
|
510
|
+
if (status === "failed" || status === "cancelled" || status === "budget_exceeded") throw new Error(`Google Interactions API completed with status: ${status}`);
|
|
511
|
+
if (status === "incomplete") output.stopReason = "length";
|
|
512
|
+
else output.stopReason = output.content.some((b) => b.type === "toolCall") ? "toolUse" : "stop";
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
endCurrentBlock();
|
|
517
|
+
if (!sawCompletion) throw Object.assign(/* @__PURE__ */ new Error("Google Interactions stream ended before a terminal event"), {
|
|
518
|
+
code: "STREAM_INCOMPLETE",
|
|
519
|
+
type: "google_incomplete_stream"
|
|
520
|
+
});
|
|
521
|
+
if (latestThoughtSignature) {
|
|
522
|
+
for (const block of output.content) if (block.type === "thinking" && !block.thinkingSignature) block.thinkingSignature = latestThoughtSignature;
|
|
523
|
+
}
|
|
524
|
+
if (!output.stopReason) output.stopReason = output.content.some((b) => b.type === "toolCall") ? "toolUse" : "stop";
|
|
525
|
+
if (output.stopReason === "aborted" || output.stopReason === "error") throw new Error("An unknown error occurred");
|
|
526
|
+
stream.push({
|
|
527
|
+
type: "done",
|
|
528
|
+
reason: output.stopReason,
|
|
529
|
+
message: output
|
|
530
|
+
});
|
|
531
|
+
stream.end();
|
|
532
|
+
} catch (error) {
|
|
533
|
+
const failure = options?.signal?.aborted ? transportAbortError(options.signal) : error;
|
|
534
|
+
failTransportStream({
|
|
535
|
+
stream,
|
|
536
|
+
output,
|
|
537
|
+
signal: options?.signal,
|
|
538
|
+
error: failure
|
|
539
|
+
});
|
|
540
|
+
} finally {
|
|
541
|
+
if (reader) {
|
|
542
|
+
const cancellation = reader.cancel().catch(() => void 0);
|
|
543
|
+
reader.releaseLock();
|
|
544
|
+
getAiTransportHost().observePendingProviderWork?.(cancellation);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
//#endregion
|
|
549
|
+
//#region packages/ai/src/providers/google-interactions.ts
|
|
550
|
+
let toolCallCounter = 0;
|
|
551
|
+
const streamGoogleInteractions = (model, context, options) => {
|
|
552
|
+
const stream = new AssistantMessageEventStream();
|
|
553
|
+
runGoogleInteractionsLifecycle({
|
|
554
|
+
stream,
|
|
555
|
+
model,
|
|
556
|
+
output: createAssistantOutput(model, "google-interactions"),
|
|
557
|
+
options,
|
|
558
|
+
context,
|
|
559
|
+
nextToolCallId: (name) => `${name}_${Date.now()}_${++toolCallCounter}`,
|
|
560
|
+
apiKey: resolveGoogleInteractionsApiKey(model, options)
|
|
561
|
+
});
|
|
562
|
+
return stream;
|
|
563
|
+
};
|
|
564
|
+
const streamSimpleGoogleInteractions = (model, context, options) => {
|
|
565
|
+
const apiKey = resolveGoogleInteractionsApiKey(model, options);
|
|
566
|
+
if (!apiKey) throw new Error(`No API key for provider: ${model.provider}`);
|
|
567
|
+
const base = buildBaseOptions(model, options, apiKey);
|
|
568
|
+
return streamGoogleInteractions(model, context, {
|
|
569
|
+
...base,
|
|
570
|
+
thinking: buildGoogleInteractionsSimpleThinking(model, options)
|
|
571
|
+
});
|
|
572
|
+
};
|
|
573
|
+
//#endregion
|
|
574
|
+
export { streamGoogleInteractions, streamSimpleGoogleInteractions };
|