@opencode-ai/ai 0.0.0-beta-17963 → 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 -6
- package/dist/protocols/open-responses.js +10 -3
- 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/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/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<{
|
|
@@ -479,7 +479,7 @@ export declare const fromRequestWithExtension: (request: LLMRequest, extension:
|
|
|
479
479
|
truncation?: "auto" | "disabled" | undefined;
|
|
480
480
|
parallel_tool_calls?: boolean | undefined;
|
|
481
481
|
max_tool_calls?: number | undefined;
|
|
482
|
-
service_tier?:
|
|
482
|
+
service_tier?: OpenResponsesOptions.ServiceTier | undefined;
|
|
483
483
|
text?: {
|
|
484
484
|
verbosity: OpenResponsesOptions.TextVerbosity;
|
|
485
485
|
} | undefined;
|
|
@@ -640,7 +640,7 @@ export declare const fromRequest: (request: LLMRequest) => Effect.Effect<{
|
|
|
640
640
|
readonly presence_penalty?: number | undefined;
|
|
641
641
|
readonly safety_identifier?: string | undefined;
|
|
642
642
|
readonly top_logprobs?: number | undefined;
|
|
643
|
-
readonly service_tier?:
|
|
643
|
+
readonly service_tier?: OpenResponsesOptions.ServiceTier | undefined;
|
|
644
644
|
readonly max_output_tokens?: number | undefined;
|
|
645
645
|
readonly max_tool_calls?: number | undefined;
|
|
646
646
|
readonly parallel_tool_calls?: boolean | undefined;
|
|
@@ -950,7 +950,7 @@ export declare const protocol: Protocol<{
|
|
|
950
950
|
readonly presence_penalty?: number | undefined;
|
|
951
951
|
readonly safety_identifier?: string | undefined;
|
|
952
952
|
readonly top_logprobs?: number | undefined;
|
|
953
|
-
readonly service_tier?:
|
|
953
|
+
readonly service_tier?: OpenResponsesOptions.ServiceTier | undefined;
|
|
954
954
|
readonly max_output_tokens?: number | undefined;
|
|
955
955
|
readonly max_tool_calls?: number | undefined;
|
|
956
956
|
readonly parallel_tool_calls?: boolean | undefined;
|
|
@@ -1121,7 +1121,7 @@ export declare const httpTransport: HttpTransport.HttpJsonTransport<{
|
|
|
1121
1121
|
readonly presence_penalty?: number | undefined;
|
|
1122
1122
|
readonly safety_identifier?: string | undefined;
|
|
1123
1123
|
readonly top_logprobs?: number | undefined;
|
|
1124
|
-
readonly service_tier?:
|
|
1124
|
+
readonly service_tier?: OpenResponsesOptions.ServiceTier | undefined;
|
|
1125
1125
|
readonly max_output_tokens?: number | undefined;
|
|
1126
1126
|
readonly max_tool_calls?: number | undefined;
|
|
1127
1127
|
readonly parallel_tool_calls?: boolean | undefined;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Effect, Schema } from "effect";
|
|
2
2
|
import { HttpTransport } from "../route/transport/index.js";
|
|
3
3
|
import { Protocol } from "../route/protocol.js";
|
|
4
|
-
import { AIError, LLMEvent, Usage, } from "../schema/index.js";
|
|
4
|
+
import { AIError, LLMEvent, ProviderInternalReason, Usage, } from "../schema/index.js";
|
|
5
5
|
import { JsonObject, optionalArray, optionalNull, ProviderShared } from "./shared.js";
|
|
6
6
|
import { classifyProviderFailure } from "../provider-error.js";
|
|
7
7
|
import { OpenResponsesOptions } from "./utils/open-responses-options.js";
|
|
@@ -290,7 +290,7 @@ const lowerMedia = Effect.fn("OpenResponses.lowerMedia")(function* (part, reques
|
|
|
290
290
|
return {
|
|
291
291
|
type: "input_file",
|
|
292
292
|
filename: part.filename ?? (media.mime === "application/pdf" ? "document.pdf" : "file"),
|
|
293
|
-
...(url ? { file_url: url } : { file_data: media.
|
|
293
|
+
...(url ? { file_url: url } : { file_data: media.dataUrl }),
|
|
294
294
|
};
|
|
295
295
|
}
|
|
296
296
|
return { type: "input_image", image_url: url ?? media.dataUrl };
|
|
@@ -856,11 +856,18 @@ export const providerFailure = (id, event, fallback) => {
|
|
|
856
856
|
: typeof event.status_code === "number"
|
|
857
857
|
? event.status_code
|
|
858
858
|
: undefined;
|
|
859
|
+
const reason = event.type === "error" &&
|
|
860
|
+
event.error === undefined &&
|
|
861
|
+
event.response === undefined &&
|
|
862
|
+
summary === undefined &&
|
|
863
|
+
status === undefined
|
|
864
|
+
? new ProviderInternalReason({ message })
|
|
865
|
+
: classifyProviderFailure({ message, code, status, rawBody: body });
|
|
859
866
|
return new AIError({
|
|
860
867
|
module: id,
|
|
861
868
|
method: "stream",
|
|
862
869
|
body,
|
|
863
|
-
reason
|
|
870
|
+
reason,
|
|
864
871
|
});
|
|
865
872
|
};
|
|
866
873
|
const providerError = (state, event, fallback) => providerFailure(state.id, event, fallback);
|
|
@@ -201,7 +201,7 @@ declare const OpenAIChatBody: Schema.Struct<{
|
|
|
201
201
|
}>;
|
|
202
202
|
export type OpenAIChatBody = Schema.Schema.Type<typeof OpenAIChatBody>;
|
|
203
203
|
export declare const OpenAIChatEvent: Schema.Struct<{
|
|
204
|
-
readonly choices: Schema.optional<Schema.NullOr<Schema.$Array<Schema.Struct<{
|
|
204
|
+
readonly choices: Schema.optional<Schema.NullOr<Schema.$Array<Schema.StructWithRest<Schema.Struct<{
|
|
205
205
|
readonly delta: Schema.optional<Schema.NullOr<Schema.StructWithRest<Schema.Struct<{
|
|
206
206
|
readonly content: Schema.optional<Schema.NullOr<Schema.String>>;
|
|
207
207
|
readonly refusal: Schema.optional<Schema.NullOr<Schema.String>>;
|
|
@@ -220,11 +220,29 @@ export declare const OpenAIChatEvent: Schema.Struct<{
|
|
|
220
220
|
}>, readonly [Schema.$Record<Schema.String, Schema.Unknown>]>>>;
|
|
221
221
|
readonly finish_reason: Schema.optional<Schema.NullOr<Schema.String>>;
|
|
222
222
|
readonly native_finish_reason: Schema.optional<Schema.NullOr<Schema.String>>;
|
|
223
|
-
|
|
223
|
+
readonly usage: Schema.optional<Schema.NullOr<Schema.StructWithRest<Schema.Struct<{
|
|
224
|
+
readonly prompt_tokens: Schema.optional<Schema.NullOr<Schema.Number>>;
|
|
225
|
+
readonly completion_tokens: Schema.optional<Schema.NullOr<Schema.Number>>;
|
|
226
|
+
readonly total_tokens: Schema.optional<Schema.NullOr<Schema.Number>>;
|
|
227
|
+
readonly cached_tokens: Schema.optional<Schema.NullOr<Schema.Number>>;
|
|
228
|
+
readonly prompt_cache_hit_tokens: Schema.optional<Schema.NullOr<Schema.Number>>;
|
|
229
|
+
readonly prompt_tokens_details: Schema.optional<Schema.NullOr<Schema.StructWithRest<Schema.Struct<{
|
|
230
|
+
readonly cached_tokens: Schema.optional<Schema.NullOr<Schema.Number>>;
|
|
231
|
+
readonly cache_write_tokens: Schema.optional<Schema.NullOr<Schema.Number>>;
|
|
232
|
+
}>, readonly [Schema.$Record<Schema.String, Schema.Unknown>]>>>;
|
|
233
|
+
readonly completion_tokens_details: Schema.optional<Schema.NullOr<Schema.StructWithRest<Schema.Struct<{
|
|
234
|
+
readonly reasoning_tokens: Schema.optional<Schema.NullOr<Schema.Number>>;
|
|
235
|
+
readonly accepted_prediction_tokens: Schema.optional<Schema.NullOr<Schema.Number>>;
|
|
236
|
+
readonly rejected_prediction_tokens: Schema.optional<Schema.NullOr<Schema.Number>>;
|
|
237
|
+
}>, readonly [Schema.$Record<Schema.String, Schema.Unknown>]>>>;
|
|
238
|
+
}>, readonly [Schema.$Record<Schema.String, Schema.Unknown>]>>>;
|
|
239
|
+
}>, readonly [Schema.$Record<Schema.String, Schema.Unknown>]>>>>;
|
|
224
240
|
readonly usage: Schema.optional<Schema.NullOr<Schema.StructWithRest<Schema.Struct<{
|
|
225
241
|
readonly prompt_tokens: Schema.optional<Schema.NullOr<Schema.Number>>;
|
|
226
242
|
readonly completion_tokens: Schema.optional<Schema.NullOr<Schema.Number>>;
|
|
227
243
|
readonly total_tokens: Schema.optional<Schema.NullOr<Schema.Number>>;
|
|
244
|
+
readonly cached_tokens: Schema.optional<Schema.NullOr<Schema.Number>>;
|
|
245
|
+
readonly prompt_cache_hit_tokens: Schema.optional<Schema.NullOr<Schema.Number>>;
|
|
228
246
|
readonly prompt_tokens_details: Schema.optional<Schema.NullOr<Schema.StructWithRest<Schema.Struct<{
|
|
229
247
|
readonly cached_tokens: Schema.optional<Schema.NullOr<Schema.Number>>;
|
|
230
248
|
readonly cache_write_tokens: Schema.optional<Schema.NullOr<Schema.Number>>;
|
|
@@ -566,6 +584,8 @@ export declare const protocol: Protocol<{
|
|
|
566
584
|
readonly prompt_tokens?: number | null | undefined;
|
|
567
585
|
readonly completion_tokens?: number | null | undefined;
|
|
568
586
|
readonly total_tokens?: number | null | undefined;
|
|
587
|
+
readonly cached_tokens?: number | null | undefined;
|
|
588
|
+
readonly prompt_cache_hit_tokens?: number | null | undefined;
|
|
569
589
|
readonly prompt_tokens_details?: {
|
|
570
590
|
readonly [x: string]: unknown;
|
|
571
591
|
readonly cached_tokens?: number | null | undefined;
|
|
@@ -579,6 +599,7 @@ export declare const protocol: Protocol<{
|
|
|
579
599
|
} | null | undefined;
|
|
580
600
|
} | null | undefined;
|
|
581
601
|
readonly choices?: readonly {
|
|
602
|
+
readonly [x: string]: unknown;
|
|
582
603
|
readonly delta?: {
|
|
583
604
|
readonly [x: string]: unknown;
|
|
584
605
|
readonly reasoning?: string | null | undefined;
|
|
@@ -596,6 +617,25 @@ export declare const protocol: Protocol<{
|
|
|
596
617
|
}[] | null | undefined;
|
|
597
618
|
readonly reasoning_details?: unknown;
|
|
598
619
|
} | null | undefined;
|
|
620
|
+
readonly usage?: {
|
|
621
|
+
readonly [x: string]: unknown;
|
|
622
|
+
readonly prompt_tokens?: number | null | undefined;
|
|
623
|
+
readonly completion_tokens?: number | null | undefined;
|
|
624
|
+
readonly total_tokens?: number | null | undefined;
|
|
625
|
+
readonly cached_tokens?: number | null | undefined;
|
|
626
|
+
readonly prompt_cache_hit_tokens?: number | null | undefined;
|
|
627
|
+
readonly prompt_tokens_details?: {
|
|
628
|
+
readonly [x: string]: unknown;
|
|
629
|
+
readonly cached_tokens?: number | null | undefined;
|
|
630
|
+
readonly cache_write_tokens?: number | null | undefined;
|
|
631
|
+
} | null | undefined;
|
|
632
|
+
readonly completion_tokens_details?: {
|
|
633
|
+
readonly [x: string]: unknown;
|
|
634
|
+
readonly reasoning_tokens?: number | null | undefined;
|
|
635
|
+
readonly accepted_prediction_tokens?: number | null | undefined;
|
|
636
|
+
readonly rejected_prediction_tokens?: number | null | undefined;
|
|
637
|
+
} | null | undefined;
|
|
638
|
+
} | null | undefined;
|
|
599
639
|
readonly finish_reason?: string | null | undefined;
|
|
600
640
|
readonly native_finish_reason?: string | null | undefined;
|
|
601
641
|
}[] | null | undefined;
|