@opencode/ai 2.0.14 → 2.0.16
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/README.md +399 -56
- package/dist/experimental/evaluation-client.d.ts +1 -1
- package/dist/experimental/evaluation-client.js +39 -3
- package/dist/experimental/evaluation.d.ts +4 -4
- package/dist/experimental/evaluation.js +2 -2
- package/dist/experimental/system-one.d.ts +3 -3
- package/dist/experimental/system-one.js +40 -51
- package/dist/generation.d.ts +83 -0
- package/dist/generation.js +113 -0
- package/dist/image-client.d.ts +16 -7
- package/dist/image-client.js +29 -13
- package/dist/image.d.ts +1410 -81
- package/dist/image.js +97 -67
- package/dist/index.d.ts +17 -2
- package/dist/index.js +12 -1
- package/dist/llm.d.ts +9 -1
- package/dist/media-model.d.ts +44 -0
- package/dist/media-model.js +49 -0
- package/dist/media.d.ts +213 -0
- package/dist/media.js +227 -0
- package/dist/promise.d.ts +974 -0
- package/dist/promise.js +81 -0
- package/dist/protocols/alibaba-chat.d.ts +12 -0
- package/dist/protocols/alibaba-responses.d.ts +2 -2
- package/dist/protocols/anthropic-messages.js +8 -19
- package/dist/protocols/assemblyai-transcription.d.ts +40 -0
- package/dist/protocols/assemblyai-transcription.js +138 -0
- package/dist/protocols/bedrock-converse.d.ts +4 -4
- package/dist/protocols/bedrock-converse.js +6 -17
- package/dist/protocols/bfl-images.d.ts +32 -0
- package/dist/protocols/bfl-images.js +153 -0
- package/dist/protocols/cartesia-speech.d.ts +127 -0
- package/dist/protocols/cartesia-speech.js +126 -0
- package/dist/protocols/deepgram-speech.d.ts +119 -0
- package/dist/protocols/deepgram-speech.js +92 -0
- package/dist/protocols/deepgram-transcription.d.ts +25 -0
- package/dist/protocols/deepgram-transcription.js +129 -0
- package/dist/protocols/elevenlabs-speech.d.ts +122 -0
- package/dist/protocols/elevenlabs-speech.js +115 -0
- package/dist/protocols/fal-images.d.ts +24 -0
- package/dist/protocols/fal-images.js +114 -0
- package/dist/protocols/fal-video.d.ts +29 -0
- package/dist/protocols/fal-video.js +88 -0
- package/dist/protocols/gemini.d.ts +30 -9
- package/dist/protocols/gemini.js +45 -35
- package/dist/protocols/google-images.d.ts +9 -21
- package/dist/protocols/google-images.js +158 -133
- package/dist/protocols/google-speech.d.ts +130 -0
- package/dist/protocols/google-speech.js +84 -0
- package/dist/protocols/google-transcription.d.ts +173 -0
- package/dist/protocols/google-transcription.js +138 -0
- package/dist/protocols/google-video.d.ts +26 -0
- package/dist/protocols/google-video.js +158 -0
- package/dist/protocols/meta-images.d.ts +7 -12
- package/dist/protocols/meta-images.js +85 -66
- package/dist/protocols/meta-responses.d.ts +4 -4
- package/dist/protocols/meta-responses.js +1 -1
- package/dist/protocols/mistral-chat.js +7 -6
- package/dist/protocols/open-responses.d.ts +17 -9
- package/dist/protocols/open-responses.js +24 -14
- package/dist/protocols/openai-chat.d.ts +118 -1
- package/dist/protocols/openai-chat.js +125 -44
- package/dist/protocols/openai-compatible-chat.d.ts +12 -0
- package/dist/protocols/openai-compatible-responses.d.ts +2 -2
- package/dist/protocols/openai-images.d.ts +128 -18
- package/dist/protocols/openai-images.js +177 -154
- package/dist/protocols/openai-responses.d.ts +15 -15
- package/dist/protocols/openai-responses.js +5 -6
- package/dist/protocols/openai-speech.d.ts +116 -0
- package/dist/protocols/openai-speech.js +98 -0
- package/dist/protocols/openai-transcription.d.ts +207 -0
- package/dist/protocols/openai-transcription.js +190 -0
- package/dist/protocols/replicate-images.d.ts +28 -0
- package/dist/protocols/replicate-images.js +133 -0
- package/dist/protocols/runway-video.d.ts +38 -0
- package/dist/protocols/runway-video.js +146 -0
- package/dist/protocols/shared.d.ts +27 -17
- package/dist/protocols/shared.js +52 -35
- package/dist/protocols/stability-images.d.ts +38 -0
- package/dist/protocols/stability-images.js +148 -0
- package/dist/protocols/utils/bedrock-media.d.ts +2 -3
- package/dist/protocols/utils/bedrock-media.js +4 -4
- package/dist/protocols/utils/fal-queue.d.ts +28 -0
- package/dist/protocols/utils/fal-queue.js +69 -0
- package/dist/protocols/utils/gemini-generate-content.d.ts +65 -0
- package/dist/protocols/utils/gemini-generate-content.js +65 -0
- package/dist/protocols/utils/gemini-json-schema.d.ts +3 -0
- package/dist/protocols/utils/gemini-json-schema.js +76 -0
- package/dist/protocols/utils/media-input.d.ts +18 -0
- package/dist/protocols/utils/media-input.js +35 -0
- package/dist/protocols/utils/responses-compaction.js +6 -5
- package/dist/protocols/utils/speech-stream.d.ts +49 -0
- package/dist/protocols/utils/speech-stream.js +67 -0
- package/dist/protocols/utils/tool-schema.d.ts +2 -2
- package/dist/protocols/utils/tool-schema.js +40 -17
- package/dist/protocols/utils/tool-stream.d.ts +27 -3
- package/dist/protocols/xai-images.d.ts +9 -15
- package/dist/protocols/xai-images.js +75 -84
- package/dist/protocols/xai-responses.d.ts +2 -2
- package/dist/protocols/xai-video.d.ts +34 -0
- package/dist/protocols/xai-video.js +147 -0
- package/dist/protocols/zai-chat.d.ts +13 -1
- package/dist/protocols/zai-images.d.ts +9 -13
- package/dist/protocols/zai-images.js +59 -57
- package/dist/provider-error.js +3 -0
- package/dist/providers/alibaba.d.ts +14 -2
- package/dist/providers/amazon-bedrock-mantle.d.ts +14 -2
- package/dist/providers/amazon-bedrock.d.ts +2 -2
- package/dist/providers/assemblyai.d.ts +25 -0
- package/dist/providers/assemblyai.js +29 -0
- package/dist/providers/azure.d.ts +18 -6
- package/dist/providers/baseten.d.ts +24 -0
- package/dist/providers/black-forest-labs.d.ts +25 -0
- package/dist/providers/black-forest-labs.js +28 -0
- package/dist/providers/cartesia.d.ts +24 -0
- package/dist/providers/cartesia.js +22 -0
- package/dist/providers/cerebras.d.ts +24 -0
- package/dist/providers/cerebras.js +6 -1
- package/dist/providers/cloudflare-ai-gateway.d.ts +30 -6
- package/dist/providers/cloudflare-workers-ai.d.ts +24 -0
- package/dist/providers/deepgram.d.ts +29 -0
- package/dist/providers/deepgram.js +31 -0
- package/dist/providers/deepinfra.d.ts +24 -0
- package/dist/providers/deepinfra.js +6 -1
- package/dist/providers/deepseek.d.ts +24 -0
- package/dist/providers/elevenlabs.d.ts +24 -0
- package/dist/providers/elevenlabs.js +28 -0
- package/dist/providers/fal.d.ts +29 -0
- package/dist/providers/fal.js +33 -0
- package/dist/providers/fireworks.d.ts +24 -0
- package/dist/providers/google-vertex-chat.d.ts +12 -0
- package/dist/providers/google-vertex-responses.d.ts +2 -2
- package/dist/providers/google-vertex.d.ts +10 -3
- package/dist/providers/google.d.ts +25 -3
- package/dist/providers/google.js +11 -2
- package/dist/providers/groq.d.ts +24 -0
- package/dist/providers/index.d.ts +10 -0
- package/dist/providers/index.js +10 -0
- package/dist/providers/meta.d.ts +14 -2
- package/dist/providers/minimax.d.ts +14 -2
- package/dist/providers/moonshot.d.ts +14 -2
- package/dist/providers/moonshot.js +3 -3
- package/dist/providers/openai-compatible-responses.d.ts +2 -2
- package/dist/providers/openai-compatible.d.ts +12 -0
- package/dist/providers/openai.d.ts +25 -3
- package/dist/providers/openai.js +10 -1
- package/dist/providers/openrouter.d.ts +67 -0
- package/dist/providers/openrouter.js +13 -1
- package/dist/providers/replicate.d.ts +25 -0
- package/dist/providers/replicate.js +22 -0
- package/dist/providers/runway.d.ts +24 -0
- package/dist/providers/runway.js +22 -0
- package/dist/providers/stability.d.ts +28 -0
- package/dist/providers/stability.js +23 -0
- package/dist/providers/togetherai.d.ts +24 -0
- package/dist/providers/vercel-ai-gateway.d.ts +41 -0
- package/dist/providers/vercel-ai-gateway.js +85 -0
- package/dist/providers/xai.d.ts +17 -0
- package/dist/providers/xai.js +5 -2
- package/dist/providers/zai-coding-plan.d.ts +15 -3
- package/dist/providers/zai.d.ts +13 -1
- package/dist/route/auth.d.ts +4 -1
- package/dist/route/auth.js +6 -0
- package/dist/route/client.d.ts +9 -1
- package/dist/route/endpoint.d.ts +10 -10
- package/dist/route/executor-service.d.ts +12 -0
- package/dist/route/executor-service.js +3 -0
- package/dist/route/executor.d.ts +4 -9
- package/dist/route/executor.js +3 -3
- package/dist/route/framing.d.ts +5 -1
- package/dist/route/framing.js +9 -0
- package/dist/route/index.d.ts +2 -0
- package/dist/route/index.js +2 -0
- package/dist/route/media-protocol.d.ts +158 -0
- package/dist/route/media-protocol.js +96 -0
- package/dist/route/media.d.ts +97 -0
- package/dist/route/media.js +236 -0
- package/dist/schema/errors.d.ts +13 -3
- package/dist/schema/errors.js +7 -0
- package/dist/schema/events.d.ts +557 -40
- package/dist/schema/events.js +35 -2
- package/dist/schema/messages.d.ts +95 -8
- package/dist/schema/messages.js +8 -6
- package/dist/schema/options.d.ts +6 -3
- package/dist/schema/options.js +6 -2
- package/dist/speech-client.d.ts +21 -0
- package/dist/speech-client.js +25 -0
- package/dist/speech.d.ts +1150 -0
- package/dist/speech.js +119 -0
- package/dist/testing.d.ts +72 -8
- package/dist/transcription-client.d.ts +28 -0
- package/dist/transcription-client.js +44 -0
- package/dist/transcription.d.ts +1504 -0
- package/dist/transcription.js +133 -0
- package/dist/utils/bytes.d.ts +1 -0
- package/dist/utils/bytes.js +10 -0
- package/dist/utils/media-type.d.ts +7 -0
- package/dist/utils/media-type.js +70 -0
- package/dist/utils/sanitize.js +3 -1
- package/dist/video-client.d.ts +28 -0
- package/dist/video-client.js +40 -0
- package/dist/video.d.ts +1359 -0
- package/dist/video.js +119 -0
- package/package.json +7 -3
- package/dist/protocols/utils/gemini-tool-schema.d.ts +0 -2
- package/dist/protocols/utils/gemini-tool-schema.js +0 -103
- package/dist/protocols/utils/image-input.d.ts +0 -21
- package/dist/protocols/utils/image-input.js +0 -20
|
@@ -12,6 +12,37 @@ declare const OpenAIChatCacheControl: Schema.Struct<{
|
|
|
12
12
|
readonly type: Schema.Literal<"ephemeral">;
|
|
13
13
|
readonly ttl: Schema.optional<Schema.String>;
|
|
14
14
|
}>;
|
|
15
|
+
declare const ReasoningDetail: Schema.Union<readonly [Schema.StructWithRest<Schema.Struct<{
|
|
16
|
+
readonly id: Schema.optional<Schema.NullOr<Schema.String>>;
|
|
17
|
+
readonly format: Schema.optional<Schema.String>;
|
|
18
|
+
readonly index: Schema.optional<Schema.Number>;
|
|
19
|
+
readonly signature: Schema.optional<Schema.NullOr<Schema.String>>;
|
|
20
|
+
readonly type: Schema.Literal<"reasoning.text">;
|
|
21
|
+
readonly text: Schema.optional<Schema.String>;
|
|
22
|
+
}>, readonly [Schema.$Record<Schema.String, Schema.Unknown>]>, Schema.StructWithRest<Schema.Struct<{
|
|
23
|
+
readonly id: Schema.optional<Schema.NullOr<Schema.String>>;
|
|
24
|
+
readonly format: Schema.optional<Schema.String>;
|
|
25
|
+
readonly index: Schema.optional<Schema.Number>;
|
|
26
|
+
readonly signature: Schema.optional<Schema.NullOr<Schema.String>>;
|
|
27
|
+
readonly type: Schema.Literal<"reasoning.summary">;
|
|
28
|
+
readonly summary: Schema.optional<Schema.String>;
|
|
29
|
+
}>, readonly [Schema.$Record<Schema.String, Schema.Unknown>]>, Schema.StructWithRest<Schema.Struct<{
|
|
30
|
+
readonly id: Schema.optional<Schema.NullOr<Schema.String>>;
|
|
31
|
+
readonly format: Schema.optional<Schema.String>;
|
|
32
|
+
readonly index: Schema.optional<Schema.Number>;
|
|
33
|
+
readonly signature: Schema.optional<Schema.NullOr<Schema.String>>;
|
|
34
|
+
readonly type: Schema.Literal<"reasoning.encrypted">;
|
|
35
|
+
readonly data: Schema.String;
|
|
36
|
+
}>, readonly [Schema.$Record<Schema.String, Schema.Unknown>]>, Schema.Struct<{
|
|
37
|
+
readonly type: Schema.Literal<"summary">;
|
|
38
|
+
readonly summary: Schema.String;
|
|
39
|
+
readonly index: Schema.optional<Schema.Number>;
|
|
40
|
+
}>, Schema.Struct<{
|
|
41
|
+
readonly type: Schema.Literal<"encrypted">;
|
|
42
|
+
readonly encrypted: Schema.String;
|
|
43
|
+
readonly index: Schema.optional<Schema.Number>;
|
|
44
|
+
}>]>;
|
|
45
|
+
type ReasoningDetail = Schema.Schema.Type<typeof ReasoningDetail>;
|
|
15
46
|
export declare const bodyFields: {
|
|
16
47
|
model: Schema.String;
|
|
17
48
|
messages: Schema.$Array<Schema.toTaggedUnion<"role", readonly [Schema.Struct<{
|
|
@@ -28,6 +59,12 @@ export declare const bodyFields: {
|
|
|
28
59
|
readonly image_url: Schema.Struct<{
|
|
29
60
|
readonly url: Schema.String;
|
|
30
61
|
}>;
|
|
62
|
+
}>, Schema.Struct<{
|
|
63
|
+
readonly type: Schema.Literal<"file">;
|
|
64
|
+
readonly file: Schema.Struct<{
|
|
65
|
+
readonly filename: Schema.String;
|
|
66
|
+
readonly file_data: Schema.String;
|
|
67
|
+
}>;
|
|
31
68
|
}>]>>]>;
|
|
32
69
|
}>, Schema.Struct<{
|
|
33
70
|
readonly role: Schema.Literal<"user">;
|
|
@@ -43,6 +80,12 @@ export declare const bodyFields: {
|
|
|
43
80
|
readonly image_url: Schema.Struct<{
|
|
44
81
|
readonly url: Schema.String;
|
|
45
82
|
}>;
|
|
83
|
+
}>, Schema.Struct<{
|
|
84
|
+
readonly type: Schema.Literal<"file">;
|
|
85
|
+
readonly file: Schema.Struct<{
|
|
86
|
+
readonly filename: Schema.String;
|
|
87
|
+
readonly file_data: Schema.String;
|
|
88
|
+
}>;
|
|
46
89
|
}>]>>]>;
|
|
47
90
|
}>, Schema.StructWithRest<Schema.Struct<{
|
|
48
91
|
readonly role: Schema.Literal<"assistant">;
|
|
@@ -124,6 +167,12 @@ declare const OpenAIChatBody: Schema.Struct<{
|
|
|
124
167
|
readonly image_url: Schema.Struct<{
|
|
125
168
|
readonly url: Schema.String;
|
|
126
169
|
}>;
|
|
170
|
+
}>, Schema.Struct<{
|
|
171
|
+
readonly type: Schema.Literal<"file">;
|
|
172
|
+
readonly file: Schema.Struct<{
|
|
173
|
+
readonly filename: Schema.String;
|
|
174
|
+
readonly file_data: Schema.String;
|
|
175
|
+
}>;
|
|
127
176
|
}>]>>]>;
|
|
128
177
|
}>, Schema.Struct<{
|
|
129
178
|
readonly role: Schema.Literal<"user">;
|
|
@@ -139,6 +188,12 @@ declare const OpenAIChatBody: Schema.Struct<{
|
|
|
139
188
|
readonly image_url: Schema.Struct<{
|
|
140
189
|
readonly url: Schema.String;
|
|
141
190
|
}>;
|
|
191
|
+
}>, Schema.Struct<{
|
|
192
|
+
readonly type: Schema.Literal<"file">;
|
|
193
|
+
readonly file: Schema.Struct<{
|
|
194
|
+
readonly filename: Schema.String;
|
|
195
|
+
readonly file_data: Schema.String;
|
|
196
|
+
}>;
|
|
142
197
|
}>]>>]>;
|
|
143
198
|
}>, Schema.StructWithRest<Schema.Struct<{
|
|
144
199
|
readonly role: Schema.Literal<"assistant">;
|
|
@@ -278,7 +333,9 @@ export interface ParserState {
|
|
|
278
333
|
readonly finishReason?: FinishReasonDetails;
|
|
279
334
|
readonly lifecycle: Lifecycle.State;
|
|
280
335
|
readonly reasoningField?: string;
|
|
281
|
-
|
|
336
|
+
/** A scalar reasoning field (`reasoning_content`, ...) has carried text in this stream. */
|
|
337
|
+
readonly reasoningTextObserved: boolean;
|
|
338
|
+
readonly reasoningDetails: Array<ReasoningDetail>;
|
|
282
339
|
readonly reasoningDetailsObserved: boolean;
|
|
283
340
|
readonly reasoningEmitted: boolean;
|
|
284
341
|
readonly latestToolIndex?: number;
|
|
@@ -321,6 +378,12 @@ export declare const fromRequest: (request: LLMRequest<import("../schema/options
|
|
|
321
378
|
readonly image_url: {
|
|
322
379
|
readonly url: string;
|
|
323
380
|
};
|
|
381
|
+
} | {
|
|
382
|
+
readonly type: "file";
|
|
383
|
+
readonly file: {
|
|
384
|
+
readonly filename: string;
|
|
385
|
+
readonly file_data: string;
|
|
386
|
+
};
|
|
324
387
|
})[];
|
|
325
388
|
} | {
|
|
326
389
|
readonly role: "user";
|
|
@@ -336,6 +399,12 @@ export declare const fromRequest: (request: LLMRequest<import("../schema/options
|
|
|
336
399
|
readonly image_url: {
|
|
337
400
|
readonly url: string;
|
|
338
401
|
};
|
|
402
|
+
} | {
|
|
403
|
+
readonly type: "file";
|
|
404
|
+
readonly file: {
|
|
405
|
+
readonly filename: string;
|
|
406
|
+
readonly file_data: string;
|
|
407
|
+
};
|
|
339
408
|
})[];
|
|
340
409
|
} | {
|
|
341
410
|
readonly [x: string]: unknown;
|
|
@@ -418,6 +487,12 @@ export declare const fromRequest: (request: LLMRequest<import("../schema/options
|
|
|
418
487
|
readonly image_url: {
|
|
419
488
|
readonly url: string;
|
|
420
489
|
};
|
|
490
|
+
} | {
|
|
491
|
+
readonly type: "file";
|
|
492
|
+
readonly file: {
|
|
493
|
+
readonly filename: string;
|
|
494
|
+
readonly file_data: string;
|
|
495
|
+
};
|
|
421
496
|
})[];
|
|
422
497
|
} | {
|
|
423
498
|
readonly role: "user";
|
|
@@ -433,6 +508,12 @@ export declare const fromRequest: (request: LLMRequest<import("../schema/options
|
|
|
433
508
|
readonly image_url: {
|
|
434
509
|
readonly url: string;
|
|
435
510
|
};
|
|
511
|
+
} | {
|
|
512
|
+
readonly type: "file";
|
|
513
|
+
readonly file: {
|
|
514
|
+
readonly filename: string;
|
|
515
|
+
readonly file_data: string;
|
|
516
|
+
};
|
|
436
517
|
})[];
|
|
437
518
|
} | {
|
|
438
519
|
readonly [x: string]: unknown;
|
|
@@ -508,6 +589,12 @@ export declare const protocol: Protocol<{
|
|
|
508
589
|
readonly image_url: {
|
|
509
590
|
readonly url: string;
|
|
510
591
|
};
|
|
592
|
+
} | {
|
|
593
|
+
readonly type: "file";
|
|
594
|
+
readonly file: {
|
|
595
|
+
readonly filename: string;
|
|
596
|
+
readonly file_data: string;
|
|
597
|
+
};
|
|
511
598
|
})[];
|
|
512
599
|
} | {
|
|
513
600
|
readonly role: "user";
|
|
@@ -523,6 +610,12 @@ export declare const protocol: Protocol<{
|
|
|
523
610
|
readonly image_url: {
|
|
524
611
|
readonly url: string;
|
|
525
612
|
};
|
|
613
|
+
} | {
|
|
614
|
+
readonly type: "file";
|
|
615
|
+
readonly file: {
|
|
616
|
+
readonly filename: string;
|
|
617
|
+
readonly file_data: string;
|
|
618
|
+
};
|
|
526
619
|
})[];
|
|
527
620
|
} | {
|
|
528
621
|
readonly [x: string]: unknown;
|
|
@@ -675,6 +768,12 @@ export declare const httpTransport: HttpTransport.HttpJsonTransport<{
|
|
|
675
768
|
readonly image_url: {
|
|
676
769
|
readonly url: string;
|
|
677
770
|
};
|
|
771
|
+
} | {
|
|
772
|
+
readonly type: "file";
|
|
773
|
+
readonly file: {
|
|
774
|
+
readonly filename: string;
|
|
775
|
+
readonly file_data: string;
|
|
776
|
+
};
|
|
678
777
|
})[];
|
|
679
778
|
} | {
|
|
680
779
|
readonly role: "user";
|
|
@@ -690,6 +789,12 @@ export declare const httpTransport: HttpTransport.HttpJsonTransport<{
|
|
|
690
789
|
readonly image_url: {
|
|
691
790
|
readonly url: string;
|
|
692
791
|
};
|
|
792
|
+
} | {
|
|
793
|
+
readonly type: "file";
|
|
794
|
+
readonly file: {
|
|
795
|
+
readonly filename: string;
|
|
796
|
+
readonly file_data: string;
|
|
797
|
+
};
|
|
693
798
|
})[];
|
|
694
799
|
} | {
|
|
695
800
|
readonly [x: string]: unknown;
|
|
@@ -774,6 +879,12 @@ export declare const route: Route<{
|
|
|
774
879
|
readonly image_url: {
|
|
775
880
|
readonly url: string;
|
|
776
881
|
};
|
|
882
|
+
} | {
|
|
883
|
+
readonly type: "file";
|
|
884
|
+
readonly file: {
|
|
885
|
+
readonly filename: string;
|
|
886
|
+
readonly file_data: string;
|
|
887
|
+
};
|
|
777
888
|
})[];
|
|
778
889
|
} | {
|
|
779
890
|
readonly role: "user";
|
|
@@ -789,6 +900,12 @@ export declare const route: Route<{
|
|
|
789
900
|
readonly image_url: {
|
|
790
901
|
readonly url: string;
|
|
791
902
|
};
|
|
903
|
+
} | {
|
|
904
|
+
readonly type: "file";
|
|
905
|
+
readonly file: {
|
|
906
|
+
readonly filename: string;
|
|
907
|
+
readonly file_data: string;
|
|
908
|
+
};
|
|
792
909
|
})[];
|
|
793
910
|
} | {
|
|
794
911
|
readonly [x: string]: unknown;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Effect, Schema } from "effect";
|
|
1
|
+
import { Effect, Option, Schema } from "effect";
|
|
2
2
|
import { Tool } from "@opencode/schema/tool";
|
|
3
3
|
import { Route } from "../route/client.js";
|
|
4
4
|
import { Auth } from "../route/auth.js";
|
|
@@ -50,6 +50,32 @@ const OpenAIChatAssistantToolCall = Schema.Struct({
|
|
|
50
50
|
arguments: Schema.String,
|
|
51
51
|
}),
|
|
52
52
|
});
|
|
53
|
+
// `reasoning_details` carries two dialects. OpenRouter's `reasoning.*` entries
|
|
54
|
+
// must be replayed unmodified (`index` included), so they keep every field they
|
|
55
|
+
// arrived with. Kimi's OpenAI-compatible surface streams preserved thinking as
|
|
56
|
+
// bare `summary` / `encrypted` entries keyed by a stream-only `index`; Kimi does
|
|
57
|
+
// not document this publicly, so the handling follows Kimi Code (Kimi's own
|
|
58
|
+
// client): merge summary deltas by `index`, replay without `index`, and always
|
|
59
|
+
// send `reasoning_content` alongside. Anything else is dropped at the boundary.
|
|
60
|
+
const OpenRouterDetailFields = {
|
|
61
|
+
id: Schema.optional(Schema.NullOr(Schema.String)),
|
|
62
|
+
format: Schema.optional(Schema.String),
|
|
63
|
+
index: Schema.optional(Schema.Number),
|
|
64
|
+
signature: Schema.optional(Schema.NullOr(Schema.String)),
|
|
65
|
+
};
|
|
66
|
+
const ReasoningDetail = Schema.Union([
|
|
67
|
+
Schema.StructWithRest(Schema.Struct({ type: Schema.Literal("reasoning.text"), text: Schema.optional(Schema.String), ...OpenRouterDetailFields }), [Schema.Record(Schema.String, Schema.Unknown)]),
|
|
68
|
+
Schema.StructWithRest(Schema.Struct({
|
|
69
|
+
type: Schema.Literal("reasoning.summary"),
|
|
70
|
+
summary: Schema.optional(Schema.String),
|
|
71
|
+
...OpenRouterDetailFields,
|
|
72
|
+
}), [Schema.Record(Schema.String, Schema.Unknown)]),
|
|
73
|
+
Schema.StructWithRest(Schema.Struct({ type: Schema.Literal("reasoning.encrypted"), data: Schema.String, ...OpenRouterDetailFields }), [Schema.Record(Schema.String, Schema.Unknown)]),
|
|
74
|
+
Schema.Struct({ type: Schema.Literal("summary"), summary: Schema.String, index: Schema.optional(Schema.Number) }),
|
|
75
|
+
Schema.Struct({ type: Schema.Literal("encrypted"), encrypted: Schema.String, index: Schema.optional(Schema.Number) }),
|
|
76
|
+
]);
|
|
77
|
+
const decodeReasoningDetail = Schema.decodeUnknownOption(ReasoningDetail);
|
|
78
|
+
const knownReasoningDetails = (details) => details.flatMap((detail) => Option.toArray(decodeReasoningDetail(detail)));
|
|
53
79
|
// Intentionally omit Gemini's provider-specific `extra_content.google.thought_signature`
|
|
54
80
|
// extension until direct Google OpenAI-compatible routing is supported here:
|
|
55
81
|
// https://github.com/vercel/ai/issues/11590
|
|
@@ -65,6 +91,10 @@ const OpenAIChatUserContent = Schema.Union([
|
|
|
65
91
|
type: Schema.Literal("image_url"),
|
|
66
92
|
image_url: Schema.Struct({ url: Schema.String }),
|
|
67
93
|
}),
|
|
94
|
+
Schema.Struct({
|
|
95
|
+
type: Schema.Literal("file"),
|
|
96
|
+
file: Schema.Struct({ filename: Schema.String, file_data: Schema.String }),
|
|
97
|
+
}),
|
|
68
98
|
]);
|
|
69
99
|
const OpenAIChatMessage = Schema.Union([
|
|
70
100
|
Schema.Struct({
|
|
@@ -204,12 +234,18 @@ const lowerToolCall = (part, options) => ({
|
|
|
204
234
|
},
|
|
205
235
|
});
|
|
206
236
|
const lowerMedia = Effect.fn("OpenAIChat.lowerMedia")(function* (part) {
|
|
207
|
-
|
|
208
|
-
if (
|
|
209
|
-
return
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
237
|
+
// Chat Completions accepts PDFs, and no other documents, as inline `file` parts; file URLs are not supported.
|
|
238
|
+
if (part.media.mediaType.toLowerCase() === "application/pdf")
|
|
239
|
+
return {
|
|
240
|
+
type: "file",
|
|
241
|
+
file: {
|
|
242
|
+
filename: part.filename ?? "document.pdf",
|
|
243
|
+
file_data: (yield* ProviderShared.requireInlineMedia("OpenAI Chat", part.media)).dataUrl,
|
|
244
|
+
},
|
|
245
|
+
};
|
|
246
|
+
if (part.media.kind !== "image")
|
|
247
|
+
return yield* ProviderShared.invalidRequest(`OpenAI Chat does not support media type ${part.media.mediaType}`);
|
|
248
|
+
const url = ProviderShared.mediaUrl(part.media) ?? (yield* ProviderShared.requireInlineMedia("OpenAI Chat", part.media)).dataUrl;
|
|
213
249
|
return { type: "image_url", image_url: { url } };
|
|
214
250
|
});
|
|
215
251
|
const openAICompatibleReasoningContent = (native) => isRecord(native) && typeof native.reasoning_content === "string" ? native.reasoning_content : undefined;
|
|
@@ -223,10 +259,20 @@ const reasoningDetails = (parts, native, providerMetadataKey) => {
|
|
|
223
259
|
return Array.isArray(details) ? details : [];
|
|
224
260
|
});
|
|
225
261
|
if (parts.some((part) => Array.isArray(part.providerMetadata?.[providerMetadataKey]?.reasoningDetails)))
|
|
226
|
-
return observed;
|
|
262
|
+
return knownReasoningDetails(observed).map(lowerReasoningDetail);
|
|
227
263
|
if (isRecord(native) && Array.isArray(native.reasoning_details))
|
|
228
|
-
return native.reasoning_details;
|
|
264
|
+
return knownReasoningDetails(native.reasoning_details).map(lowerReasoningDetail);
|
|
265
|
+
};
|
|
266
|
+
// Kimi rejects its stream-only `index` on requests
|
|
267
|
+
// ("the reasoning_details ... must not contain streaming index").
|
|
268
|
+
const lowerReasoningDetail = (detail) => {
|
|
269
|
+
if (detail.type === "summary")
|
|
270
|
+
return { type: detail.type, summary: detail.summary };
|
|
271
|
+
if (detail.type === "encrypted")
|
|
272
|
+
return { type: detail.type, encrypted: detail.encrypted };
|
|
273
|
+
return detail;
|
|
229
274
|
};
|
|
275
|
+
const isKimiDetail = (detail) => detail.type === "summary" || detail.type === "encrypted";
|
|
230
276
|
const lowerUserMessage = Effect.fn("OpenAIChat.lowerUserMessage")(function* (message, options) {
|
|
231
277
|
const content = [];
|
|
232
278
|
for (const part of message.content) {
|
|
@@ -285,6 +331,10 @@ const lowerAssistantMessage = Effect.fn("OpenAIChat.lowerAssistantMessage")(func
|
|
|
285
331
|
return "reasoning_content";
|
|
286
332
|
if (!fullyStructured || requireReasoning)
|
|
287
333
|
return "reasoning_content";
|
|
334
|
+
// Kimi always expects `reasoning_content` on replayed assistant messages,
|
|
335
|
+
// even when thinking arrived only through structured details.
|
|
336
|
+
if (details?.some(isKimiDetail))
|
|
337
|
+
return "reasoning_content";
|
|
288
338
|
})();
|
|
289
339
|
const reasoningText = (() => {
|
|
290
340
|
if (configuredField !== undefined)
|
|
@@ -308,7 +358,7 @@ const lowerAssistantMessage = Effect.fn("OpenAIChat.lowerAssistantMessage")(func
|
|
|
308
358
|
});
|
|
309
359
|
const lowerToolMessages = Effect.fn("OpenAIChat.lowerToolMessages")(function* (message, options) {
|
|
310
360
|
const messages = [];
|
|
311
|
-
const
|
|
361
|
+
const attachments = [];
|
|
312
362
|
for (const part of message.content) {
|
|
313
363
|
if (!ProviderShared.supportsContent(part, ["tool-result"]))
|
|
314
364
|
return yield* ProviderShared.unsupportedContent("OpenAI Chat", "tool", ["tool-result"]);
|
|
@@ -330,9 +380,9 @@ const lowerToolMessages = Effect.fn("OpenAIChat.lowerToolMessages")(function* (m
|
|
|
330
380
|
cache_control: options.cacheControl?.(part.cache),
|
|
331
381
|
});
|
|
332
382
|
const files = content.filter((item) => item.type === "file");
|
|
333
|
-
|
|
383
|
+
attachments.push(...(yield* Effect.forEach(files, (item) => lowerMedia(ProviderShared.toolFileMedia(item)))));
|
|
334
384
|
}
|
|
335
|
-
return { messages,
|
|
385
|
+
return { messages, attachments };
|
|
336
386
|
});
|
|
337
387
|
const lowerMessage = Effect.fn("OpenAIChat.lowerMessage")(function* (message, reasoningField, requireReasoning, options) {
|
|
338
388
|
if (message.role === "user")
|
|
@@ -386,23 +436,23 @@ const lowerMessages = Effect.fn("OpenAIChat.lowerMessages")(function* (request,
|
|
|
386
436
|
if (requireAssistantAfterTool && messages.at(-1)?.role === "tool")
|
|
387
437
|
messages.push({ role: "assistant", content: "Done." });
|
|
388
438
|
};
|
|
389
|
-
const
|
|
390
|
-
const
|
|
391
|
-
if (
|
|
439
|
+
const pendingAttachments = [];
|
|
440
|
+
const flushAttachments = () => {
|
|
441
|
+
if (pendingAttachments.length === 0)
|
|
392
442
|
return;
|
|
393
443
|
bridgeTools();
|
|
394
|
-
messages.push({ role: "user", content:
|
|
444
|
+
messages.push({ role: "user", content: pendingAttachments.splice(0) });
|
|
395
445
|
};
|
|
396
446
|
for (const message of request.messages) {
|
|
397
447
|
if (message.role === "user")
|
|
398
448
|
bridgeTools();
|
|
399
449
|
if (message.role === "system") {
|
|
400
450
|
const part = yield* ProviderShared.wrappedSystemUpdate("OpenAI Chat", message);
|
|
401
|
-
if (
|
|
451
|
+
if (pendingAttachments.length > 0) {
|
|
402
452
|
messages.push({
|
|
403
453
|
role: "user",
|
|
404
454
|
content: [
|
|
405
|
-
...
|
|
455
|
+
...pendingAttachments.splice(0),
|
|
406
456
|
{ type: "text", text: part.text, cache_control: options.cacheControl?.(part.cache) },
|
|
407
457
|
],
|
|
408
458
|
});
|
|
@@ -442,13 +492,13 @@ const lowerMessages = Effect.fn("OpenAIChat.lowerMessages")(function* (request,
|
|
|
442
492
|
if (message.role === "tool") {
|
|
443
493
|
const lowered = yield* lowerToolMessages(message, lowering);
|
|
444
494
|
messages.push(...lowered.messages);
|
|
445
|
-
|
|
495
|
+
pendingAttachments.push(...lowered.attachments);
|
|
446
496
|
continue;
|
|
447
497
|
}
|
|
448
|
-
|
|
498
|
+
flushAttachments();
|
|
449
499
|
messages.push(...(yield* lowerMessage(message, reasoningField, requireReasoning, lowering)));
|
|
450
500
|
}
|
|
451
|
-
|
|
501
|
+
flushAttachments();
|
|
452
502
|
return messages;
|
|
453
503
|
});
|
|
454
504
|
// Anthropic via LiteLLM and Amazon Bedrock require `tools` to be present
|
|
@@ -581,7 +631,6 @@ export const fromRequest = Effect.fn("OpenAIChat.fromRequest")(function* (reques
|
|
|
581
631
|
if (reasoningField && RESERVED_REASONING_FIELDS.has(reasoningField))
|
|
582
632
|
return yield* ProviderShared.invalidRequest(`OpenAI Chat reasoning field conflicts with reserved field ${reasoningField}`);
|
|
583
633
|
const generation = request.generation;
|
|
584
|
-
const toolSchemaCompatibility = request.model.compatibility?.toolSchema;
|
|
585
634
|
const flattened = ProviderShared.flattenToolRequest(request);
|
|
586
635
|
const provider = String(request.model.provider);
|
|
587
636
|
const baseURL = request.model.route.endpoint.baseURL;
|
|
@@ -600,7 +649,7 @@ export const fromRequest = Effect.fn("OpenAIChat.fromRequest")(function* (reques
|
|
|
600
649
|
? hasHistory
|
|
601
650
|
? []
|
|
602
651
|
: undefined
|
|
603
|
-
: flattened.tools.map((tool) => lowerTool(tool, ToolSchemaProjection.modelCompatibility(tool.inputSchema,
|
|
652
|
+
: flattened.tools.map((tool) => lowerTool(tool, ToolSchemaProjection.modelCompatibility(tool.inputSchema, request.model), options, supportsStrictMode)),
|
|
604
653
|
tool_choice: hasActiveTools && request.toolChoice ? yield* lowerToolChoice(request.toolChoice) : undefined,
|
|
605
654
|
stream: true,
|
|
606
655
|
...(supportsUsageInStreaming ? { stream_options: { include_usage: true } } : {}),
|
|
@@ -708,14 +757,16 @@ const reasoningDelta = (delta, configuredField) => {
|
|
|
708
757
|
}
|
|
709
758
|
return undefined;
|
|
710
759
|
};
|
|
711
|
-
const detailText = (details) => {
|
|
760
|
+
const detailText = (details, hideKimiSummary) => {
|
|
712
761
|
const text = details.flatMap((detail) => {
|
|
713
|
-
if (
|
|
714
|
-
return [];
|
|
715
|
-
if (detail.type === "reasoning.
|
|
716
|
-
return [detail.
|
|
717
|
-
|
|
718
|
-
|
|
762
|
+
if (detail.type === "reasoning.text")
|
|
763
|
+
return detail.text ? [detail.text] : [];
|
|
764
|
+
if (detail.type === "reasoning.summary")
|
|
765
|
+
return detail.summary ? [detail.summary] : [];
|
|
766
|
+
// Kimi streams the full thinking through `reasoning_content` and a separate
|
|
767
|
+
// summary through details; show the summary only when nothing else does.
|
|
768
|
+
if (detail.type === "summary")
|
|
769
|
+
return detail.summary && !hideKimiSummary ? [detail.summary] : [];
|
|
719
770
|
return [];
|
|
720
771
|
});
|
|
721
772
|
if (text.length > 0)
|
|
@@ -724,25 +775,48 @@ const detailText = (details) => {
|
|
|
724
775
|
const appendReasoningDetails = (result, details) => {
|
|
725
776
|
for (const detail of details) {
|
|
726
777
|
const previous = result.at(-1);
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
!isRecord(detail) ||
|
|
730
|
-
detail.type !== "reasoning.text" ||
|
|
731
|
-
conflictingReasoningTextDetails(previous, detail)) {
|
|
778
|
+
const merged = previous === undefined ? undefined : mergeReasoningDetails(previous, detail);
|
|
779
|
+
if (merged === undefined) {
|
|
732
780
|
result.push(detail);
|
|
733
781
|
continue;
|
|
734
782
|
}
|
|
735
|
-
result[result.length - 1] =
|
|
783
|
+
result[result.length - 1] = merged;
|
|
784
|
+
}
|
|
785
|
+
};
|
|
786
|
+
// Consecutive text or summary deltas of the same kind accumulate into one
|
|
787
|
+
// entry; encrypted entries are opaque and never merge.
|
|
788
|
+
const mergeReasoningDetails = (previous, detail) => {
|
|
789
|
+
if (conflictingReasoningDetails(previous, detail))
|
|
790
|
+
return undefined;
|
|
791
|
+
if (previous.type === "reasoning.text" && detail.type === "reasoning.text")
|
|
792
|
+
return {
|
|
736
793
|
...previous,
|
|
737
|
-
...
|
|
738
|
-
text: `${
|
|
739
|
-
|
|
740
|
-
format: mergeDetailValue(previous.format, detail.format),
|
|
794
|
+
...detail,
|
|
795
|
+
text: `${previous.text ?? ""}${detail.text ?? ""}`,
|
|
796
|
+
...mergeDetailIdentity(previous, detail),
|
|
741
797
|
};
|
|
742
|
-
|
|
798
|
+
if (previous.type === "reasoning.summary" && detail.type === "reasoning.summary")
|
|
799
|
+
return {
|
|
800
|
+
...previous,
|
|
801
|
+
...detail,
|
|
802
|
+
summary: `${previous.summary ?? ""}${detail.summary ?? ""}`,
|
|
803
|
+
...mergeDetailIdentity(previous, detail),
|
|
804
|
+
};
|
|
805
|
+
if (previous.type === "summary" && detail.type === "summary")
|
|
806
|
+
return { ...previous, ...detail, summary: previous.summary + detail.summary };
|
|
807
|
+
};
|
|
808
|
+
// The first non-empty signature and format win; a later delta may carry the
|
|
809
|
+
// signature for text that streamed earlier.
|
|
810
|
+
const mergeDetailIdentity = (previous, current) => {
|
|
811
|
+
const signature = mergeDetailValue(previous.signature, current.signature);
|
|
812
|
+
const format = mergeDetailValue(previous.format, current.format);
|
|
813
|
+
return {
|
|
814
|
+
...(signature === undefined ? {} : { signature }),
|
|
815
|
+
...(format === undefined ? {} : { format }),
|
|
816
|
+
};
|
|
743
817
|
};
|
|
744
818
|
const mergeDetailValue = (previous, current) => previous || current || (previous !== undefined ? previous : current);
|
|
745
|
-
const
|
|
819
|
+
const conflictingReasoningDetails = (previous, current) => conflictingDetailValue(previous.id, current.id) ||
|
|
746
820
|
conflictingDetailValue(previous.index, current.index) ||
|
|
747
821
|
conflictingDetailValue(previous.format, current.format) ||
|
|
748
822
|
(Boolean(previous.signature) && Boolean(current.signature) && previous.signature !== current.signature);
|
|
@@ -798,12 +872,17 @@ const step = (state, event) => Effect.gen(function* () {
|
|
|
798
872
|
return [{ ...state, usage }, events];
|
|
799
873
|
}
|
|
800
874
|
const reasoningField = state.reasoningField ?? reasoning?.field;
|
|
801
|
-
const
|
|
875
|
+
const reasoningTextObserved = state.reasoningTextObserved || reasoning !== undefined;
|
|
876
|
+
const detailDelta = Array.isArray(delta?.reasoning_details)
|
|
877
|
+
? knownReasoningDetails(delta.reasoning_details)
|
|
878
|
+
: undefined;
|
|
802
879
|
if (detailDelta !== undefined)
|
|
803
880
|
appendReasoningDetails(state.reasoningDetails, detailDelta);
|
|
804
881
|
const reasoningDetailsObserved = state.reasoningDetailsObserved || detailDelta !== undefined;
|
|
805
882
|
const deltaMetadata = reasoningMetadata(state.providerMetadataKey, reasoningField);
|
|
806
|
-
const text = detailDelta?.length
|
|
883
|
+
const text = detailDelta?.length
|
|
884
|
+
? (detailText(detailDelta, reasoningTextObserved) ?? reasoning?.text)
|
|
885
|
+
: reasoning?.text;
|
|
807
886
|
if (text !== undefined)
|
|
808
887
|
lifecycle = Lifecycle.reasoningDelta(lifecycle, events, "reasoning-0", text, deltaMetadata);
|
|
809
888
|
else if (reasoningDetailsObserved &&
|
|
@@ -881,6 +960,7 @@ const step = (state, event) => Effect.gen(function* () {
|
|
|
881
960
|
finishReason,
|
|
882
961
|
lifecycle,
|
|
883
962
|
reasoningField,
|
|
963
|
+
reasoningTextObserved,
|
|
884
964
|
reasoningDetails: state.reasoningDetails,
|
|
885
965
|
reasoningDetailsObserved,
|
|
886
966
|
reasoningEmitted,
|
|
@@ -947,6 +1027,7 @@ export const protocol = Protocol.make({
|
|
|
947
1027
|
toolCallEvents: [],
|
|
948
1028
|
lifecycle: Lifecycle.initial(),
|
|
949
1029
|
reasoningField: request.model.compatibility?.reasoningField,
|
|
1030
|
+
reasoningTextObserved: false,
|
|
950
1031
|
reasoningDetails: [],
|
|
951
1032
|
reasoningDetailsObserved: false,
|
|
952
1033
|
reasoningEmitted: false,
|
|
@@ -23,6 +23,12 @@ export declare const route: Route<{
|
|
|
23
23
|
readonly image_url: {
|
|
24
24
|
readonly url: string;
|
|
25
25
|
};
|
|
26
|
+
} | {
|
|
27
|
+
readonly type: "file";
|
|
28
|
+
readonly file: {
|
|
29
|
+
readonly filename: string;
|
|
30
|
+
readonly file_data: string;
|
|
31
|
+
};
|
|
26
32
|
})[];
|
|
27
33
|
} | {
|
|
28
34
|
readonly role: "user";
|
|
@@ -38,6 +44,12 @@ export declare const route: Route<{
|
|
|
38
44
|
readonly image_url: {
|
|
39
45
|
readonly url: string;
|
|
40
46
|
};
|
|
47
|
+
} | {
|
|
48
|
+
readonly type: "file";
|
|
49
|
+
readonly file: {
|
|
50
|
+
readonly filename: string;
|
|
51
|
+
readonly file_data: string;
|
|
52
|
+
};
|
|
41
53
|
})[];
|
|
42
54
|
} | {
|
|
43
55
|
readonly [x: string]: unknown;
|
|
@@ -72,8 +72,8 @@ export declare const route: Route<{
|
|
|
72
72
|
} | {
|
|
73
73
|
readonly type: "input_file";
|
|
74
74
|
readonly filename: string;
|
|
75
|
-
readonly detail?: string | undefined;
|
|
76
75
|
readonly file_data?: string | undefined;
|
|
76
|
+
readonly detail?: string | undefined;
|
|
77
77
|
readonly file_url?: string | undefined;
|
|
78
78
|
} | {
|
|
79
79
|
readonly type: "input_text";
|
|
@@ -109,8 +109,8 @@ export declare const route: Route<{
|
|
|
109
109
|
} | {
|
|
110
110
|
readonly type: "input_file";
|
|
111
111
|
readonly filename: string;
|
|
112
|
-
readonly detail?: string | undefined;
|
|
113
112
|
readonly file_data?: string | undefined;
|
|
113
|
+
readonly detail?: string | undefined;
|
|
114
114
|
readonly file_url?: string | undefined;
|
|
115
115
|
} | {
|
|
116
116
|
readonly type: "input_text";
|