@opencode-ai/ai 0.0.0-dev-18234 → 0.0.0-dev-18237
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.
|
@@ -612,9 +612,13 @@ const endsInServerToolUse = (message) => {
|
|
|
612
612
|
const last = message.content.at(-1);
|
|
613
613
|
return message.role === "assistant" && last?.type === "tool-call" && last.providerExecuted === true;
|
|
614
614
|
};
|
|
615
|
-
const canUseNativeSystemUpdate = (
|
|
616
|
-
const previous = messages[index - 1];
|
|
617
|
-
const next = messages[index + 1];
|
|
615
|
+
const canUseNativeSystemUpdate = (request, index) => {
|
|
616
|
+
const previous = request.messages[index - 1];
|
|
617
|
+
const next = request.messages[index + 1];
|
|
618
|
+
// Vertex currently rejects/404s for a system message after local tool results,
|
|
619
|
+
// so fold it into the user tool-result turn across continuations and history.
|
|
620
|
+
if (request.model.route.id === "google-vertex-messages" && previous?.role === "tool")
|
|
621
|
+
return false;
|
|
618
622
|
return (previous !== undefined &&
|
|
619
623
|
previous.role !== "system" &&
|
|
620
624
|
(previous.role === "user" || previous.role === "tool" || endsInServerToolUse(previous)) &&
|
|
@@ -650,7 +654,7 @@ const lowerMessages = Effect.fn("AnthropicMessages.lowerMessages")(function* (re
|
|
|
650
654
|
if (message.role === "system") {
|
|
651
655
|
if (splitsLocalToolResults(request.messages, index))
|
|
652
656
|
return yield* invalid("Anthropic Messages system updates cannot split a local tool call from its tool result");
|
|
653
|
-
if (supportsNativeSystemUpdates(request) && canUseNativeSystemUpdate(request
|
|
657
|
+
if (supportsNativeSystemUpdates(request) && canUseNativeSystemUpdate(request, index)) {
|
|
654
658
|
messages.push(yield* lowerNativeSystemUpdate(message, breakpoints));
|
|
655
659
|
continue;
|
|
656
660
|
}
|
|
@@ -703,26 +703,24 @@ const onOutputItemAdded = (state, event) => {
|
|
|
703
703
|
events,
|
|
704
704
|
];
|
|
705
705
|
}
|
|
706
|
-
if (item?.type !== "function_call" || !item.
|
|
706
|
+
if (item?.type !== "function_call" || !item.call_id)
|
|
707
707
|
return [state, NO_EVENTS];
|
|
708
|
-
const
|
|
708
|
+
const id = item.id ?? item.call_id;
|
|
709
|
+
const metadata = item.id ? providerMetadata(state, { itemId: item.id }) : undefined;
|
|
709
710
|
const events = [];
|
|
710
711
|
const lifecycle = Lifecycle.stepStart(state.lifecycle, events);
|
|
711
712
|
return [
|
|
712
713
|
{
|
|
713
714
|
...state,
|
|
714
715
|
lifecycle,
|
|
715
|
-
tools: ToolStream.start(state.tools,
|
|
716
|
-
id: item.call_id
|
|
716
|
+
tools: ToolStream.start(state.tools, id, {
|
|
717
|
+
id: item.call_id,
|
|
717
718
|
name: item.name ?? "",
|
|
718
719
|
input: item.arguments ?? "",
|
|
719
720
|
providerMetadata: metadata,
|
|
720
721
|
}),
|
|
721
722
|
},
|
|
722
|
-
[
|
|
723
|
-
...events,
|
|
724
|
-
LLMEvent.toolInputStart({ id: item.call_id ?? item.id, name: item.name ?? "", providerMetadata: metadata }),
|
|
725
|
-
],
|
|
723
|
+
[...events, LLMEvent.toolInputStart({ id: item.call_id, name: item.name ?? "", providerMetadata: metadata })],
|
|
726
724
|
];
|
|
727
725
|
};
|
|
728
726
|
const onReasoningSummaryPartAdded = (state, event) => {
|
|
@@ -825,18 +823,19 @@ const onOutputItemDone = Effect.fn("OpenResponses.onOutputItemDone")(function* (
|
|
|
825
823
|
];
|
|
826
824
|
}
|
|
827
825
|
if (item.type === "function_call") {
|
|
828
|
-
if (!item.
|
|
826
|
+
if (!item.call_id || !item.name)
|
|
829
827
|
return [state, NO_EVENTS];
|
|
830
|
-
const
|
|
828
|
+
const id = item.id ?? item.call_id;
|
|
829
|
+
const tools = state.tools[id]
|
|
831
830
|
? state.tools
|
|
832
|
-
: ToolStream.start(state.tools,
|
|
831
|
+
: ToolStream.start(state.tools, id, {
|
|
833
832
|
id: item.call_id,
|
|
834
833
|
name: item.name,
|
|
835
|
-
providerMetadata: providerMetadata(state, { itemId: item.id }),
|
|
834
|
+
providerMetadata: item.id ? providerMetadata(state, { itemId: item.id }) : undefined,
|
|
836
835
|
});
|
|
837
836
|
const result = item.arguments === undefined
|
|
838
|
-
? yield* ToolStream.finish(state.id, tools,
|
|
839
|
-
: yield* ToolStream.finishWithInput(state.id, tools,
|
|
837
|
+
? yield* ToolStream.finish(state.id, tools, id)
|
|
838
|
+
: yield* ToolStream.finishWithInput(state.id, tools, id, item.arguments);
|
|
840
839
|
const events = [];
|
|
841
840
|
const resultEvents = result.events ?? [];
|
|
842
841
|
const lifecycle = resultEvents.length ? Lifecycle.stepStart(state.lifecycle, events) : state.lifecycle;
|
|
@@ -879,9 +878,10 @@ const onOutputItemDone = Effect.fn("OpenResponses.onOutputItemDone")(function* (
|
|
|
879
878
|
const onResponseFinish = Effect.fn("OpenResponses.onResponseFinish")(function* (state, event) {
|
|
880
879
|
const reconciled = event.type === "response.completed"
|
|
881
880
|
? yield* Effect.reduce(event.response?.output ?? [], () => [state, NO_EVENTS], ([current, events], item) => {
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
881
|
+
const id = item.id ?? (item.type === "function_call" ? item.call_id : undefined);
|
|
882
|
+
if (!id ||
|
|
883
|
+
((item.type !== "function_call" || !current.tools[id]) &&
|
|
884
|
+
(item.type !== "reasoning" || !current.reasoningItems[id])))
|
|
885
885
|
return Effect.succeed([current, events]);
|
|
886
886
|
return onOutputItemDone(current, { type: "response.output_item.done", item }).pipe(Effect.map(([next, emitted]) => [next, [...events, ...emitted]]));
|
|
887
887
|
})
|
|
@@ -991,8 +991,9 @@ export const step = (state, input) => {
|
|
|
991
991
|
if (event.type === "response.output_item.added") {
|
|
992
992
|
if (event.item?.type === "message" && !event.item.id)
|
|
993
993
|
return ProviderShared.eventError(state.id, `${event.type} message is missing id`);
|
|
994
|
-
|
|
995
|
-
|
|
994
|
+
const id = event.item?.id ?? (event.item?.type === "function_call" ? event.item.call_id : undefined);
|
|
995
|
+
return Effect.succeed(onOutputItemAdded(event.output_index !== undefined && id
|
|
996
|
+
? { ...state, outputItems: { ...state.outputItems, [event.output_index]: id } }
|
|
996
997
|
: state, event));
|
|
997
998
|
}
|
|
998
999
|
if (event.type === "response.function_call_arguments.delta" || event.type === "response.function_call_arguments.done")
|
|
@@ -241,7 +241,7 @@ const lowerUserMessage = Effect.fn("OpenAIChat.lowerUserMessage")(function* (mes
|
|
|
241
241
|
};
|
|
242
242
|
return { role: "user", content };
|
|
243
243
|
});
|
|
244
|
-
const lowerAssistantMessage = Effect.fn("OpenAIChat.lowerAssistantMessage")(function* (message, configuredField, options
|
|
244
|
+
const lowerAssistantMessage = Effect.fn("OpenAIChat.lowerAssistantMessage")(function* (message, configuredField, requireReasoning, options) {
|
|
245
245
|
const content = [];
|
|
246
246
|
const reasoning = [];
|
|
247
247
|
const toolCalls = [];
|
|
@@ -267,22 +267,22 @@ const lowerAssistantMessage = Effect.fn("OpenAIChat.lowerAssistantMessage")(func
|
|
|
267
267
|
const nativeReasoning = openAICompatibleReasoningContent(message.native?.openaiCompatible);
|
|
268
268
|
const fullyStructured = reasoning.every((part) => Array.isArray(part.providerMetadata?.openai?.reasoningDetails));
|
|
269
269
|
const field = (() => {
|
|
270
|
-
if (configuredField !== undefined)
|
|
270
|
+
if (configuredField !== undefined && (requireReasoning || reasoning.length > 0 || nativeReasoning !== undefined))
|
|
271
271
|
return configuredField;
|
|
272
272
|
if (reasoning.length === 0)
|
|
273
|
-
return undefined;
|
|
273
|
+
return requireReasoning ? "reasoning_content" : undefined;
|
|
274
274
|
if (observedField !== undefined)
|
|
275
275
|
return observedField;
|
|
276
276
|
if (nativeReasoning !== undefined)
|
|
277
277
|
return "reasoning_content";
|
|
278
|
-
if (!fullyStructured)
|
|
278
|
+
if (!fullyStructured || requireReasoning)
|
|
279
279
|
return "reasoning_content";
|
|
280
280
|
})();
|
|
281
281
|
const reasoningText = (() => {
|
|
282
282
|
if (configuredField !== undefined)
|
|
283
|
-
return reasoning.length === 0 ? (nativeReasoning ?? "") : text;
|
|
283
|
+
return reasoning.length === 0 ? (nativeReasoning ?? (requireReasoning ? "" : undefined)) : text;
|
|
284
284
|
if (reasoning.length === 0)
|
|
285
|
-
return nativeReasoning;
|
|
285
|
+
return nativeReasoning ?? (requireReasoning ? "" : undefined);
|
|
286
286
|
return text;
|
|
287
287
|
})();
|
|
288
288
|
const cached = message.content.findLast((part) => "cache" in part && part.cache !== undefined);
|
|
@@ -326,11 +326,11 @@ const lowerToolMessages = Effect.fn("OpenAIChat.lowerToolMessages")(function* (m
|
|
|
326
326
|
}
|
|
327
327
|
return { messages, images };
|
|
328
328
|
});
|
|
329
|
-
const lowerMessage = Effect.fn("OpenAIChat.lowerMessage")(function* (message, reasoningField, options
|
|
329
|
+
const lowerMessage = Effect.fn("OpenAIChat.lowerMessage")(function* (message, reasoningField, requireReasoning, options) {
|
|
330
330
|
if (message.role === "user")
|
|
331
331
|
return [yield* lowerUserMessage(message, options)];
|
|
332
332
|
if (message.role === "assistant")
|
|
333
|
-
return [yield* lowerAssistantMessage(message, reasoningField, options)];
|
|
333
|
+
return [yield* lowerAssistantMessage(message, reasoningField, requireReasoning, options)];
|
|
334
334
|
return (yield* lowerToolMessages(message, options)).messages;
|
|
335
335
|
});
|
|
336
336
|
const lowerMessages = Effect.fn("OpenAIChat.lowerMessages")(function* (request, options) {
|
|
@@ -350,6 +350,12 @@ const lowerMessages = Effect.fn("OpenAIChat.lowerMessages")(function* (request,
|
|
|
350
350
|
: [{ role: "system", content: ProviderShared.joinText(request.system) }];
|
|
351
351
|
const messages = [...system];
|
|
352
352
|
const modelID = request.model.id.toLowerCase();
|
|
353
|
+
const requireReasoning = request.model.compatibility?.requireReasoning ??
|
|
354
|
+
(request.model.compatibility?.reasoningField !== undefined ||
|
|
355
|
+
request.model.provider === "deepseek" ||
|
|
356
|
+
request.model.route.endpoint.baseURL?.toLowerCase().includes("deepseek.com") ||
|
|
357
|
+
modelID.includes("deepseek"));
|
|
358
|
+
const reasoningField = request.model.compatibility?.reasoningField;
|
|
353
359
|
const mistral = ["mistral", "devstral", "codestral", "pixtral", "mixtral"].some((family) => modelID.includes(family));
|
|
354
360
|
const lowering = {
|
|
355
361
|
...options,
|
|
@@ -427,7 +433,7 @@ const lowerMessages = Effect.fn("OpenAIChat.lowerMessages")(function* (request,
|
|
|
427
433
|
continue;
|
|
428
434
|
}
|
|
429
435
|
flushImages();
|
|
430
|
-
messages.push(...(yield* lowerMessage(message,
|
|
436
|
+
messages.push(...(yield* lowerMessage(message, reasoningField, requireReasoning, lowering)));
|
|
431
437
|
}
|
|
432
438
|
flushImages();
|
|
433
439
|
return messages;
|
package/dist/schema/options.d.ts
CHANGED
|
@@ -72,6 +72,8 @@ export type LanguageModelMaxTokensFieldCompatibility = Schema.Schema.Type<typeof
|
|
|
72
72
|
declare const LanguageModelCompatibility_base: Schema.Class<LanguageModelCompatibility, Schema.Struct<{
|
|
73
73
|
readonly toolSchema: Schema.optional<Schema.Literals<readonly ["gemini", "moonshot"]>>;
|
|
74
74
|
readonly reasoningField: Schema.optional<Schema.String>;
|
|
75
|
+
/** Require every assistant message to include its reasoning field, even when empty. */
|
|
76
|
+
readonly requireReasoning: Schema.optional<Schema.Boolean>;
|
|
75
77
|
readonly maxTokensField: Schema.optional<Schema.Literals<readonly ["max_completion_tokens", "max_tokens"]>>;
|
|
76
78
|
readonly requireFinishReason: Schema.optional<Schema.Boolean>;
|
|
77
79
|
readonly requireAssistantAfterTool: Schema.optional<Schema.Boolean>;
|
package/dist/schema/options.js
CHANGED
|
@@ -99,6 +99,8 @@ export const LanguageModelMaxTokensFieldCompatibility = Schema.Literals(["max_co
|
|
|
99
99
|
export class LanguageModelCompatibility extends Schema.Class("LLM.LanguageModelCompatibility")({
|
|
100
100
|
toolSchema: Schema.optional(LanguageModelToolSchemaCompatibility),
|
|
101
101
|
reasoningField: Schema.optional(Schema.String),
|
|
102
|
+
/** Require every assistant message to include its reasoning field, even when empty. */
|
|
103
|
+
requireReasoning: Schema.optional(Schema.Boolean),
|
|
102
104
|
maxTokensField: Schema.optional(LanguageModelMaxTokensFieldCompatibility),
|
|
103
105
|
requireFinishReason: Schema.optional(Schema.Boolean),
|
|
104
106
|
requireAssistantAfterTool: Schema.optional(Schema.Boolean),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package.json",
|
|
3
|
-
"version": "0.0.0-dev-
|
|
3
|
+
"version": "0.0.0-dev-18237",
|
|
4
4
|
"name": "@opencode-ai/ai",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@clack/prompts": "1.0.0-alpha.1",
|
|
32
32
|
"@effect/platform-node": "4.0.0-rc.111",
|
|
33
|
-
"@opencode-ai/http-recorder": "0.0.0-dev-
|
|
33
|
+
"@opencode-ai/http-recorder": "0.0.0-dev-18237",
|
|
34
34
|
"@tsconfig/bun": "1.0.9",
|
|
35
35
|
"@types/bun": "1.3.13",
|
|
36
36
|
"@typescript/native-preview": "7.0.0-dev.20251207.1",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@smithy/eventstream-codec": "4.2.14",
|
|
41
41
|
"@smithy/util-utf8": "4.2.2",
|
|
42
|
-
"@opencode-ai/schema": "0.0.0-dev-
|
|
42
|
+
"@opencode-ai/schema": "0.0.0-dev-18237",
|
|
43
43
|
"aws4fetch": "1.0.20",
|
|
44
44
|
"effect": "4.0.0-rc.111",
|
|
45
45
|
"google-auth-library": "10.5.0"
|