@opencode-ai/ai 0.0.0-dev-18001 → 0.0.0-dev-18017
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 +47 -34
- package/dist/protocols/open-responses.d.ts +6 -6
- package/dist/protocols/open-responses.js +1 -1
- 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 +1 -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";
|
|
@@ -38,10 +38,15 @@ const omitsFunctionCallIds = (modelID) => {
|
|
|
38
38
|
// =============================================================================
|
|
39
39
|
// Request Body Schema
|
|
40
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.
|
|
41
46
|
const GeminiTextPart = Schema.Struct({
|
|
42
47
|
text: Schema.String,
|
|
43
|
-
thought:
|
|
44
|
-
thoughtSignature:
|
|
48
|
+
thought: optionalNull(Schema.Boolean),
|
|
49
|
+
thoughtSignature: optionalNull(Schema.String),
|
|
45
50
|
});
|
|
46
51
|
const GeminiInlineDataPart = Schema.Struct({
|
|
47
52
|
inlineData: Schema.Struct({
|
|
@@ -51,11 +56,11 @@ const GeminiInlineDataPart = Schema.Struct({
|
|
|
51
56
|
});
|
|
52
57
|
const GeminiFunctionCallPart = Schema.Struct({
|
|
53
58
|
functionCall: Schema.Struct({
|
|
54
|
-
id:
|
|
59
|
+
id: optionalNull(Schema.String),
|
|
55
60
|
name: Schema.String,
|
|
56
61
|
args: Schema.optional(Schema.Unknown),
|
|
57
62
|
}),
|
|
58
|
-
thoughtSignature:
|
|
63
|
+
thoughtSignature: optionalNull(Schema.String),
|
|
59
64
|
});
|
|
60
65
|
const GeminiFunctionResponsePart = Schema.Struct({
|
|
61
66
|
functionResponse: Schema.Struct({
|
|
@@ -72,8 +77,8 @@ const GeminiContentPart = Schema.Union([
|
|
|
72
77
|
GeminiFunctionResponsePart,
|
|
73
78
|
]);
|
|
74
79
|
const GeminiContent = Schema.Struct({
|
|
75
|
-
role: Schema.Literals(["user", "model"]),
|
|
76
|
-
parts: Schema.Array(GeminiContentPart),
|
|
80
|
+
role: optionalNull(Schema.Literals(["user", "model"])),
|
|
81
|
+
parts: optionalNull(Schema.Array(GeminiContentPart)),
|
|
77
82
|
});
|
|
78
83
|
const GeminiSystemInstruction = Schema.Struct({
|
|
79
84
|
parts: Schema.Array(Schema.Struct({ text: Schema.String })),
|
|
@@ -125,25 +130,25 @@ const GeminiBodyFields = {
|
|
|
125
130
|
};
|
|
126
131
|
const GeminiBody = Schema.Struct(GeminiBodyFields);
|
|
127
132
|
const GeminiUsage = Schema.Struct({
|
|
128
|
-
cachedContentTokenCount:
|
|
129
|
-
thoughtsTokenCount:
|
|
130
|
-
promptTokenCount:
|
|
131
|
-
candidatesTokenCount:
|
|
132
|
-
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),
|
|
133
138
|
});
|
|
134
139
|
const GeminiCandidate = Schema.Struct({
|
|
135
|
-
content:
|
|
136
|
-
finishReason:
|
|
140
|
+
content: optionalNull(GeminiContent),
|
|
141
|
+
finishReason: optionalNull(Schema.String),
|
|
137
142
|
});
|
|
138
143
|
const GeminiPromptFeedback = Schema.StructWithRest(Schema.Struct({
|
|
139
|
-
blockReason:
|
|
140
|
-
blockReasonMessage:
|
|
141
|
-
safetyRatings:
|
|
144
|
+
blockReason: optionalNull(Schema.String),
|
|
145
|
+
blockReasonMessage: optionalNull(Schema.String),
|
|
146
|
+
safetyRatings: optionalNull(Schema.Unknown),
|
|
142
147
|
}), [Schema.Record(Schema.String, Schema.Unknown)]);
|
|
143
148
|
const GeminiEvent = Schema.Struct({
|
|
144
|
-
candidates:
|
|
145
|
-
promptFeedback:
|
|
146
|
-
usageMetadata:
|
|
149
|
+
candidates: optionalNull(Schema.Array(GeminiCandidate)),
|
|
150
|
+
promptFeedback: optionalNull(GeminiPromptFeedback),
|
|
151
|
+
usageMetadata: optionalNull(GeminiUsage),
|
|
147
152
|
});
|
|
148
153
|
// =============================================================================
|
|
149
154
|
// Tool Schema Conversion
|
|
@@ -217,8 +222,8 @@ const lowerMessages = Effect.fn("Gemini.lowerMessages")(function* (request) {
|
|
|
217
222
|
const previous = contents.at(-1);
|
|
218
223
|
// Gemini rejects a continuation whose function-response turn carries extra
|
|
219
224
|
// parts, so an update after a tool result starts its own user turn.
|
|
220
|
-
if (previous?.role === "user" && !previous.parts.some((item) => "functionResponse" in item))
|
|
221
|
-
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 }] };
|
|
222
227
|
else
|
|
223
228
|
contents.push({ role: "user", parts: [{ text: part.text }] });
|
|
224
229
|
continue;
|
|
@@ -309,8 +314,8 @@ const lowerMessages = Effect.fn("Gemini.lowerMessages")(function* (request) {
|
|
|
309
314
|
// Gemini requires every response to a parallel call batch in one user turn,
|
|
310
315
|
// so consecutive tool results join the open function-response turn.
|
|
311
316
|
const previous = contents.at(-1);
|
|
312
|
-
if (previous?.role === "user" && previous.parts.some((item) => "functionResponse" in item))
|
|
313
|
-
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] };
|
|
314
319
|
else
|
|
315
320
|
contents.push({ role: "user", parts });
|
|
316
321
|
}
|
|
@@ -389,20 +394,25 @@ const fromRequest = Effect.fn("Gemini.fromRequest")(function* (request) {
|
|
|
389
394
|
const mapUsage = (usage) => {
|
|
390
395
|
if (!usage)
|
|
391
396
|
return undefined;
|
|
392
|
-
|
|
393
|
-
|
|
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);
|
|
394
404
|
// `candidatesTokenCount` is visible-only; sum with thoughts to produce the
|
|
395
405
|
// inclusive `outputTokens` the contract expects. Only compute the total
|
|
396
406
|
// when the visible component is reported — otherwise we'd fabricate an
|
|
397
407
|
// inclusive number from a partial breakdown.
|
|
398
|
-
const outputTokens =
|
|
408
|
+
const outputTokens = visible !== undefined ? visible + (thoughts ?? 0) : undefined;
|
|
399
409
|
return new Usage({
|
|
400
|
-
inputTokens:
|
|
410
|
+
inputTokens: promptTokens,
|
|
401
411
|
outputTokens,
|
|
402
412
|
nonCachedInputTokens: nonCached,
|
|
403
413
|
cacheReadInputTokens: cached,
|
|
404
|
-
reasoningTokens:
|
|
405
|
-
totalTokens: ProviderShared.totalTokens(
|
|
414
|
+
reasoningTokens: thoughts,
|
|
415
|
+
totalTokens: ProviderShared.totalTokens(promptTokens, outputTokens, usage.totalTokenCount ?? undefined),
|
|
406
416
|
providerMetadata: { google: usage },
|
|
407
417
|
});
|
|
408
418
|
};
|
|
@@ -434,7 +444,9 @@ const mapFinishReason = (finishReason, hasToolCalls) => {
|
|
|
434
444
|
return "unknown";
|
|
435
445
|
};
|
|
436
446
|
const finish = (state) => {
|
|
437
|
-
|
|
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;
|
|
438
450
|
const finishReason = state.finishReason ?? promptBlockReason;
|
|
439
451
|
if (finishReason === undefined && state.usage === undefined)
|
|
440
452
|
return [];
|
|
@@ -473,7 +485,7 @@ const step = (state, event) => {
|
|
|
473
485
|
let textSignature = nextState.textSignature;
|
|
474
486
|
// Supplier ids must be tracked across chunks of the same response, not just within one event's parts.
|
|
475
487
|
const seenCallIds = new Set(nextState.seenCallIds);
|
|
476
|
-
for (const part of candidate.content.parts) {
|
|
488
|
+
for (const part of candidate.content.parts ?? []) {
|
|
477
489
|
const signature = "thoughtSignature" in part && part.thoughtSignature ? part.thoughtSignature : undefined;
|
|
478
490
|
// Gemini attaches replay signatures to thought parts, visible text, or function calls;
|
|
479
491
|
// each block kind must retain the signature attached to its own parts.
|
|
@@ -496,7 +508,8 @@ const step = (state, event) => {
|
|
|
496
508
|
// Gemini 2.0+ supplies a unique function call ID on the part; when omitted (e.g. Gemini 1.5),
|
|
497
509
|
// generate a globally unique ID rather than a per-request counter to prevent cross-request collisions in downstream registries.
|
|
498
510
|
// A repeated supplier id would replay as two identical calls, so only the first occurrence keeps it.
|
|
499
|
-
|
|
511
|
+
// A `null` supplier id normalizes to absent so the generated-id fallback applies.
|
|
512
|
+
const supplied = part.functionCall.id ?? undefined;
|
|
500
513
|
const duplicate = supplied !== undefined && seenCallIds.has(supplied);
|
|
501
514
|
if (supplied !== undefined)
|
|
502
515
|
seenCallIds.add(supplied);
|
|
@@ -507,7 +520,7 @@ const step = (state, event) => {
|
|
|
507
520
|
id,
|
|
508
521
|
name: part.functionCall.name,
|
|
509
522
|
input,
|
|
510
|
-
providerMetadata: part.thoughtSignature
|
|
523
|
+
providerMetadata: part.thoughtSignature ? googleMetadata({ thoughtSignature: part.thoughtSignature }) : undefined,
|
|
511
524
|
}));
|
|
512
525
|
hasToolCalls = true;
|
|
513
526
|
}
|
|
@@ -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;
|
|
@@ -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 };
|
|
@@ -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;
|
|
@@ -123,6 +123,9 @@ const OpenAIChatUsage = Schema.StructWithRest(Schema.Struct({
|
|
|
123
123
|
prompt_tokens: optionalNull(Schema.Number),
|
|
124
124
|
completion_tokens: optionalNull(Schema.Number),
|
|
125
125
|
total_tokens: optionalNull(Schema.Number),
|
|
126
|
+
// Zai reports cache hits as top-level `cached_tokens`; DeepSeek uses `prompt_cache_hit_tokens`.
|
|
127
|
+
cached_tokens: optionalNull(Schema.Number),
|
|
128
|
+
prompt_cache_hit_tokens: optionalNull(Schema.Number),
|
|
126
129
|
prompt_tokens_details: optionalNull(Schema.StructWithRest(Schema.Struct({
|
|
127
130
|
cached_tokens: optionalNull(Schema.Number),
|
|
128
131
|
cache_write_tokens: optionalNull(Schema.Number),
|
|
@@ -151,11 +154,13 @@ const OpenAIChatDelta = Schema.StructWithRest(Schema.Struct({
|
|
|
151
154
|
reasoning_details: optionalNull(Schema.Unknown),
|
|
152
155
|
tool_calls: optionalNull(Schema.Array(OpenAIChatToolCallDelta)),
|
|
153
156
|
}), [Schema.Record(Schema.String, Schema.Unknown)]);
|
|
154
|
-
const OpenAIChatChoice = Schema.Struct({
|
|
157
|
+
const OpenAIChatChoice = Schema.StructWithRest(Schema.Struct({
|
|
155
158
|
delta: optionalNull(OpenAIChatDelta),
|
|
156
159
|
finish_reason: optionalNull(Schema.String),
|
|
157
160
|
native_finish_reason: optionalNull(Schema.String),
|
|
158
|
-
|
|
161
|
+
// Moonshot streams usage on `choice.usage` instead of top-level `usage`.
|
|
162
|
+
usage: optionalNull(OpenAIChatUsage),
|
|
163
|
+
}), [Schema.Record(Schema.String, Schema.Unknown)]);
|
|
159
164
|
const OpenAIChatError = Schema.Struct({
|
|
160
165
|
code: optionalNull(Schema.Union([Schema.String, Schema.Number])),
|
|
161
166
|
message: Schema.String,
|
|
@@ -396,6 +401,18 @@ const lowerMessages = Effect.fn("OpenAIChat.lowerMessages")(function* (request,
|
|
|
396
401
|
flushImages();
|
|
397
402
|
return messages;
|
|
398
403
|
});
|
|
404
|
+
// Anthropic via LiteLLM and Amazon Bedrock require `tools` to be present
|
|
405
|
+
// whenever the conversation history contains tool calls/results. Send an
|
|
406
|
+
// explicit empty array when we have history but no active tools.
|
|
407
|
+
const hasToolHistory = (messages) => {
|
|
408
|
+
for (const message of messages) {
|
|
409
|
+
if (message.role === "tool")
|
|
410
|
+
return true;
|
|
411
|
+
if (message.role === "assistant" && message.content.some((part) => part.type === "tool-call"))
|
|
412
|
+
return true;
|
|
413
|
+
}
|
|
414
|
+
return false;
|
|
415
|
+
};
|
|
399
416
|
const lowerOptions = (request) => {
|
|
400
417
|
const options = OpenAIOptions.resolve(request);
|
|
401
418
|
return {
|
|
@@ -413,11 +430,14 @@ export const fromRequest = Effect.fn("OpenAIChat.fromRequest")(function* (reques
|
|
|
413
430
|
const generation = request.generation;
|
|
414
431
|
const toolSchemaCompatibility = request.model.compatibility?.toolSchema;
|
|
415
432
|
const maxTokensField = request.model.compatibility?.maxTokensField ?? "max_tokens";
|
|
433
|
+
const hasHistory = hasToolHistory(request.messages);
|
|
416
434
|
return {
|
|
417
435
|
model: request.model.id,
|
|
418
436
|
messages: yield* lowerMessages(request, options),
|
|
419
437
|
tools: request.tools.length === 0
|
|
420
|
-
?
|
|
438
|
+
? hasHistory
|
|
439
|
+
? []
|
|
440
|
+
: undefined
|
|
421
441
|
: request.tools.map((tool) => lowerTool(tool, ToolSchemaProjection.modelCompatibility(tool.inputSchema, toolSchemaCompatibility), options)),
|
|
422
442
|
tool_choice: request.toolChoice ? yield* lowerToolChoice(request.toolChoice) : undefined,
|
|
423
443
|
stream: true,
|
|
@@ -458,12 +478,18 @@ const mapFinishReason = (reason) => {
|
|
|
458
478
|
// total) with a `reasoning_tokens` subset. We pass the inclusive totals
|
|
459
479
|
// through and derive the non-cached breakdown so the `AI.Usage` contract is
|
|
460
480
|
// satisfied on both sides.
|
|
481
|
+
// Providers differ on cache-hit location: OpenAI uses
|
|
482
|
+
// `prompt_tokens_details.cached_tokens`, DeepSeek uses
|
|
483
|
+
// `prompt_cache_hit_tokens`, and Zai uses top-level `cached_tokens`.
|
|
461
484
|
const mapUsage = (usage) => {
|
|
462
485
|
if (!usage)
|
|
463
486
|
return undefined;
|
|
464
487
|
const input = usage.prompt_tokens ?? undefined;
|
|
465
488
|
const output = usage.completion_tokens ?? undefined;
|
|
466
|
-
const cached = usage.prompt_tokens_details?.cached_tokens ??
|
|
489
|
+
const cached = (usage.prompt_tokens_details?.cached_tokens ??
|
|
490
|
+
usage.prompt_cache_hit_tokens ??
|
|
491
|
+
usage.cached_tokens ??
|
|
492
|
+
undefined);
|
|
467
493
|
const cacheWrite = usage.prompt_tokens_details?.cache_write_tokens ?? undefined;
|
|
468
494
|
const reasoning = usage.completion_tokens_details?.reasoning_tokens ?? undefined;
|
|
469
495
|
const nonCached = ProviderShared.subtractTokens(input, ProviderShared.sumTokens(cached, cacheWrite));
|
|
@@ -554,8 +580,11 @@ const step = (state, event) => Effect.gen(function* () {
|
|
|
554
580
|
}),
|
|
555
581
|
});
|
|
556
582
|
const events = [];
|
|
557
|
-
const usage = mapUsage(event.usage) ?? state.usage;
|
|
558
583
|
const choice = event.choices?.[0];
|
|
584
|
+
// Moonshot (and a few other OpenAI-compatible providers) attach usage to
|
|
585
|
+
// `choice.usage` instead of the top-level `usage` field.
|
|
586
|
+
const choiceUsage = choice?.usage;
|
|
587
|
+
const usage = mapUsage(event.usage) ?? (choiceUsage ? mapUsage(choiceUsage) : undefined) ?? state.usage;
|
|
559
588
|
const rawFinishReason = choice?.finish_reason;
|
|
560
589
|
const finishReason = rawFinishReason !== undefined && rawFinishReason !== null
|
|
561
590
|
? { normalized: mapFinishReason(rawFinishReason), raw: choice?.native_finish_reason ?? rawFinishReason }
|
|
@@ -117,7 +117,7 @@ export declare const route: Route<{
|
|
|
117
117
|
readonly presence_penalty?: number | undefined;
|
|
118
118
|
readonly safety_identifier?: string | undefined;
|
|
119
119
|
readonly top_logprobs?: number | undefined;
|
|
120
|
-
readonly service_tier?: "
|
|
120
|
+
readonly service_tier?: import("./utils/open-responses-options.js").ServiceTier | undefined;
|
|
121
121
|
readonly max_output_tokens?: number | undefined;
|
|
122
122
|
readonly max_tool_calls?: number | undefined;
|
|
123
123
|
readonly parallel_tool_calls?: boolean | undefined;
|