@opencode-ai/ai 0.0.0-dev-18146 → 0.0.0-dev-18149
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/bedrock-converse.js +1 -1
- package/dist/protocols/gemini.js +1 -1
- package/dist/protocols/openai-chat.d.ts +7 -4
- package/dist/protocols/openai-chat.js +52 -28
- package/dist/providers/openrouter.d.ts +2 -0
- package/dist/route/client.js +11 -1
- package/dist/route/protocol.d.ts +2 -2
- package/package.json +3 -3
package/dist/protocols/gemini.js
CHANGED
|
@@ -204,7 +204,7 @@ declare const OpenAIChatBody: Schema.Struct<{
|
|
|
204
204
|
stop: Schema.optional<Schema.$Array<Schema.String>>;
|
|
205
205
|
}>;
|
|
206
206
|
export type OpenAIChatBody = Schema.Schema.Type<typeof OpenAIChatBody>;
|
|
207
|
-
export declare const OpenAIChatEvent: Schema.Struct<{
|
|
207
|
+
export declare const OpenAIChatEvent: Schema.StructWithRest<Schema.Struct<{
|
|
208
208
|
readonly choices: Schema.optional<Schema.NullOr<Schema.$Array<Schema.StructWithRest<Schema.Struct<{
|
|
209
209
|
readonly delta: Schema.optional<Schema.NullOr<Schema.StructWithRest<Schema.Struct<{
|
|
210
210
|
readonly content: Schema.optional<Schema.NullOr<Schema.String>>;
|
|
@@ -257,11 +257,11 @@ export declare const OpenAIChatEvent: Schema.Struct<{
|
|
|
257
257
|
readonly rejected_prediction_tokens: Schema.optional<Schema.NullOr<Schema.Number>>;
|
|
258
258
|
}>, readonly [Schema.$Record<Schema.String, Schema.Unknown>]>>>;
|
|
259
259
|
}>, readonly [Schema.$Record<Schema.String, Schema.Unknown>]>>>;
|
|
260
|
-
readonly error: Schema.optional<Schema.NullOr<Schema.Struct<{
|
|
260
|
+
readonly error: Schema.optional<Schema.NullOr<Schema.StructWithRest<Schema.Struct<{
|
|
261
261
|
readonly code: Schema.optional<Schema.NullOr<Schema.Union<readonly [Schema.String, Schema.Number]>>>;
|
|
262
262
|
readonly message: Schema.String;
|
|
263
|
-
}>>>;
|
|
264
|
-
}>;
|
|
263
|
+
}>, readonly [Schema.$Record<Schema.String, Schema.Unknown>]>>>;
|
|
264
|
+
}>, readonly [Schema.$Record<Schema.String, Schema.Unknown>]>;
|
|
265
265
|
export type OpenAIChatEvent = Schema.Schema.Type<typeof OpenAIChatEvent>;
|
|
266
266
|
interface PendingToolDelta {
|
|
267
267
|
readonly id?: string;
|
|
@@ -281,6 +281,7 @@ export interface ParserState {
|
|
|
281
281
|
readonly reasoningEmitted: boolean;
|
|
282
282
|
readonly latestToolIndex?: number;
|
|
283
283
|
readonly nextToolIndex: number;
|
|
284
|
+
readonly requireFinishReason: boolean;
|
|
284
285
|
}
|
|
285
286
|
interface LoweringOptions {
|
|
286
287
|
readonly cacheControl?: (cache: CacheHint | undefined) => Schema.Schema.Type<typeof OpenAIChatCacheControl> | undefined;
|
|
@@ -585,7 +586,9 @@ export declare const protocol: Protocol<{
|
|
|
585
586
|
readonly frequency_penalty?: number | undefined;
|
|
586
587
|
readonly presence_penalty?: number | undefined;
|
|
587
588
|
}, string, {
|
|
589
|
+
readonly [x: string]: unknown;
|
|
588
590
|
readonly error?: {
|
|
591
|
+
readonly [x: string]: unknown;
|
|
589
592
|
readonly message: string;
|
|
590
593
|
readonly code?: string | number | null | undefined;
|
|
591
594
|
} | null | undefined;
|
|
@@ -5,7 +5,7 @@ import { Auth } from "../route/auth.js";
|
|
|
5
5
|
import { Endpoint } from "../route/endpoint.js";
|
|
6
6
|
import { HttpTransport } from "../route/transport/index.js";
|
|
7
7
|
import { Protocol } from "../route/protocol.js";
|
|
8
|
-
import { AIError, LLMEvent, Usage, } from "../schema/index.js";
|
|
8
|
+
import { AIError, LLMEvent, ProviderInternalReason, UnknownProviderReason, Usage, } from "../schema/index.js";
|
|
9
9
|
import { classifyProviderFailure } from "../provider-error.js";
|
|
10
10
|
import { isRecord, JsonObject, optionalArray, optionalNull, ProviderShared } from "./shared.js";
|
|
11
11
|
import { OpenAIOptions } from "./utils/openai-options.js";
|
|
@@ -167,15 +167,15 @@ const OpenAIChatChoice = Schema.StructWithRest(Schema.Struct({
|
|
|
167
167
|
// Moonshot streams usage on `choice.usage` instead of top-level `usage`.
|
|
168
168
|
usage: optionalNull(OpenAIChatUsage),
|
|
169
169
|
}), [Schema.Record(Schema.String, Schema.Unknown)]);
|
|
170
|
-
const OpenAIChatError = Schema.Struct({
|
|
170
|
+
const OpenAIChatError = Schema.StructWithRest(Schema.Struct({
|
|
171
171
|
code: optionalNull(Schema.Union([Schema.String, Schema.Number])),
|
|
172
172
|
message: Schema.String,
|
|
173
|
-
});
|
|
174
|
-
export const OpenAIChatEvent = Schema.Struct({
|
|
173
|
+
}), [Schema.Record(Schema.String, Schema.Unknown)]);
|
|
174
|
+
export const OpenAIChatEvent = Schema.StructWithRest(Schema.Struct({
|
|
175
175
|
choices: optionalNull(Schema.Array(OpenAIChatChoice)),
|
|
176
176
|
usage: optionalNull(OpenAIChatUsage),
|
|
177
177
|
error: optionalNull(OpenAIChatError),
|
|
178
|
-
});
|
|
178
|
+
}), [Schema.Record(Schema.String, Schema.Unknown)]);
|
|
179
179
|
const lowerTool = (tool, inputSchema, options, supportsStrictMode) => ({
|
|
180
180
|
type: "function",
|
|
181
181
|
function: {
|
|
@@ -577,19 +577,32 @@ export const fromRequest = Effect.fn("OpenAIChat.fromRequest")(function* (reques
|
|
|
577
577
|
// Streaming parsers are small state machines: every event returns a new state
|
|
578
578
|
// plus the common `LLMEvent`s produced by that event. Tool calls are accumulated
|
|
579
579
|
// because OpenAI streams JSON arguments across multiple deltas.
|
|
580
|
-
const
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
580
|
+
const finishReasonError = (event, reason) => new AIError({
|
|
581
|
+
module: ADAPTER,
|
|
582
|
+
method: "stream",
|
|
583
|
+
body: ProviderShared.encodeJson(event),
|
|
584
|
+
reason,
|
|
585
|
+
});
|
|
586
|
+
const mapFinishReason = Effect.fn("OpenAIChat.mapFinishReason")(function* (event, reason) {
|
|
587
|
+
switch (reason) {
|
|
588
|
+
case "error":
|
|
589
|
+
return yield* finishReasonError(event, new UnknownProviderReason({ message: "Provider reported an error (finish_reason: error)" }));
|
|
590
|
+
case "network_error":
|
|
591
|
+
return yield* finishReasonError(event, new ProviderInternalReason({ message: "Provider reported a network error (finish_reason: network_error)" }));
|
|
592
|
+
case "stop":
|
|
593
|
+
case "end":
|
|
594
|
+
return "stop";
|
|
595
|
+
case "length":
|
|
596
|
+
return "length";
|
|
597
|
+
case "content_filter":
|
|
598
|
+
return "content-filter";
|
|
599
|
+
case "function_call":
|
|
600
|
+
case "tool_calls":
|
|
601
|
+
return "tool-calls";
|
|
602
|
+
default:
|
|
603
|
+
return "unknown";
|
|
604
|
+
}
|
|
605
|
+
});
|
|
593
606
|
// OpenAI Chat reports `prompt_tokens` (inclusive total) with a
|
|
594
607
|
// cached-read and cache-write subsets, and `completion_tokens` (inclusive
|
|
595
608
|
// total) with a `reasoning_tokens` subset. We pass the inclusive totals
|
|
@@ -686,16 +699,20 @@ const reasoningMetadata = (field, details) => ({
|
|
|
686
699
|
},
|
|
687
700
|
});
|
|
688
701
|
const step = (state, event) => Effect.gen(function* () {
|
|
689
|
-
if (event.error)
|
|
702
|
+
if (event.error) {
|
|
703
|
+
const body = ProviderShared.encodeJson(event);
|
|
690
704
|
return yield* new AIError({
|
|
691
705
|
module: ADAPTER,
|
|
692
706
|
method: "stream",
|
|
707
|
+
body,
|
|
693
708
|
reason: classifyProviderFailure({
|
|
694
709
|
message: event.error.message,
|
|
695
710
|
code: event.error.code === undefined || event.error.code === null ? undefined : String(event.error.code),
|
|
696
711
|
status: typeof event.error.code === "number" ? event.error.code : undefined,
|
|
712
|
+
rawBody: body,
|
|
697
713
|
}),
|
|
698
714
|
});
|
|
715
|
+
}
|
|
699
716
|
const events = [];
|
|
700
717
|
const choice = event.choices?.[0];
|
|
701
718
|
// Moonshot (and a few other OpenAI-compatible providers) attach usage to
|
|
@@ -703,8 +720,11 @@ const step = (state, event) => Effect.gen(function* () {
|
|
|
703
720
|
const choiceUsage = choice?.usage;
|
|
704
721
|
const usage = mapUsage(event.usage) ?? (choiceUsage ? mapUsage(choiceUsage) : undefined) ?? state.usage;
|
|
705
722
|
const rawFinishReason = choice?.finish_reason;
|
|
706
|
-
const finishReason = rawFinishReason
|
|
707
|
-
? {
|
|
723
|
+
const finishReason = rawFinishReason
|
|
724
|
+
? {
|
|
725
|
+
normalized: yield* mapFinishReason(event, rawFinishReason),
|
|
726
|
+
raw: choice?.native_finish_reason ?? rawFinishReason,
|
|
727
|
+
}
|
|
708
728
|
: state.finishReason;
|
|
709
729
|
const delta = choice?.delta;
|
|
710
730
|
const toolDeltas = delta?.tool_calls ?? [];
|
|
@@ -721,7 +741,7 @@ const step = (state, event) => Effect.gen(function* () {
|
|
|
721
741
|
toolDeltas.some((tool) => Boolean(tool.id) || Boolean(tool.function?.name) || Boolean(tool.function?.arguments));
|
|
722
742
|
if (state.finishReason !== undefined) {
|
|
723
743
|
if (hasLateContent)
|
|
724
|
-
return yield* ProviderShared.eventError(ADAPTER, "OpenAI Chat received content after the finish reason");
|
|
744
|
+
return yield* ProviderShared.eventError(ADAPTER, "OpenAI Chat received content after the finish reason", ProviderShared.encodeJson(event));
|
|
725
745
|
return [{ ...state, usage }, events];
|
|
726
746
|
}
|
|
727
747
|
const reasoningField = state.reasoningField ?? reasoning?.field;
|
|
@@ -773,14 +793,14 @@ const step = (state, event) => Effect.gen(function* () {
|
|
|
773
793
|
}
|
|
774
794
|
const result = ToolStream.appendOrStart(ADAPTER, tools, index, { id: id || undefined, name: name || undefined, text }, "OpenAI Chat tool call delta is missing id or name");
|
|
775
795
|
if (ToolStream.isError(result))
|
|
776
|
-
return yield* result;
|
|
796
|
+
return yield* ProviderShared.eventError(ADAPTER, result.reason.message, ProviderShared.encodeJson(event));
|
|
777
797
|
tools = result.tools;
|
|
778
798
|
if (result.events.length)
|
|
779
799
|
lifecycle = Lifecycle.stepStart(lifecycle, events);
|
|
780
800
|
events.push(...result.events);
|
|
781
801
|
}
|
|
782
802
|
if (finishReason !== undefined && state.finishReason === undefined && Object.keys(pendingTools).length > 0)
|
|
783
|
-
return yield* ProviderShared.eventError(ADAPTER, "OpenAI Chat tool call delta is missing id or name");
|
|
803
|
+
return yield* ProviderShared.eventError(ADAPTER, "OpenAI Chat tool call delta is missing id or name", ProviderShared.encodeJson(event));
|
|
784
804
|
// Finalize accumulated tool inputs eagerly when finish_reason arrives so
|
|
785
805
|
// valid calls and malformed local calls settle independently.
|
|
786
806
|
const finished = finishReason !== undefined && state.finishReason === undefined && Object.keys(tools).length > 0
|
|
@@ -800,14 +820,17 @@ const step = (state, event) => Effect.gen(function* () {
|
|
|
800
820
|
reasoningEmitted,
|
|
801
821
|
latestToolIndex,
|
|
802
822
|
nextToolIndex,
|
|
823
|
+
requireFinishReason: state.requireFinishReason,
|
|
803
824
|
},
|
|
804
825
|
events,
|
|
805
826
|
];
|
|
806
827
|
});
|
|
807
|
-
const finishEvents = (state)
|
|
828
|
+
const finishEvents = Effect.fn("OpenAIChat.finishEvents")(function* (state) {
|
|
829
|
+
if (state.finishReason === undefined && state.requireFinishReason)
|
|
830
|
+
return yield* ProviderShared.eventError(ADAPTER, "OpenAI Chat stream ended without finish_reason");
|
|
808
831
|
const events = [];
|
|
809
832
|
const toolCallEvents = state.finishReason === undefined && Object.keys(state.tools).length > 0
|
|
810
|
-
?
|
|
833
|
+
? (yield* ToolStream.finishAll(ADAPTER, state.tools)).events
|
|
811
834
|
: state.toolCallEvents;
|
|
812
835
|
const hasToolCalls = toolCallEvents.length > 0;
|
|
813
836
|
const reason = state.finishReason
|
|
@@ -815,7 +838,7 @@ const finishEvents = (state) => {
|
|
|
815
838
|
...state.finishReason,
|
|
816
839
|
normalized: state.finishReason.normalized === "stop" && hasToolCalls ? "tool-calls" : state.finishReason.normalized,
|
|
817
840
|
}
|
|
818
|
-
: { normalized: hasToolCalls ? "tool-calls" : "
|
|
841
|
+
: { normalized: hasToolCalls ? "tool-calls" : "stop" };
|
|
819
842
|
const metadata = reasoningMetadata(state.reasoningField, state.reasoningDetailsObserved ? state.reasoningDetails : undefined);
|
|
820
843
|
const started = state.reasoningDetailsObserved && !state.reasoningEmitted
|
|
821
844
|
? Lifecycle.reasoningStart(state.lifecycle, events, "reasoning-0", reasoningMetadata(state.reasoningField))
|
|
@@ -825,7 +848,7 @@ const finishEvents = (state) => {
|
|
|
825
848
|
events.push(...toolCallEvents);
|
|
826
849
|
Lifecycle.finish(lifecycle, events, { reason, usage: state.usage });
|
|
827
850
|
return events;
|
|
828
|
-
};
|
|
851
|
+
});
|
|
829
852
|
// =============================================================================
|
|
830
853
|
// Protocol And OpenAI Route
|
|
831
854
|
// =============================================================================
|
|
@@ -853,6 +876,7 @@ export const protocol = Protocol.make({
|
|
|
853
876
|
reasoningDetailsObserved: false,
|
|
854
877
|
reasoningEmitted: false,
|
|
855
878
|
nextToolIndex: 0,
|
|
879
|
+
requireFinishReason: request.model.compatibility?.requireFinishReason ?? true,
|
|
856
880
|
}),
|
|
857
881
|
step,
|
|
858
882
|
onHalt: finishEvents,
|
|
@@ -283,7 +283,9 @@ export declare const protocol: Protocol<{
|
|
|
283
283
|
readonly frequency_penalty?: number | undefined;
|
|
284
284
|
readonly presence_penalty?: number | undefined;
|
|
285
285
|
}, string, {
|
|
286
|
+
readonly [x: string]: unknown;
|
|
286
287
|
readonly error?: {
|
|
288
|
+
readonly [x: string]: unknown;
|
|
287
289
|
readonly message: string;
|
|
288
290
|
readonly code?: string | number | null | undefined;
|
|
289
291
|
} | null | undefined;
|
package/dist/route/client.js
CHANGED
|
@@ -123,7 +123,17 @@ function makeFromTransport(input) {
|
|
|
123
123
|
const route = `${request.model.provider}/${request.model.route.id}`;
|
|
124
124
|
return Stream.unwrap(routeInput.transport.execute(prepared, request, runtime, options).pipe(Effect.map((execution) => {
|
|
125
125
|
const events = execution.frames.pipe(Stream.mapEffect(decodeEvent(route)), protocol.stream.terminal ? Stream.takeUntil(protocol.stream.terminal) : (stream) => stream);
|
|
126
|
-
const stream =
|
|
126
|
+
const stream = Stream.suspend(() => {
|
|
127
|
+
let state = protocol.stream.initial(request);
|
|
128
|
+
const parsed = events.pipe(Stream.mapEffect((event) => protocol.stream.step(state, event).pipe(Effect.map(([next, output]) => {
|
|
129
|
+
state = next;
|
|
130
|
+
return output;
|
|
131
|
+
}))), Stream.flatMap(Stream.fromIterable));
|
|
132
|
+
const onHalt = protocol.stream.onHalt;
|
|
133
|
+
return onHalt
|
|
134
|
+
? parsed.pipe(Stream.concat(Stream.suspend(() => Stream.unwrap(onHalt(state).pipe(Effect.map(Stream.fromIterable))))))
|
|
135
|
+
: parsed;
|
|
136
|
+
}).pipe(Stream.catchCause((cause) => Stream.fail(streamError(route, `Failed to read ${route} stream`, cause))), requireTerminalEvent(route));
|
|
127
137
|
return execution.complete ? stream.pipe(Stream.onEnd(execution.complete)) : stream;
|
|
128
138
|
})));
|
|
129
139
|
},
|
package/dist/route/protocol.d.ts
CHANGED
|
@@ -56,8 +56,8 @@ export interface ProtocolStream<Frame, Event, State> {
|
|
|
56
56
|
readonly step: (state: State, event: Event) => Effect.Effect<readonly [State, ReadonlyArray<LLMEvent>], AIError>;
|
|
57
57
|
/** Optional request-completion signal for transports that do not end naturally. */
|
|
58
58
|
readonly terminal?: (event: Event) => boolean;
|
|
59
|
-
/** Optional flush emitted when the framed stream ends. */
|
|
60
|
-
readonly onHalt?: (state: State) => ReadonlyArray<LLMEvent>;
|
|
59
|
+
/** Optional effectful flush emitted when the framed stream ends. */
|
|
60
|
+
readonly onHalt?: (state: State) => Effect.Effect<ReadonlyArray<LLMEvent>, AIError>;
|
|
61
61
|
}
|
|
62
62
|
/**
|
|
63
63
|
* Construct a `Protocol` from its body and stream pieces:
|
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-18149",
|
|
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-18149",
|
|
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-18149",
|
|
43
43
|
"aws4fetch": "1.0.20",
|
|
44
44
|
"effect": "4.0.0-rc.111",
|
|
45
45
|
"google-auth-library": "10.5.0"
|