@opencode-ai/ai 0.0.0-beta-17941 → 0.0.0-beta-18027
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/protocols/anthropic-messages.js +3 -2
- package/dist/protocols/gemini.d.ts +40 -40
- package/dist/protocols/gemini.js +68 -50
- package/dist/protocols/open-responses.d.ts +6 -7
- package/dist/protocols/open-responses.js +40 -50
- package/dist/protocols/openai-chat.d.ts +42 -2
- package/dist/protocols/openai-chat.js +34 -5
- package/dist/protocols/openai-compatible-responses.d.ts +1 -1
- package/dist/protocols/openai-responses.d.ts +6 -6
- package/dist/protocols/openai-responses.js +1 -2
- package/dist/protocols/utils/open-responses-options.d.ts +4 -3
- package/dist/protocols/utils/open-responses-options.js +2 -1
- package/dist/protocols/utils/openai-options.d.ts +3 -3
- package/dist/protocols/utils/openai-options.js +1 -1
- package/dist/protocols/xai-responses.d.ts +1 -1
- package/dist/provider-error.d.ts +1 -0
- package/dist/provider-error.js +4 -2
- package/dist/providers/amazon-bedrock-mantle.d.ts +11 -11
- package/dist/providers/azure.d.ts +8 -8
- package/dist/providers/cloudflare.d.ts +2 -2
- package/dist/providers/google-vertex-chat.d.ts +2 -2
- package/dist/providers/google-vertex-responses.d.ts +1 -1
- package/dist/providers/google-vertex.d.ts +7 -7
- package/dist/providers/google-vertex.js +13 -1
- package/dist/providers/google.d.ts +7 -7
- package/dist/providers/openai-compatible-responses.d.ts +1 -1
- package/dist/providers/openai-compatible.d.ts +9 -9
- package/dist/providers/openai-options.d.ts +6 -2
- package/dist/providers/openai.d.ts +12 -12
- package/dist/providers/openrouter.d.ts +22 -0
- package/dist/providers/xai.d.ts +11 -11
- package/dist/schema/errors.d.ts +1 -0
- package/dist/schema/errors.js +3 -0
- package/package.json +5 -5
|
@@ -24,6 +24,7 @@ const SSE_EVENTS = new Set([
|
|
|
24
24
|
"content_block_start",
|
|
25
25
|
"content_block_delta",
|
|
26
26
|
"content_block_stop",
|
|
27
|
+
"ping",
|
|
27
28
|
"error",
|
|
28
29
|
]);
|
|
29
30
|
export const framing = Framing.sseEvents(SSE_EVENTS);
|
|
@@ -829,11 +830,11 @@ const providerErrorMessage = (event) => {
|
|
|
829
830
|
return `${type}: ${message}`;
|
|
830
831
|
return message || type || "Anthropic Messages stream error";
|
|
831
832
|
};
|
|
832
|
-
const onError = (event) => new AIError({
|
|
833
|
+
const onError = (event) => Effect.fail(new AIError({
|
|
833
834
|
module: ADAPTER,
|
|
834
835
|
method: "stream",
|
|
835
836
|
reason: classifyProviderFailure({ message: providerErrorMessage(event), code: event.error?.type }),
|
|
836
|
-
});
|
|
837
|
+
}));
|
|
837
838
|
const step = (state, event) => {
|
|
838
839
|
if (event.type === "message_start")
|
|
839
840
|
return Effect.succeed(onMessageStart(state, event));
|
|
@@ -21,11 +21,11 @@ export type ProviderOptionsInput = OptionsInput;
|
|
|
21
21
|
declare const GeminiBody: Schema.Struct<{
|
|
22
22
|
cachedContent: Schema.optional<Schema.String>;
|
|
23
23
|
contents: Schema.$Array<Schema.Struct<{
|
|
24
|
-
readonly role: Schema.Literals<readonly ["user", "model"]
|
|
25
|
-
readonly parts: Schema.$Array<Schema.Union<readonly [Schema.Struct<{
|
|
24
|
+
readonly role: Schema.optional<Schema.NullOr<Schema.Literals<readonly ["user", "model"]>>>;
|
|
25
|
+
readonly parts: Schema.optional<Schema.NullOr<Schema.$Array<Schema.Union<readonly [Schema.Struct<{
|
|
26
26
|
readonly text: Schema.String;
|
|
27
|
-
readonly thought: Schema.optional<Schema.Boolean
|
|
28
|
-
readonly thoughtSignature: Schema.optional<Schema.String
|
|
27
|
+
readonly thought: Schema.optional<Schema.NullOr<Schema.Boolean>>;
|
|
28
|
+
readonly thoughtSignature: Schema.optional<Schema.NullOr<Schema.String>>;
|
|
29
29
|
}>, Schema.Struct<{
|
|
30
30
|
readonly inlineData: Schema.Struct<{
|
|
31
31
|
readonly mimeType: Schema.String;
|
|
@@ -33,11 +33,11 @@ declare const GeminiBody: Schema.Struct<{
|
|
|
33
33
|
}>;
|
|
34
34
|
}>, Schema.Struct<{
|
|
35
35
|
readonly functionCall: Schema.Struct<{
|
|
36
|
-
readonly id: Schema.optional<Schema.String
|
|
36
|
+
readonly id: Schema.optional<Schema.NullOr<Schema.String>>;
|
|
37
37
|
readonly name: Schema.String;
|
|
38
38
|
readonly args: Schema.optional<Schema.Unknown>;
|
|
39
39
|
}>;
|
|
40
|
-
readonly thoughtSignature: Schema.optional<Schema.String
|
|
40
|
+
readonly thoughtSignature: Schema.optional<Schema.NullOr<Schema.String>>;
|
|
41
41
|
}>, Schema.Struct<{
|
|
42
42
|
readonly functionResponse: Schema.Struct<{
|
|
43
43
|
readonly id: Schema.optional<Schema.String>;
|
|
@@ -50,7 +50,7 @@ declare const GeminiBody: Schema.Struct<{
|
|
|
50
50
|
}>;
|
|
51
51
|
}>>>;
|
|
52
52
|
}>;
|
|
53
|
-
}>]
|
|
53
|
+
}>]>>>>;
|
|
54
54
|
}>>;
|
|
55
55
|
labels: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
|
|
56
56
|
safetySettings: Schema.optional<Schema.$Array<Schema.Struct<{
|
|
@@ -99,23 +99,22 @@ export type GeminiBody = Schema.Schema.Type<typeof GeminiBody>;
|
|
|
99
99
|
*/
|
|
100
100
|
export declare const protocol: Protocol<{
|
|
101
101
|
readonly contents: readonly {
|
|
102
|
-
readonly
|
|
103
|
-
readonly parts: readonly ({
|
|
102
|
+
readonly parts?: readonly ({
|
|
104
103
|
readonly inlineData: {
|
|
105
104
|
readonly mimeType: string;
|
|
106
105
|
readonly data: string;
|
|
107
106
|
};
|
|
108
107
|
} | {
|
|
109
108
|
readonly text: string;
|
|
110
|
-
readonly thought?: boolean | undefined;
|
|
111
|
-
readonly thoughtSignature?: string | undefined;
|
|
109
|
+
readonly thought?: boolean | null | undefined;
|
|
110
|
+
readonly thoughtSignature?: string | null | undefined;
|
|
112
111
|
} | {
|
|
113
112
|
readonly functionCall: {
|
|
114
113
|
readonly name: string;
|
|
115
|
-
readonly id?: string | undefined;
|
|
114
|
+
readonly id?: string | null | undefined;
|
|
116
115
|
readonly args?: unknown;
|
|
117
116
|
};
|
|
118
|
-
readonly thoughtSignature?: string | undefined;
|
|
117
|
+
readonly thoughtSignature?: string | null | undefined;
|
|
119
118
|
} | {
|
|
120
119
|
readonly functionResponse: {
|
|
121
120
|
readonly name: string;
|
|
@@ -128,7 +127,8 @@ export declare const protocol: Protocol<{
|
|
|
128
127
|
};
|
|
129
128
|
}[] | undefined;
|
|
130
129
|
};
|
|
131
|
-
})[];
|
|
130
|
+
})[] | null | undefined;
|
|
131
|
+
readonly role?: "model" | "user" | null | undefined;
|
|
132
132
|
}[];
|
|
133
133
|
readonly tools?: readonly {
|
|
134
134
|
readonly functionDeclarations: readonly {
|
|
@@ -177,23 +177,22 @@ export declare const protocol: Protocol<{
|
|
|
177
177
|
}, string, {
|
|
178
178
|
readonly candidates?: readonly {
|
|
179
179
|
readonly content?: {
|
|
180
|
-
readonly
|
|
181
|
-
readonly parts: readonly ({
|
|
180
|
+
readonly parts?: readonly ({
|
|
182
181
|
readonly inlineData: {
|
|
183
182
|
readonly mimeType: string;
|
|
184
183
|
readonly data: string;
|
|
185
184
|
};
|
|
186
185
|
} | {
|
|
187
186
|
readonly text: string;
|
|
188
|
-
readonly thought?: boolean | undefined;
|
|
189
|
-
readonly thoughtSignature?: string | undefined;
|
|
187
|
+
readonly thought?: boolean | null | undefined;
|
|
188
|
+
readonly thoughtSignature?: string | null | undefined;
|
|
190
189
|
} | {
|
|
191
190
|
readonly functionCall: {
|
|
192
191
|
readonly name: string;
|
|
193
|
-
readonly id?: string | undefined;
|
|
192
|
+
readonly id?: string | null | undefined;
|
|
194
193
|
readonly args?: unknown;
|
|
195
194
|
};
|
|
196
|
-
readonly thoughtSignature?: string | undefined;
|
|
195
|
+
readonly thoughtSignature?: string | null | undefined;
|
|
197
196
|
} | {
|
|
198
197
|
readonly functionResponse: {
|
|
199
198
|
readonly name: string;
|
|
@@ -206,46 +205,46 @@ export declare const protocol: Protocol<{
|
|
|
206
205
|
};
|
|
207
206
|
}[] | undefined;
|
|
208
207
|
};
|
|
209
|
-
})[];
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
208
|
+
})[] | null | undefined;
|
|
209
|
+
readonly role?: "model" | "user" | null | undefined;
|
|
210
|
+
} | null | undefined;
|
|
211
|
+
readonly finishReason?: string | null | undefined;
|
|
212
|
+
}[] | null | undefined;
|
|
213
213
|
readonly promptFeedback?: {
|
|
214
214
|
readonly [x: string]: unknown;
|
|
215
|
-
readonly blockReason?: string | undefined;
|
|
216
|
-
readonly blockReasonMessage?: string | undefined;
|
|
215
|
+
readonly blockReason?: string | null | undefined;
|
|
216
|
+
readonly blockReasonMessage?: string | null | undefined;
|
|
217
217
|
readonly safetyRatings?: unknown;
|
|
218
|
-
} | undefined;
|
|
218
|
+
} | null | undefined;
|
|
219
219
|
readonly usageMetadata?: {
|
|
220
|
-
readonly cachedContentTokenCount?: number | undefined;
|
|
221
|
-
readonly thoughtsTokenCount?: number | undefined;
|
|
222
|
-
readonly promptTokenCount?: number | undefined;
|
|
223
|
-
readonly candidatesTokenCount?: number | undefined;
|
|
224
|
-
readonly totalTokenCount?: number | undefined;
|
|
225
|
-
} | undefined;
|
|
220
|
+
readonly cachedContentTokenCount?: number | null | undefined;
|
|
221
|
+
readonly thoughtsTokenCount?: number | null | undefined;
|
|
222
|
+
readonly promptTokenCount?: number | null | undefined;
|
|
223
|
+
readonly candidatesTokenCount?: number | null | undefined;
|
|
224
|
+
readonly totalTokenCount?: number | null | undefined;
|
|
225
|
+
} | null | undefined;
|
|
226
226
|
}, {
|
|
227
227
|
hasToolCalls: boolean;
|
|
228
228
|
lifecycle: Lifecycle.State;
|
|
229
229
|
}>;
|
|
230
230
|
export declare const route: Route<{
|
|
231
231
|
readonly contents: readonly {
|
|
232
|
-
readonly
|
|
233
|
-
readonly parts: readonly ({
|
|
232
|
+
readonly parts?: readonly ({
|
|
234
233
|
readonly inlineData: {
|
|
235
234
|
readonly mimeType: string;
|
|
236
235
|
readonly data: string;
|
|
237
236
|
};
|
|
238
237
|
} | {
|
|
239
238
|
readonly text: string;
|
|
240
|
-
readonly thought?: boolean | undefined;
|
|
241
|
-
readonly thoughtSignature?: string | undefined;
|
|
239
|
+
readonly thought?: boolean | null | undefined;
|
|
240
|
+
readonly thoughtSignature?: string | null | undefined;
|
|
242
241
|
} | {
|
|
243
242
|
readonly functionCall: {
|
|
244
243
|
readonly name: string;
|
|
245
|
-
readonly id?: string | undefined;
|
|
244
|
+
readonly id?: string | null | undefined;
|
|
246
245
|
readonly args?: unknown;
|
|
247
246
|
};
|
|
248
|
-
readonly thoughtSignature?: string | undefined;
|
|
247
|
+
readonly thoughtSignature?: string | null | undefined;
|
|
249
248
|
} | {
|
|
250
249
|
readonly functionResponse: {
|
|
251
250
|
readonly name: string;
|
|
@@ -258,7 +257,8 @@ export declare const route: Route<{
|
|
|
258
257
|
};
|
|
259
258
|
}[] | undefined;
|
|
260
259
|
};
|
|
261
|
-
})[];
|
|
260
|
+
})[] | null | undefined;
|
|
261
|
+
readonly role?: "model" | "user" | null | undefined;
|
|
262
262
|
}[];
|
|
263
263
|
readonly tools?: readonly {
|
|
264
264
|
readonly functionDeclarations: readonly {
|
package/dist/protocols/gemini.js
CHANGED
|
@@ -6,7 +6,7 @@ import { Endpoint } from "../route/endpoint.js";
|
|
|
6
6
|
import { Framing } from "../route/framing.js";
|
|
7
7
|
import { Protocol } from "../route/protocol.js";
|
|
8
8
|
import { LLMEvent, Usage, } from "../schema/index.js";
|
|
9
|
-
import { JsonObject, optionalArray, ProviderShared } from "./shared.js";
|
|
9
|
+
import { JsonObject, optionalArray, optionalNull, ProviderShared } from "./shared.js";
|
|
10
10
|
import { GeminiToolSchema } from "./utils/gemini-tool-schema.js";
|
|
11
11
|
import { Lifecycle } from "./utils/lifecycle.js";
|
|
12
12
|
import { ToolSchemaProjection } from "./utils/tool-schema.js";
|
|
@@ -29,13 +29,24 @@ const requiresThoughtSignatureFallback = (modelID) => {
|
|
|
29
29
|
// Gemini 3 accepts media nested inside function responses; matched Gemini 2.5 variants reject it,
|
|
30
30
|
// so their tool-result attachments lower as a separate user turn instead.
|
|
31
31
|
const routesLegacyToolMedia = (modelID) => /gemini-2[.-]5(?:[.-]|$)/i.test(modelID);
|
|
32
|
+
// Blacklist: Gemini 1.x/2.x ignore or reject explicit function call ids.
|
|
33
|
+
// Every other model id (Gemini 3+, gemma, anything unrecognized) gets them.
|
|
34
|
+
const omitsFunctionCallIds = (modelID) => {
|
|
35
|
+
const match = /^gemini(?:-live)?-(\d+)/i.exec(modelID);
|
|
36
|
+
return match !== null && Number(match[1]) < 3;
|
|
37
|
+
};
|
|
32
38
|
// =============================================================================
|
|
33
39
|
// Request Body Schema
|
|
34
40
|
// =============================================================================
|
|
41
|
+
// Gemini is known to send explicit `null` for optional streaming fields
|
|
42
|
+
// (usage counts, flags, whole subtrees), so every response-side optional uses
|
|
43
|
+
// `optionalNull` instead of bare `Schema.optional`. The same part/content
|
|
44
|
+
// schemas lower the outbound request body; encoding drops `undefined` keys,
|
|
45
|
+
// so the shared schemas stay safe there.
|
|
35
46
|
const GeminiTextPart = Schema.Struct({
|
|
36
47
|
text: Schema.String,
|
|
37
|
-
thought:
|
|
38
|
-
thoughtSignature:
|
|
48
|
+
thought: optionalNull(Schema.Boolean),
|
|
49
|
+
thoughtSignature: optionalNull(Schema.String),
|
|
39
50
|
});
|
|
40
51
|
const GeminiInlineDataPart = Schema.Struct({
|
|
41
52
|
inlineData: Schema.Struct({
|
|
@@ -45,11 +56,11 @@ const GeminiInlineDataPart = Schema.Struct({
|
|
|
45
56
|
});
|
|
46
57
|
const GeminiFunctionCallPart = Schema.Struct({
|
|
47
58
|
functionCall: Schema.Struct({
|
|
48
|
-
id:
|
|
59
|
+
id: optionalNull(Schema.String),
|
|
49
60
|
name: Schema.String,
|
|
50
61
|
args: Schema.optional(Schema.Unknown),
|
|
51
62
|
}),
|
|
52
|
-
thoughtSignature:
|
|
63
|
+
thoughtSignature: optionalNull(Schema.String),
|
|
53
64
|
});
|
|
54
65
|
const GeminiFunctionResponsePart = Schema.Struct({
|
|
55
66
|
functionResponse: Schema.Struct({
|
|
@@ -66,8 +77,8 @@ const GeminiContentPart = Schema.Union([
|
|
|
66
77
|
GeminiFunctionResponsePart,
|
|
67
78
|
]);
|
|
68
79
|
const GeminiContent = Schema.Struct({
|
|
69
|
-
role: Schema.Literals(["user", "model"]),
|
|
70
|
-
parts: Schema.Array(GeminiContentPart),
|
|
80
|
+
role: optionalNull(Schema.Literals(["user", "model"])),
|
|
81
|
+
parts: optionalNull(Schema.Array(GeminiContentPart)),
|
|
71
82
|
});
|
|
72
83
|
const GeminiSystemInstruction = Schema.Struct({
|
|
73
84
|
parts: Schema.Array(Schema.Struct({ text: Schema.String })),
|
|
@@ -119,25 +130,25 @@ const GeminiBodyFields = {
|
|
|
119
130
|
};
|
|
120
131
|
const GeminiBody = Schema.Struct(GeminiBodyFields);
|
|
121
132
|
const GeminiUsage = Schema.Struct({
|
|
122
|
-
cachedContentTokenCount:
|
|
123
|
-
thoughtsTokenCount:
|
|
124
|
-
promptTokenCount:
|
|
125
|
-
candidatesTokenCount:
|
|
126
|
-
totalTokenCount:
|
|
133
|
+
cachedContentTokenCount: optionalNull(Schema.Number),
|
|
134
|
+
thoughtsTokenCount: optionalNull(Schema.Number),
|
|
135
|
+
promptTokenCount: optionalNull(Schema.Number),
|
|
136
|
+
candidatesTokenCount: optionalNull(Schema.Number),
|
|
137
|
+
totalTokenCount: optionalNull(Schema.Number),
|
|
127
138
|
});
|
|
128
139
|
const GeminiCandidate = Schema.Struct({
|
|
129
|
-
content:
|
|
130
|
-
finishReason:
|
|
140
|
+
content: optionalNull(GeminiContent),
|
|
141
|
+
finishReason: optionalNull(Schema.String),
|
|
131
142
|
});
|
|
132
143
|
const GeminiPromptFeedback = Schema.StructWithRest(Schema.Struct({
|
|
133
|
-
blockReason:
|
|
134
|
-
blockReasonMessage:
|
|
135
|
-
safetyRatings:
|
|
144
|
+
blockReason: optionalNull(Schema.String),
|
|
145
|
+
blockReasonMessage: optionalNull(Schema.String),
|
|
146
|
+
safetyRatings: optionalNull(Schema.Unknown),
|
|
136
147
|
}), [Schema.Record(Schema.String, Schema.Unknown)]);
|
|
137
148
|
const GeminiEvent = Schema.Struct({
|
|
138
|
-
candidates:
|
|
139
|
-
promptFeedback:
|
|
140
|
-
usageMetadata:
|
|
149
|
+
candidates: optionalNull(Schema.Array(GeminiCandidate)),
|
|
150
|
+
promptFeedback: optionalNull(GeminiPromptFeedback),
|
|
151
|
+
usageMetadata: optionalNull(GeminiUsage),
|
|
141
152
|
});
|
|
142
153
|
// =============================================================================
|
|
143
154
|
// Tool Schema Conversion
|
|
@@ -188,18 +199,13 @@ const thoughtSignature = (providerMetadata) => {
|
|
|
188
199
|
? google.thoughtSignature
|
|
189
200
|
: undefined;
|
|
190
201
|
};
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
return ProviderShared.isRecord(google) && typeof google.functionCallId === "string"
|
|
194
|
-
? google.functionCallId
|
|
195
|
-
: undefined;
|
|
196
|
-
};
|
|
197
|
-
const lowerToolCall = (part) => ({
|
|
198
|
-
functionCall: { id: functionCallId(part.providerMetadata), name: part.name, args: part.input },
|
|
202
|
+
const lowerToolCall = (part, omitIds) => ({
|
|
203
|
+
functionCall: { ...(omitIds ? {} : { id: part.id }), name: part.name, args: part.input },
|
|
199
204
|
thoughtSignature: thoughtSignature(part.providerMetadata),
|
|
200
205
|
});
|
|
201
206
|
const lowerMessages = Effect.fn("Gemini.lowerMessages")(function* (request) {
|
|
202
207
|
const contents = [];
|
|
208
|
+
const omitCallIds = omitsFunctionCallIds(request.model.id);
|
|
203
209
|
const legacyToolMedia = routesLegacyToolMedia(request.model.id);
|
|
204
210
|
let pendingMedia;
|
|
205
211
|
const flushMedia = () => {
|
|
@@ -216,8 +222,8 @@ const lowerMessages = Effect.fn("Gemini.lowerMessages")(function* (request) {
|
|
|
216
222
|
const previous = contents.at(-1);
|
|
217
223
|
// Gemini rejects a continuation whose function-response turn carries extra
|
|
218
224
|
// parts, so an update after a tool result starts its own user turn.
|
|
219
|
-
if (previous?.role === "user" && !previous.parts.some((item) => "functionResponse" in item))
|
|
220
|
-
contents[contents.length - 1] = { role: "user", parts: [...previous.parts, { text: part.text }] };
|
|
225
|
+
if (previous?.role === "user" && !(previous.parts ?? []).some((item) => "functionResponse" in item))
|
|
226
|
+
contents[contents.length - 1] = { role: "user", parts: [...(previous.parts ?? []), { text: part.text }] };
|
|
221
227
|
else
|
|
222
228
|
contents.push({ role: "user", parts: [{ text: part.text }] });
|
|
223
229
|
continue;
|
|
@@ -248,7 +254,7 @@ const lowerMessages = Effect.fn("Gemini.lowerMessages")(function* (request) {
|
|
|
248
254
|
continue;
|
|
249
255
|
}
|
|
250
256
|
if (part.type === "tool-call") {
|
|
251
|
-
const lowered = lowerToolCall(part);
|
|
257
|
+
const lowered = lowerToolCall(part, omitCallIds);
|
|
252
258
|
const signature = lowered.thoughtSignature;
|
|
253
259
|
parts.push({
|
|
254
260
|
...lowered,
|
|
@@ -272,7 +278,7 @@ const lowerMessages = Effect.fn("Gemini.lowerMessages")(function* (request) {
|
|
|
272
278
|
if (part.result.type !== "content") {
|
|
273
279
|
parts.push({
|
|
274
280
|
functionResponse: {
|
|
275
|
-
id:
|
|
281
|
+
...(omitCallIds ? {} : { id: part.id }),
|
|
276
282
|
name: part.name,
|
|
277
283
|
response: {
|
|
278
284
|
name: part.name,
|
|
@@ -295,7 +301,7 @@ const lowerMessages = Effect.fn("Gemini.lowerMessages")(function* (request) {
|
|
|
295
301
|
(pendingMedia ??= []).push(...media);
|
|
296
302
|
parts.push({
|
|
297
303
|
functionResponse: {
|
|
298
|
-
id:
|
|
304
|
+
...(omitCallIds ? {} : { id: part.id }),
|
|
299
305
|
name: part.name,
|
|
300
306
|
response: {
|
|
301
307
|
name: part.name,
|
|
@@ -308,8 +314,8 @@ const lowerMessages = Effect.fn("Gemini.lowerMessages")(function* (request) {
|
|
|
308
314
|
// Gemini requires every response to a parallel call batch in one user turn,
|
|
309
315
|
// so consecutive tool results join the open function-response turn.
|
|
310
316
|
const previous = contents.at(-1);
|
|
311
|
-
if (previous?.role === "user" && previous.parts.some((item) => "functionResponse" in item))
|
|
312
|
-
contents[contents.length - 1] = { role: "user", parts: [...previous.parts, ...parts] };
|
|
317
|
+
if (previous?.role === "user" && (previous.parts ?? []).some((item) => "functionResponse" in item))
|
|
318
|
+
contents[contents.length - 1] = { role: "user", parts: [...(previous.parts ?? []), ...parts] };
|
|
313
319
|
else
|
|
314
320
|
contents.push({ role: "user", parts });
|
|
315
321
|
}
|
|
@@ -388,20 +394,25 @@ const fromRequest = Effect.fn("Gemini.fromRequest")(function* (request) {
|
|
|
388
394
|
const mapUsage = (usage) => {
|
|
389
395
|
if (!usage)
|
|
390
396
|
return undefined;
|
|
391
|
-
|
|
392
|
-
|
|
397
|
+
// Explicit provider nulls decode as `null`; normalize to `undefined` so the
|
|
398
|
+
// token arithmetic below treats them like absent counts.
|
|
399
|
+
const promptTokens = usage.promptTokenCount ?? undefined;
|
|
400
|
+
const cached = usage.cachedContentTokenCount ?? undefined;
|
|
401
|
+
const thoughts = usage.thoughtsTokenCount ?? undefined;
|
|
402
|
+
const visible = usage.candidatesTokenCount ?? undefined;
|
|
403
|
+
const nonCached = ProviderShared.subtractTokens(promptTokens, cached);
|
|
393
404
|
// `candidatesTokenCount` is visible-only; sum with thoughts to produce the
|
|
394
405
|
// inclusive `outputTokens` the contract expects. Only compute the total
|
|
395
406
|
// when the visible component is reported — otherwise we'd fabricate an
|
|
396
407
|
// inclusive number from a partial breakdown.
|
|
397
|
-
const outputTokens =
|
|
408
|
+
const outputTokens = visible !== undefined ? visible + (thoughts ?? 0) : undefined;
|
|
398
409
|
return new Usage({
|
|
399
|
-
inputTokens:
|
|
410
|
+
inputTokens: promptTokens,
|
|
400
411
|
outputTokens,
|
|
401
412
|
nonCachedInputTokens: nonCached,
|
|
402
413
|
cacheReadInputTokens: cached,
|
|
403
|
-
reasoningTokens:
|
|
404
|
-
totalTokens: ProviderShared.totalTokens(
|
|
414
|
+
reasoningTokens: thoughts,
|
|
415
|
+
totalTokens: ProviderShared.totalTokens(promptTokens, outputTokens, usage.totalTokenCount ?? undefined),
|
|
405
416
|
providerMetadata: { google: usage },
|
|
406
417
|
});
|
|
407
418
|
};
|
|
@@ -433,7 +444,9 @@ const mapFinishReason = (finishReason, hasToolCalls) => {
|
|
|
433
444
|
return "unknown";
|
|
434
445
|
};
|
|
435
446
|
const finish = (state) => {
|
|
436
|
-
|
|
447
|
+
// `?? undefined` normalizes an explicit `null` blockReason back to absent so
|
|
448
|
+
// the "nothing to finish" check below keeps its meaning.
|
|
449
|
+
const promptBlockReason = state.finishReason === undefined ? (state.promptFeedback?.blockReason ?? undefined) : undefined;
|
|
437
450
|
const finishReason = state.finishReason ?? promptBlockReason;
|
|
438
451
|
if (finishReason === undefined && state.usage === undefined)
|
|
439
452
|
return [];
|
|
@@ -470,7 +483,9 @@ const step = (state, event) => {
|
|
|
470
483
|
let lifecycle = nextState.lifecycle;
|
|
471
484
|
let reasoningSignature = nextState.reasoningSignature;
|
|
472
485
|
let textSignature = nextState.textSignature;
|
|
473
|
-
|
|
486
|
+
// Supplier ids must be tracked across chunks of the same response, not just within one event's parts.
|
|
487
|
+
const seenCallIds = new Set(nextState.seenCallIds);
|
|
488
|
+
for (const part of candidate.content.parts ?? []) {
|
|
474
489
|
const signature = "thoughtSignature" in part && part.thoughtSignature ? part.thoughtSignature : undefined;
|
|
475
490
|
// Gemini attaches replay signatures to thought parts, visible text, or function calls;
|
|
476
491
|
// each block kind must retain the signature attached to its own parts.
|
|
@@ -490,20 +505,22 @@ const step = (state, event) => {
|
|
|
490
505
|
}
|
|
491
506
|
if ("functionCall" in part) {
|
|
492
507
|
const input = part.functionCall.args === undefined ? {} : part.functionCall.args;
|
|
493
|
-
// Gemini 2.0+
|
|
508
|
+
// Gemini 2.0+ supplies a unique function call ID on the part; when omitted (e.g. Gemini 1.5),
|
|
494
509
|
// generate a globally unique ID rather than a per-request counter to prevent cross-request collisions in downstream registries.
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
510
|
+
// A repeated supplier id would replay as two identical calls, so only the first occurrence keeps it.
|
|
511
|
+
// A `null` supplier id normalizes to absent so the generated-id fallback applies.
|
|
512
|
+
const supplied = part.functionCall.id ?? undefined;
|
|
513
|
+
const duplicate = supplied !== undefined && seenCallIds.has(supplied);
|
|
514
|
+
if (supplied !== undefined)
|
|
515
|
+
seenCallIds.add(supplied);
|
|
516
|
+
const id = supplied !== undefined && !duplicate ? supplied : `tool_${crypto.randomUUID().replaceAll("-", "")}`;
|
|
500
517
|
lifecycle = Lifecycle.reasoningEnd(lifecycle, events, "reasoning-0", reasoningSignature ? googleMetadata({ thoughtSignature: reasoningSignature }) : undefined);
|
|
501
518
|
lifecycle = Lifecycle.stepStart(lifecycle, events);
|
|
502
519
|
events.push(LLMEvent.toolCall({
|
|
503
520
|
id,
|
|
504
521
|
name: part.functionCall.name,
|
|
505
522
|
input,
|
|
506
|
-
providerMetadata:
|
|
523
|
+
providerMetadata: part.thoughtSignature ? googleMetadata({ thoughtSignature: part.thoughtSignature }) : undefined,
|
|
507
524
|
}));
|
|
508
525
|
hasToolCalls = true;
|
|
509
526
|
}
|
|
@@ -515,6 +532,7 @@ const step = (state, event) => {
|
|
|
515
532
|
lifecycle,
|
|
516
533
|
reasoningSignature,
|
|
517
534
|
textSignature,
|
|
535
|
+
seenCallIds,
|
|
518
536
|
finishReason: candidate.finishReason ?? nextState.finishReason,
|
|
519
537
|
},
|
|
520
538
|
events,
|
|
@@ -207,7 +207,7 @@ export declare const coreFields: {
|
|
|
207
207
|
}>>;
|
|
208
208
|
top_logprobs: Schema.optional<Schema.Int>;
|
|
209
209
|
truncation: Schema.optional<Schema.Literals<readonly ["auto", "disabled"]>>;
|
|
210
|
-
service_tier: Schema.optional<Schema.
|
|
210
|
+
service_tier: Schema.optional<Schema.declare<OpenResponsesOptions.ServiceTier, OpenResponsesOptions.ServiceTier>>;
|
|
211
211
|
prompt_cache_key: Schema.optional<Schema.String>;
|
|
212
212
|
include: Schema.optional<Schema.$Array<Schema.declare<OpenResponsesOptions.ResponseIncludable, OpenResponsesOptions.ResponseIncludable>>>;
|
|
213
213
|
reasoning: Schema.optional<Schema.Struct<{
|
|
@@ -320,7 +320,7 @@ declare const OpenResponsesBody: Schema.Struct<{
|
|
|
320
320
|
}>>;
|
|
321
321
|
readonly top_logprobs: Schema.optional<Schema.Int>;
|
|
322
322
|
readonly truncation: Schema.optional<Schema.Literals<readonly ["auto", "disabled"]>>;
|
|
323
|
-
readonly service_tier: Schema.optional<Schema.
|
|
323
|
+
readonly service_tier: Schema.optional<Schema.declare<OpenResponsesOptions.ServiceTier, OpenResponsesOptions.ServiceTier>>;
|
|
324
324
|
readonly prompt_cache_key: Schema.optional<Schema.String>;
|
|
325
325
|
readonly include: Schema.optional<Schema.$Array<Schema.declare<OpenResponsesOptions.ResponseIncludable, OpenResponsesOptions.ResponseIncludable>>>;
|
|
326
326
|
readonly reasoning: Schema.optional<Schema.Struct<{
|
|
@@ -451,7 +451,6 @@ export interface ParserState {
|
|
|
451
451
|
readonly hasFunctionCall: boolean;
|
|
452
452
|
readonly lifecycle: Lifecycle.State;
|
|
453
453
|
readonly messageItems: ReadonlySet<string>;
|
|
454
|
-
readonly messagePhase: (value: unknown) => MessagePhase | null | undefined;
|
|
455
454
|
readonly messagePhases: Readonly<Record<string, MessagePhase | null>>;
|
|
456
455
|
readonly reasoningItems: Readonly<Record<string, ReasoningStreamItem>>;
|
|
457
456
|
readonly store: boolean | undefined;
|
|
@@ -480,7 +479,7 @@ export declare const fromRequestWithExtension: (request: LLMRequest, extension:
|
|
|
480
479
|
truncation?: "auto" | "disabled" | undefined;
|
|
481
480
|
parallel_tool_calls?: boolean | undefined;
|
|
482
481
|
max_tool_calls?: number | undefined;
|
|
483
|
-
service_tier?:
|
|
482
|
+
service_tier?: OpenResponsesOptions.ServiceTier | undefined;
|
|
484
483
|
text?: {
|
|
485
484
|
verbosity: OpenResponsesOptions.TextVerbosity;
|
|
486
485
|
} | undefined;
|
|
@@ -641,7 +640,7 @@ export declare const fromRequest: (request: LLMRequest) => Effect.Effect<{
|
|
|
641
640
|
readonly presence_penalty?: number | undefined;
|
|
642
641
|
readonly safety_identifier?: string | undefined;
|
|
643
642
|
readonly top_logprobs?: number | undefined;
|
|
644
|
-
readonly service_tier?:
|
|
643
|
+
readonly service_tier?: OpenResponsesOptions.ServiceTier | undefined;
|
|
645
644
|
readonly max_output_tokens?: number | undefined;
|
|
646
645
|
readonly max_tool_calls?: number | undefined;
|
|
647
646
|
readonly parallel_tool_calls?: boolean | undefined;
|
|
@@ -951,7 +950,7 @@ export declare const protocol: Protocol<{
|
|
|
951
950
|
readonly presence_penalty?: number | undefined;
|
|
952
951
|
readonly safety_identifier?: string | undefined;
|
|
953
952
|
readonly top_logprobs?: number | undefined;
|
|
954
|
-
readonly service_tier?:
|
|
953
|
+
readonly service_tier?: OpenResponsesOptions.ServiceTier | undefined;
|
|
955
954
|
readonly max_output_tokens?: number | undefined;
|
|
956
955
|
readonly max_tool_calls?: number | undefined;
|
|
957
956
|
readonly parallel_tool_calls?: boolean | undefined;
|
|
@@ -1122,7 +1121,7 @@ export declare const httpTransport: HttpTransport.HttpJsonTransport<{
|
|
|
1122
1121
|
readonly presence_penalty?: number | undefined;
|
|
1123
1122
|
readonly safety_identifier?: string | undefined;
|
|
1124
1123
|
readonly top_logprobs?: number | undefined;
|
|
1125
|
-
readonly service_tier?:
|
|
1124
|
+
readonly service_tier?: OpenResponsesOptions.ServiceTier | undefined;
|
|
1126
1125
|
readonly max_output_tokens?: number | undefined;
|
|
1127
1126
|
readonly max_tool_calls?: number | undefined;
|
|
1128
1127
|
readonly parallel_tool_calls?: boolean | undefined;
|