@opencode/ai 0.0.0-dev-20536 → 0.0.0-dev-20538
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 +7 -7
- package/dist/protocols/bedrock-converse.js +3 -3
- package/dist/protocols/cartesia-speech.js +3 -3
- package/dist/protocols/cohere-chat.js +1 -1
- package/dist/protocols/elevenlabs-speech.js +3 -3
- package/dist/protocols/gemini.js +2 -2
- package/dist/protocols/google-interactions.js +7 -7
- package/dist/protocols/google-speech.js +1 -1
- package/dist/protocols/google-transcription.js +1 -1
- package/dist/protocols/meta-responses.js +3 -3
- package/dist/protocols/mistral-chat.js +8 -8
- package/dist/protocols/open-responses.js +13 -9
- package/dist/protocols/openai-chat.js +8 -8
- package/dist/protocols/openai-images.js +2 -2
- package/dist/protocols/openai-responses.js +3 -3
- package/dist/protocols/openai-speech.js +1 -1
- package/dist/protocols/openai-transcription.js +1 -1
- package/dist/protocols/shared.js +2 -2
- package/dist/protocols/stability-images.js +1 -1
- package/dist/protocols/utils/bedrock-media.js +2 -2
- package/dist/protocols/utils/responses-checkpoint.js +2 -2
- package/dist/protocols/utils/responses-hosted-tools.js +1 -1
- package/dist/route/framing.js +23 -23
- package/package.json +3 -3
|
@@ -462,7 +462,7 @@ const serverToolResultType = (name) => {
|
|
|
462
462
|
return "web_fetch_tool_result";
|
|
463
463
|
return undefined;
|
|
464
464
|
};
|
|
465
|
-
const lowerServerToolResult = Effect.
|
|
465
|
+
const lowerServerToolResult = Effect.fnUntraced(function* (part, providerMetadataKey) {
|
|
466
466
|
const wireType = serverToolResultType(part.name);
|
|
467
467
|
if (!wireType)
|
|
468
468
|
return yield* invalid(`Anthropic Messages does not know how to round-trip server tool result for ${part.name}`);
|
|
@@ -538,7 +538,7 @@ const citationsFromMetadata = (metadata) => {
|
|
|
538
538
|
return undefined;
|
|
539
539
|
};
|
|
540
540
|
const isHttpUrl = (value) => /^https?:\/\//i.test(value.trim());
|
|
541
|
-
const lowerMedia = Effect.
|
|
541
|
+
const lowerMedia = Effect.fnUntraced(function* (part, breakpoints) {
|
|
542
542
|
const mime = part.media.mediaType.toLowerCase();
|
|
543
543
|
const cacheControlValue = breakpoints ? cacheControl(breakpoints, part.cache) : undefined;
|
|
544
544
|
const fileId = fileIdFromMetadata(part.metadata);
|
|
@@ -716,7 +716,7 @@ const splitsLocalToolResults = (messages, index) => {
|
|
|
716
716
|
}
|
|
717
717
|
return pending.size > 0;
|
|
718
718
|
};
|
|
719
|
-
const lowerNativeSystemUpdate = Effect.
|
|
719
|
+
const lowerNativeSystemUpdate = Effect.fnUntraced(function* (message, breakpoints) {
|
|
720
720
|
const content = yield* ProviderShared.systemUpdateText("Anthropic Messages", message);
|
|
721
721
|
return {
|
|
722
722
|
role: "system",
|
|
@@ -727,7 +727,7 @@ const lowerNativeSystemUpdate = Effect.fn("AnthropicMessages.lowerNativeSystemUp
|
|
|
727
727
|
})),
|
|
728
728
|
};
|
|
729
729
|
});
|
|
730
|
-
const lowerMessages = Effect.
|
|
730
|
+
const lowerMessages = Effect.fnUntraced(function* (request, breakpoints) {
|
|
731
731
|
const messages = [];
|
|
732
732
|
const providerMetadataKey = request.model.route.providerMetadataKey ?? String(request.model.provider);
|
|
733
733
|
for (const [index, message] of request.messages.entries()) {
|
|
@@ -1132,7 +1132,7 @@ const onContentBlockStart = (state, event) => {
|
|
|
1132
1132
|
const events = [];
|
|
1133
1133
|
return [{ ...state, lifecycle: Lifecycle.stepStart(state.lifecycle, events) }, [...events, result]];
|
|
1134
1134
|
};
|
|
1135
|
-
const onContentBlockDelta = Effect.
|
|
1135
|
+
const onContentBlockDelta = Effect.fnUntraced(function* (state, event) {
|
|
1136
1136
|
const delta = event.delta;
|
|
1137
1137
|
if (delta.type === "compaction_delta") {
|
|
1138
1138
|
if (event.index === undefined || !(event.index in state.compactions) || delta.content === undefined)
|
|
@@ -1190,7 +1190,7 @@ const onContentBlockDelta = Effect.fn("AnthropicMessages.onContentBlockDelta")(f
|
|
|
1190
1190
|
}
|
|
1191
1191
|
return [state, NO_EVENTS];
|
|
1192
1192
|
});
|
|
1193
|
-
const onContentBlockStop = Effect.
|
|
1193
|
+
const onContentBlockStop = Effect.fnUntraced(function* (state, event) {
|
|
1194
1194
|
if (event.index === undefined)
|
|
1195
1195
|
return [state, NO_EVENTS];
|
|
1196
1196
|
if (event.index in state.compactions) {
|
|
@@ -1246,7 +1246,7 @@ const onMessageDelta = (state, event) => {
|
|
|
1246
1246
|
NO_EVENTS,
|
|
1247
1247
|
];
|
|
1248
1248
|
};
|
|
1249
|
-
const onMessageStop = Effect.
|
|
1249
|
+
const onMessageStop = Effect.fnUntraced(function* (state) {
|
|
1250
1250
|
if (Object.keys(state.compactions).length)
|
|
1251
1251
|
return yield* ProviderShared.eventError(ADAPTER, "Response ended with an incomplete compaction block");
|
|
1252
1252
|
const result = yield* ToolStream.finishAll(ADAPTER, state.tools);
|
|
@@ -211,7 +211,7 @@ const lowerToolCall = (part, normalizeID) => ({
|
|
|
211
211
|
input: removeEmptyToolInputKeys(part.input),
|
|
212
212
|
},
|
|
213
213
|
});
|
|
214
|
-
const lowerToolResultContent = Effect.
|
|
214
|
+
const lowerToolResultContent = Effect.fnUntraced(function* (part, documentNames) {
|
|
215
215
|
if (part.result.type === "text" || part.result.type === "error")
|
|
216
216
|
return [{ text: ProviderShared.toolResultText(part) }];
|
|
217
217
|
if (part.result.type === "json")
|
|
@@ -227,7 +227,7 @@ const lowerToolResultContent = Effect.fn("BedrockConverse.lowerToolResultContent
|
|
|
227
227
|
}
|
|
228
228
|
return content;
|
|
229
229
|
});
|
|
230
|
-
const lowerToolResult = Effect.
|
|
230
|
+
const lowerToolResult = Effect.fnUntraced(function* (part, documentNames, normalizeID) {
|
|
231
231
|
return {
|
|
232
232
|
toolResult: {
|
|
233
233
|
toolUseId: normalizeID(part.id),
|
|
@@ -238,7 +238,7 @@ const lowerToolResult = Effect.fn("BedrockConverse.lowerToolResult")(function* (
|
|
|
238
238
|
});
|
|
239
239
|
// Keep Claude and Nova tool-result images inline; put other models' images beside the result.
|
|
240
240
|
const keepToolImagesInline = (id) => id.includes("anthropic.claude-") || id.includes("amazon.nova-");
|
|
241
|
-
const lowerMessages = Effect.
|
|
241
|
+
const lowerMessages = Effect.fnUntraced(function* (request, breakpoints) {
|
|
242
242
|
const messages = [];
|
|
243
243
|
const documentNames = new Set();
|
|
244
244
|
// Mistral can reject replay IDs even when they satisfy Converse's broader ID syntax.
|
|
@@ -38,7 +38,7 @@ const decodeEvent = route.decodeFrame(SseEvent);
|
|
|
38
38
|
/** Timestamps exist only on the SSE endpoint, so a `generate` that asks for them collects an SSE stream. */
|
|
39
39
|
const usesSse = (request) => request.mode === "stream" || request.timestamps === true;
|
|
40
40
|
const CONTAINERS = { pcm: "raw", wav: "wav", mp3: "mp3" };
|
|
41
|
-
const outputFormat = Effect.
|
|
41
|
+
const outputFormat = Effect.fnUntraced(function* (request) {
|
|
42
42
|
const sse = usesSse(request);
|
|
43
43
|
const format = request.format ?? (sse ? "pcm" : "mp3");
|
|
44
44
|
const container = CONTAINERS[format];
|
|
@@ -69,7 +69,7 @@ const fromRequest = Effect.fn("CartesiaSpeech.fromRequest")(function* (request)
|
|
|
69
69
|
// ---------------------------------------------------------------------------
|
|
70
70
|
// 6. Stream parsing
|
|
71
71
|
// ---------------------------------------------------------------------------
|
|
72
|
-
const onEvent = Effect.
|
|
72
|
+
const onEvent = Effect.fnUntraced(function* (state, frame) {
|
|
73
73
|
const event = yield* decodeEvent(frame);
|
|
74
74
|
if (event.type === "chunk" && event.data !== undefined)
|
|
75
75
|
return SpeechStream.delta(state, event.data);
|
|
@@ -89,7 +89,7 @@ const onEvent = Effect.fn("CartesiaSpeech.onEvent")(function* (state, frame) {
|
|
|
89
89
|
});
|
|
90
90
|
return [state, []];
|
|
91
91
|
});
|
|
92
|
-
const finish = Effect.
|
|
92
|
+
const finish = Effect.fnUntraced(function* (state, context) {
|
|
93
93
|
if (usesSse(context.request) && !state.done)
|
|
94
94
|
return yield* route.incomplete();
|
|
95
95
|
const format = yield* outputFormat(context.request);
|
|
@@ -225,7 +225,7 @@ const mapUsage = (usage) => new Usage({
|
|
|
225
225
|
providerMetadata: { cohere: usage },
|
|
226
226
|
});
|
|
227
227
|
// Lifecycle deltas open blocks on demand and ends are no-ops for closed blocks, so content-start needs no handling.
|
|
228
|
-
const step = Effect.
|
|
228
|
+
const step = Effect.fnUntraced(function* (state, event) {
|
|
229
229
|
const events = [];
|
|
230
230
|
switch (event.type) {
|
|
231
231
|
case "message-start":
|
|
@@ -32,7 +32,7 @@ const OUTPUT_FORMATS = {
|
|
|
32
32
|
opus: "opus_48000_64",
|
|
33
33
|
};
|
|
34
34
|
/** WAV is served only by the non-streaming endpoints. */
|
|
35
|
-
const outputFormat = Effect.
|
|
35
|
+
const outputFormat = Effect.fnUntraced(function* (request) {
|
|
36
36
|
const format = request.providerOptions?.outputFormat ?? OUTPUT_FORMATS[request.format ?? "mp3"];
|
|
37
37
|
if (format === undefined)
|
|
38
38
|
return yield* route.unsupported("media.format", `${route.name} has no default output format for "${request.format}"; pass providerOptions.outputFormat`);
|
|
@@ -55,7 +55,7 @@ const path = (request) => `${PATH}/${encodeURIComponent(SpeechStream.voiceID(req
|
|
|
55
55
|
// ---------------------------------------------------------------------------
|
|
56
56
|
// 6. Stream parsing
|
|
57
57
|
// ---------------------------------------------------------------------------
|
|
58
|
-
const onRecord = Effect.
|
|
58
|
+
const onRecord = Effect.fnUntraced(function* (state, frame) {
|
|
59
59
|
const record = yield* decodeRecord(frame);
|
|
60
60
|
const [next, events] = SpeechStream.delta(state, record.audio_base64);
|
|
61
61
|
const alignment = record.alignment;
|
|
@@ -80,7 +80,7 @@ const describeOutput = (format) => {
|
|
|
80
80
|
const encoding = PCM_CODECS[codec];
|
|
81
81
|
return encoding === undefined ? SpeechStream.container(codec, sampleRate) : SpeechStream.pcm(encoding, sampleRate);
|
|
82
82
|
};
|
|
83
|
-
const finish = Effect.
|
|
83
|
+
const finish = Effect.fnUntraced(function* (state, context) {
|
|
84
84
|
const requestID = context.http.headers["request-id"];
|
|
85
85
|
return yield* SpeechStream.finish(route, state, {
|
|
86
86
|
...describeOutput(yield* outputFormat(context.request)),
|
package/dist/protocols/gemini.js
CHANGED
|
@@ -201,7 +201,7 @@ const lowerToolConfig = (toolChoice) => ProviderShared.matchToolChoice("Gemini",
|
|
|
201
201
|
required: () => ({ functionCallingConfig: { mode: "ANY" } }),
|
|
202
202
|
tool: (name) => ({ functionCallingConfig: { mode: "ANY", allowedFunctionNames: [name] } }),
|
|
203
203
|
});
|
|
204
|
-
const lowerContentPart = Effect.
|
|
204
|
+
const lowerContentPart = Effect.fnUntraced(function* (part) {
|
|
205
205
|
if (part.type === "text")
|
|
206
206
|
return { text: part.text };
|
|
207
207
|
return yield* GeminiGenerateContent.mediaPart("Gemini", part.media);
|
|
@@ -217,7 +217,7 @@ const lowerToolCall = (part, omitIds, metadataKey) => ({
|
|
|
217
217
|
functionCall: { ...(omitIds ? {} : { id: part.id }), name: part.name, args: part.input },
|
|
218
218
|
thoughtSignature: thoughtSignature(part.providerMetadata, metadataKey),
|
|
219
219
|
});
|
|
220
|
-
const lowerMessages = Effect.
|
|
220
|
+
const lowerMessages = Effect.fnUntraced(function* (request) {
|
|
221
221
|
const contents = [];
|
|
222
222
|
const metadataKey = request.model.route.providerMetadataKey ?? String(request.model.provider);
|
|
223
223
|
const omitCallIds = omitsFunctionCallIds(request.model.id);
|
|
@@ -140,7 +140,7 @@ const Event = Schema.Union([
|
|
|
140
140
|
// =============================================================================
|
|
141
141
|
// Request Body Construction
|
|
142
142
|
// =============================================================================
|
|
143
|
-
const mediaContent = Effect.
|
|
143
|
+
const mediaContent = Effect.fnUntraced(function* (asset) {
|
|
144
144
|
if (asset.kind !== "image" &&
|
|
145
145
|
asset.kind !== "audio" &&
|
|
146
146
|
asset.kind !== "video" &&
|
|
@@ -160,7 +160,7 @@ const signature = (metadata, key) => {
|
|
|
160
160
|
? value.interactionSignature
|
|
161
161
|
: undefined;
|
|
162
162
|
};
|
|
163
|
-
const lowerMessages = Effect.
|
|
163
|
+
const lowerMessages = Effect.fnUntraced(function* (request) {
|
|
164
164
|
const steps = [];
|
|
165
165
|
const key = request.model.route.providerMetadataKey ?? String(request.model.provider);
|
|
166
166
|
for (const message of request.messages) {
|
|
@@ -223,7 +223,7 @@ const lowerMessages = Effect.fn("GoogleInteractions.lowerMessages")(function* (r
|
|
|
223
223
|
}
|
|
224
224
|
return steps;
|
|
225
225
|
});
|
|
226
|
-
const lowerToolResult = Effect.
|
|
226
|
+
const lowerToolResult = Effect.fnUntraced(function* (part) {
|
|
227
227
|
if (part.result.type === "json" && ProviderShared.isRecord(part.result.value))
|
|
228
228
|
return part.result.value;
|
|
229
229
|
if (part.result.type !== "content")
|
|
@@ -306,7 +306,7 @@ const mapUsage = (usage, key) => {
|
|
|
306
306
|
providerMetadata: { [key]: usage },
|
|
307
307
|
});
|
|
308
308
|
};
|
|
309
|
-
const onStart = Effect.
|
|
309
|
+
const onStart = Effect.fnUntraced(function* (state, index, step) {
|
|
310
310
|
const events = [];
|
|
311
311
|
let lifecycle = Lifecycle.stepStart(state.lifecycle, events);
|
|
312
312
|
let tools = state.tools;
|
|
@@ -340,7 +340,7 @@ const onStart = Effect.fn("GoogleInteractions.onStart")(function* (state, index,
|
|
|
340
340
|
return yield* ProviderShared.eventError(ADAPTER, `Unsupported Interactions step: ${step.type}`, encodeJson(step));
|
|
341
341
|
return [{ ...state, lifecycle, tools, steps: { ...state.steps, [index]: step } }, events];
|
|
342
342
|
});
|
|
343
|
-
const onDelta = Effect.
|
|
343
|
+
const onDelta = Effect.fnUntraced(function* (state, index, delta) {
|
|
344
344
|
const step = state.steps[index];
|
|
345
345
|
if (!step)
|
|
346
346
|
return yield* ProviderShared.eventError(ADAPTER, "Interactions delta without step.start", encodeJson(delta));
|
|
@@ -375,7 +375,7 @@ const onDelta = Effect.fn("GoogleInteractions.onDelta")(function* (state, index,
|
|
|
375
375
|
}
|
|
376
376
|
return yield* ProviderShared.eventError(ADAPTER, `Unsupported Interactions delta: ${delta.type}`, encodeJson(delta));
|
|
377
377
|
});
|
|
378
|
-
const onStop = Effect.
|
|
378
|
+
const onStop = Effect.fnUntraced(function* (state, index) {
|
|
379
379
|
const step = state.steps[index];
|
|
380
380
|
if (!step)
|
|
381
381
|
return yield* ProviderShared.eventError(ADAPTER, "Interactions step.stop without step.start");
|
|
@@ -393,7 +393,7 @@ const onStop = Effect.fn("GoogleInteractions.onStop")(function* (state, index) {
|
|
|
393
393
|
const result = yield* ToolStream.finish(ADAPTER, state.tools, index);
|
|
394
394
|
return [{ ...state, tools: result.tools }, result.events ?? []];
|
|
395
395
|
});
|
|
396
|
-
const step = Effect.
|
|
396
|
+
const step = Effect.fnUntraced(function* (state, event) {
|
|
397
397
|
switch (event.event_type) {
|
|
398
398
|
case "step.start":
|
|
399
399
|
return yield* onStart(state, event.index, event.step);
|
|
@@ -42,7 +42,7 @@ const fromRequest = Effect.fn("GoogleSpeech.fromRequest")(function* (request) {
|
|
|
42
42
|
// ---------------------------------------------------------------------------
|
|
43
43
|
// 6. Stream parsing
|
|
44
44
|
// ---------------------------------------------------------------------------
|
|
45
|
-
const step = Effect.
|
|
45
|
+
const step = Effect.fnUntraced(function* (state, frame) {
|
|
46
46
|
const chunk = yield* decodeChunk(frame);
|
|
47
47
|
const blocked = GeminiGenerateContent.blocked(route.name, chunk, frame);
|
|
48
48
|
if (blocked !== undefined)
|
|
@@ -70,7 +70,7 @@ const turn = (part) => {
|
|
|
70
70
|
},
|
|
71
71
|
};
|
|
72
72
|
};
|
|
73
|
-
const step = Effect.
|
|
73
|
+
const step = Effect.fnUntraced(function* (state, frame) {
|
|
74
74
|
const chunk = yield* decodeChunk(frame);
|
|
75
75
|
const blocked = GeminiGenerateContent.blocked(route.name, chunk, frame);
|
|
76
76
|
if (blocked !== undefined)
|
|
@@ -74,7 +74,7 @@ const HOSTED_TOOLS = {
|
|
|
74
74
|
image_generation_call: {
|
|
75
75
|
name: "image_generation",
|
|
76
76
|
input: () => ({}),
|
|
77
|
-
result: Effect.
|
|
77
|
+
result: Effect.fnUntraced(function* (raw) {
|
|
78
78
|
const item = yield* Schema.decodeUnknownEffect(ImageItem)(raw).pipe(Effect.mapError((cause) => ProviderShared.eventError(ADAPTER, "Meta returned an invalid image item", ProviderShared.encodeJson(raw), cause)));
|
|
79
79
|
if (item.error !== undefined && item.error !== null)
|
|
80
80
|
return { type: "error", value: item.error };
|
|
@@ -92,7 +92,7 @@ const HOSTED_TOOLS = {
|
|
|
92
92
|
}),
|
|
93
93
|
},
|
|
94
94
|
};
|
|
95
|
-
const onEvent = Effect.
|
|
95
|
+
const onEvent = Effect.fnUntraced(function* (state, input) {
|
|
96
96
|
const event = OpenResponses.normalize(state, input);
|
|
97
97
|
if (event.type === "response.output_item.done" && event.item && ResponsesHostedTools.isItem(event.item, HOSTED_TOOLS))
|
|
98
98
|
return yield* ResponsesHostedTools.onDone(state, event.item, HOSTED_TOOLS);
|
|
@@ -116,7 +116,7 @@ const onEvent = Effect.fn("MetaResponses.onEvent")(function* (state, input) {
|
|
|
116
116
|
: item),
|
|
117
117
|
];
|
|
118
118
|
});
|
|
119
|
-
const step = Effect.
|
|
119
|
+
const step = Effect.fnUntraced(function* (state, input) {
|
|
120
120
|
const completedItems = new Set(state.completedItems);
|
|
121
121
|
const event = OpenResponses.normalize(state, input);
|
|
122
122
|
if (event.type === "response.output_item.done" && event.item && completedItems.has(event.item.id))
|
|
@@ -143,7 +143,7 @@ const MistralEvent = Schema.StructWithRest(Schema.Struct({
|
|
|
143
143
|
error: optionalNull(MistralError),
|
|
144
144
|
}), [Schema.Record(Schema.String, Schema.Unknown)]);
|
|
145
145
|
const MistralStreamEvent = Schema.Union([Schema.Literal(DONE), Protocol.jsonEvent(MistralEvent)]);
|
|
146
|
-
const lowerMedia = Effect.
|
|
146
|
+
const lowerMedia = Effect.fnUntraced(function* (part) {
|
|
147
147
|
const mime = part.media.mediaType.toLowerCase();
|
|
148
148
|
const url = ProviderShared.mediaUrl(part.media) ??
|
|
149
149
|
(yield* ProviderShared.requireInlineMedia("Mistral Chat", part.media)).dataUrl;
|
|
@@ -153,7 +153,7 @@ const lowerMedia = Effect.fn("MistralChat.lowerMedia")(function* (part) {
|
|
|
153
153
|
return { type: "document_url", document_url: url };
|
|
154
154
|
return yield* ProviderShared.invalidRequest(`Mistral Chat does not support media type ${part.media.mediaType}`);
|
|
155
155
|
});
|
|
156
|
-
const lowerUser = Effect.
|
|
156
|
+
const lowerUser = Effect.fnUntraced(function* (message) {
|
|
157
157
|
const content = [];
|
|
158
158
|
for (const part of message.content) {
|
|
159
159
|
if (part.type === "text") {
|
|
@@ -175,7 +175,7 @@ const lowerToolCall = (part, normalizeID) => ({
|
|
|
175
175
|
type: "function",
|
|
176
176
|
function: { name: part.name, arguments: ProviderShared.encodeJson(part.input) },
|
|
177
177
|
});
|
|
178
|
-
const lowerAssistant = Effect.
|
|
178
|
+
const lowerAssistant = Effect.fnUntraced(function* (message, normalizeID, prefix) {
|
|
179
179
|
const structured = message.content.some((part) => part.type === "reasoning" && isMistralThinkingContent(part.providerMetadata?.mistral?.thinking));
|
|
180
180
|
const content = [];
|
|
181
181
|
const text = [];
|
|
@@ -211,7 +211,7 @@ const lowerAssistant = Effect.fn("MistralChat.lowerAssistant")(function* (messag
|
|
|
211
211
|
...(prefix ? { prefix: true } : {}),
|
|
212
212
|
};
|
|
213
213
|
});
|
|
214
|
-
const lowerToolResults = Effect.
|
|
214
|
+
const lowerToolResults = Effect.fnUntraced(function* (message, normalizeID) {
|
|
215
215
|
const output = [];
|
|
216
216
|
for (const part of message.content) {
|
|
217
217
|
if (part.type !== "tool-result")
|
|
@@ -244,7 +244,7 @@ const lowerToolResults = Effect.fn("MistralChat.lowerToolResults")(function* (me
|
|
|
244
244
|
}
|
|
245
245
|
return output;
|
|
246
246
|
});
|
|
247
|
-
const lowerMessages = Effect.
|
|
247
|
+
const lowerMessages = Effect.fnUntraced(function* (request) {
|
|
248
248
|
const normalizeID = MistralToolID.normalizer(request);
|
|
249
249
|
const messages = request.system.length === 0
|
|
250
250
|
? []
|
|
@@ -451,7 +451,7 @@ const toolText = (tool) => {
|
|
|
451
451
|
return value;
|
|
452
452
|
return value === null || value === undefined ? "" : ProviderShared.encodeJson(value);
|
|
453
453
|
};
|
|
454
|
-
const appendTools = Effect.
|
|
454
|
+
const appendTools = Effect.fnUntraced(function* (initial, events, deltas) {
|
|
455
455
|
if (deltas.length === 0)
|
|
456
456
|
return initial;
|
|
457
457
|
let state = closeActive(initial, events);
|
|
@@ -506,7 +506,7 @@ const hasLateContent = (event) => {
|
|
|
506
506
|
return true;
|
|
507
507
|
return (delta?.tool_calls ?? []).some((tool) => Boolean(tool.id) || Boolean(tool.function?.name) || tool.function?.arguments !== undefined);
|
|
508
508
|
};
|
|
509
|
-
const step = Effect.
|
|
509
|
+
const step = Effect.fnUntraced(function* (state, event) {
|
|
510
510
|
if (event.error) {
|
|
511
511
|
const body = ProviderShared.encodeJson(event);
|
|
512
512
|
return yield* new AIError({
|
|
@@ -559,7 +559,7 @@ const step = Effect.fn("MistralChat.step")(function* (state, event) {
|
|
|
559
559
|
events,
|
|
560
560
|
];
|
|
561
561
|
});
|
|
562
|
-
const finishEvents = Effect.
|
|
562
|
+
const finishEvents = Effect.fnUntraced(function* (state) {
|
|
563
563
|
if (!state.finishReason)
|
|
564
564
|
return yield* new AIError({
|
|
565
565
|
reason: new InvalidProviderOutputError({
|
|
@@ -289,7 +289,7 @@ const BASE_ADAPTER = { id: ADAPTER, name: NAME };
|
|
|
289
289
|
// =============================================================================
|
|
290
290
|
// Request Lowering
|
|
291
291
|
// =============================================================================
|
|
292
|
-
export const lowerTool = Effect.
|
|
292
|
+
export const lowerTool = Effect.fnUntraced(function* (protocolName, tool) {
|
|
293
293
|
if (tool.native !== undefined)
|
|
294
294
|
return yield* ProviderShared.invalidRequest(`${protocolName} does not support provider-native tool ${tool.name}`);
|
|
295
295
|
return {
|
|
@@ -343,12 +343,15 @@ const lowerReasoning = (part, providerMetadataKey) => {
|
|
|
343
343
|
encrypted_content: encryptedContent,
|
|
344
344
|
};
|
|
345
345
|
};
|
|
346
|
-
const
|
|
346
|
+
const decodeImageDetail = ProviderShared.validateWith(Schema.decodeUnknownEffect(OpenResponsesInputImage.fields.detail));
|
|
347
|
+
const decodeMessageMetadata = ProviderShared.validateWith(Schema.decodeUnknownEffect(MessageMetadata));
|
|
348
|
+
const lowerMedia = Effect.fnUntraced(function* (part, request, adapter, target) {
|
|
347
349
|
const media = part.media.inline();
|
|
348
350
|
const providerMedia = adapter.lowerMedia?.({ part, media, request });
|
|
349
351
|
if (providerMedia)
|
|
350
352
|
return providerMedia;
|
|
351
|
-
const
|
|
353
|
+
const rawDetail = part.providerMetadata?.[metadataKey(request.model)]?.detail;
|
|
354
|
+
const detail = rawDetail === undefined ? undefined : yield* decodeImageDetail(rawDetail);
|
|
352
355
|
const mime = part.media.mediaType.toLowerCase();
|
|
353
356
|
const url = ProviderShared.mediaUrl(part.media);
|
|
354
357
|
const location = url ?? (yield* ProviderShared.requireInlineMedia(adapter.name, part.media)).dataUrl;
|
|
@@ -403,11 +406,12 @@ const lowerToolResultOutput = Effect.fnUntraced(function* (part, request, adapte
|
|
|
403
406
|
return yield* Effect.forEach(content, (item) => lowerToolResultContentItem(item, request, adapter));
|
|
404
407
|
});
|
|
405
408
|
const DEFAULT_EFFORT = "medium";
|
|
406
|
-
const lowerMessages = Effect.
|
|
409
|
+
const lowerMessages = Effect.fnUntraced(function* (request, adapter) {
|
|
407
410
|
const input = [];
|
|
408
411
|
const providerMetadataKey = metadataKey(request.model);
|
|
409
412
|
for (const message of request.messages) {
|
|
410
|
-
const
|
|
413
|
+
const rawMetadata = message.providerMetadata?.[providerMetadataKey];
|
|
414
|
+
const metadata = rawMetadata === undefined ? undefined : yield* decodeMessageMetadata(rawMetadata);
|
|
411
415
|
if (message.role === "system") {
|
|
412
416
|
const update = effortUpdate(message);
|
|
413
417
|
if (update) {
|
|
@@ -555,7 +559,7 @@ const lowerMessages = Effect.fn("OpenResponses.lowerMessages")(function* (reques
|
|
|
555
559
|
}
|
|
556
560
|
return input;
|
|
557
561
|
});
|
|
558
|
-
export const lowerConversation = Effect.
|
|
562
|
+
export const lowerConversation = Effect.fnUntraced(function* (request, adapter) {
|
|
559
563
|
const instructions = ProviderShared.joinText(request.system);
|
|
560
564
|
return {
|
|
561
565
|
model: request.model.id,
|
|
@@ -898,7 +902,7 @@ const onReasoningSummaryPartDone = (state, event) => {
|
|
|
898
902
|
NO_EVENTS,
|
|
899
903
|
];
|
|
900
904
|
};
|
|
901
|
-
const onFunctionCallArgumentsDelta = Effect.
|
|
905
|
+
const onFunctionCallArgumentsDelta = Effect.fnUntraced(function* (state, event) {
|
|
902
906
|
if (event.item_id === undefined)
|
|
903
907
|
return [state, NO_EVENTS];
|
|
904
908
|
const tool = state.tools[event.item_id];
|
|
@@ -923,7 +927,7 @@ const onFunctionCallArgumentsDelta = Effect.fn("OpenResponses.onFunctionCallArgu
|
|
|
923
927
|
events.push(...result.events);
|
|
924
928
|
return [{ ...state, lifecycle, tools: result.tools }, events];
|
|
925
929
|
});
|
|
926
|
-
const onOutputItemDone = Effect.
|
|
930
|
+
const onOutputItemDone = Effect.fnUntraced(function* (state, item) {
|
|
927
931
|
if (!item)
|
|
928
932
|
return [state, NO_EVENTS];
|
|
929
933
|
if (item.type === "compaction") {
|
|
@@ -1051,7 +1055,7 @@ const onOutputItemDone = Effect.fn("OpenResponses.onOutputItemDone")(function* (
|
|
|
1051
1055
|
}
|
|
1052
1056
|
return [state, NO_EVENTS];
|
|
1053
1057
|
});
|
|
1054
|
-
const onResponseFinish = Effect.
|
|
1058
|
+
const onResponseFinish = Effect.fnUntraced(function* (state, event) {
|
|
1055
1059
|
let current = state;
|
|
1056
1060
|
const events = [];
|
|
1057
1061
|
if (event.type === "response.completed") {
|
|
@@ -233,7 +233,7 @@ const lowerToolCall = (part, options) => ({
|
|
|
233
233
|
},
|
|
234
234
|
extra_content: decodeExtraContent(part.providerMetadata?.[options.providerMetadataKey]?.extraContent),
|
|
235
235
|
});
|
|
236
|
-
const lowerMedia = Effect.
|
|
236
|
+
const lowerMedia = Effect.fnUntraced(function* (part) {
|
|
237
237
|
// Chat Completions accepts PDFs, and no other documents, as inline `file` parts; file URLs are not supported.
|
|
238
238
|
if (part.media.mediaType.toLowerCase() === "application/pdf")
|
|
239
239
|
return {
|
|
@@ -273,7 +273,7 @@ const lowerReasoningDetail = (detail) => {
|
|
|
273
273
|
return detail;
|
|
274
274
|
};
|
|
275
275
|
const isKimiDetail = (detail) => detail.type === "summary" || detail.type === "encrypted";
|
|
276
|
-
const lowerUserMessage = Effect.
|
|
276
|
+
const lowerUserMessage = Effect.fnUntraced(function* (message, options) {
|
|
277
277
|
const content = [];
|
|
278
278
|
for (const part of message.content) {
|
|
279
279
|
if (part.type === "text") {
|
|
@@ -293,7 +293,7 @@ const lowerUserMessage = Effect.fn("OpenAIChat.lowerUserMessage")(function* (mes
|
|
|
293
293
|
};
|
|
294
294
|
return { role: "user", content };
|
|
295
295
|
});
|
|
296
|
-
const lowerAssistantMessage = Effect.
|
|
296
|
+
const lowerAssistantMessage = Effect.fnUntraced(function* (message, configuredField, requireReasoning, options) {
|
|
297
297
|
const content = [];
|
|
298
298
|
const reasoning = [];
|
|
299
299
|
const toolCalls = [];
|
|
@@ -359,7 +359,7 @@ const lowerAssistantMessage = Effect.fn("OpenAIChat.lowerAssistantMessage")(func
|
|
|
359
359
|
return result;
|
|
360
360
|
return { ...result, [field]: reasoningText };
|
|
361
361
|
});
|
|
362
|
-
const lowerToolMessages = Effect.
|
|
362
|
+
const lowerToolMessages = Effect.fnUntraced(function* (message, options) {
|
|
363
363
|
const messages = [];
|
|
364
364
|
const attachments = [];
|
|
365
365
|
for (const part of message.content) {
|
|
@@ -383,14 +383,14 @@ const toolMessage = (toolCallID, text, cacheControl) => ({
|
|
|
383
383
|
tool_call_id: toolCallID,
|
|
384
384
|
content: cacheControl === undefined ? text : [{ type: "text", text, cache_control: cacheControl }],
|
|
385
385
|
});
|
|
386
|
-
const lowerMessage = Effect.
|
|
386
|
+
const lowerMessage = Effect.fnUntraced(function* (message, reasoningField, requireReasoning, options) {
|
|
387
387
|
if (message.role === "user")
|
|
388
388
|
return [yield* lowerUserMessage(message, options)];
|
|
389
389
|
if (message.role === "assistant")
|
|
390
390
|
return [yield* lowerAssistantMessage(message, reasoningField, requireReasoning, options)];
|
|
391
391
|
return (yield* lowerToolMessages(message, options)).messages;
|
|
392
392
|
});
|
|
393
|
-
const lowerMessages = Effect.
|
|
393
|
+
const lowerMessages = Effect.fnUntraced(function* (request, options) {
|
|
394
394
|
const system = request.system.length === 0
|
|
395
395
|
? []
|
|
396
396
|
: request.system.some((part) => part.cache !== undefined) && options.cacheControl !== undefined
|
|
@@ -674,7 +674,7 @@ export const fromRequest = Effect.fn("OpenAIChat.fromRequest")(function* (reques
|
|
|
674
674
|
// Streaming parsers are small state machines: every event returns a new state
|
|
675
675
|
// plus the common `LLMEvent`s produced by that event. Tool calls are accumulated
|
|
676
676
|
// because OpenAI streams JSON arguments across multiple deltas.
|
|
677
|
-
const mapFinishReason = Effect.
|
|
677
|
+
const mapFinishReason = Effect.fnUntraced(function* (event, reason) {
|
|
678
678
|
switch (reason) {
|
|
679
679
|
case "error":
|
|
680
680
|
return yield* new AIError({
|
|
@@ -981,7 +981,7 @@ const step = (state, event) => Effect.gen(function* () {
|
|
|
981
981
|
events,
|
|
982
982
|
];
|
|
983
983
|
});
|
|
984
|
-
const finishEvents = Effect.
|
|
984
|
+
const finishEvents = Effect.fnUntraced(function* (state) {
|
|
985
985
|
if (state.finishReason === undefined && state.requireFinishReason)
|
|
986
986
|
return yield* new AIError({
|
|
987
987
|
reason: new InvalidProviderOutputError({
|
|
@@ -126,7 +126,7 @@ const info = (format, size) => {
|
|
|
126
126
|
const eventImage = (frame, label, data, format, size) => MediaInput.decodedAsset((message, cause) => route.frameError(message, frame, cause), label, data, `image/${format}`, {
|
|
127
127
|
info: info(format, size),
|
|
128
128
|
});
|
|
129
|
-
const onEvent = Effect.
|
|
129
|
+
const onEvent = Effect.fnUntraced(function* (state, frame) {
|
|
130
130
|
const event = yield* decodeEvent(frame);
|
|
131
131
|
const format = event.output_format;
|
|
132
132
|
if ("partial_image_index" in event) {
|
|
@@ -146,7 +146,7 @@ const onEvent = Effect.fn("OpenAIImages.onEvent")(function* (state, frame) {
|
|
|
146
146
|
[ImageOutputEvent.make({ index: state.completed, image })],
|
|
147
147
|
];
|
|
148
148
|
});
|
|
149
|
-
const onDocument = Effect.
|
|
149
|
+
const onDocument = Effect.fnUntraced(function* (frame) {
|
|
150
150
|
const invalid = (message, cause) => route.frameError(message, frame.document, cause);
|
|
151
151
|
const decoded = yield* decodeDocument(frame.document).pipe(Effect.mapError((cause) => invalid(`${route.name} returned an invalid response`, cause)));
|
|
152
152
|
const format = decoded.output_format ?? frame.requested ?? "png";
|
|
@@ -126,7 +126,7 @@ const nativeImageTool = (tool) => {
|
|
|
126
126
|
const native = nativeImageToolInput(tool);
|
|
127
127
|
return Schema.is(OpenAIResponsesImageGenerationTool)(native) ? native : undefined;
|
|
128
128
|
};
|
|
129
|
-
const lowerTool = Effect.
|
|
129
|
+
const lowerTool = Effect.fnUntraced(function* (tool) {
|
|
130
130
|
const native = nativeImageToolInput(tool);
|
|
131
131
|
if (native !== undefined) {
|
|
132
132
|
if (Schema.is(OpenAIResponsesImageGenerationTool)(native))
|
|
@@ -137,7 +137,7 @@ const lowerTool = Effect.fn("OpenAIResponses.lowerTool")(function* (tool) {
|
|
|
137
137
|
});
|
|
138
138
|
// Native namespaces hold only function tools, so deeper levels flatten into
|
|
139
139
|
// the leaf names the same way non-native protocols flatten the whole tree.
|
|
140
|
-
const lowerToolEntry = Effect.
|
|
140
|
+
const lowerToolEntry = Effect.fnUntraced(function* (tool) {
|
|
141
141
|
if (tool.type === "tool")
|
|
142
142
|
return yield* lowerTool(tool);
|
|
143
143
|
// OpenAI requires a namespace description; fall back to a generic one so a
|
|
@@ -191,7 +191,7 @@ const checkpointBody = {
|
|
|
191
191
|
};
|
|
192
192
|
}),
|
|
193
193
|
};
|
|
194
|
-
const hostedToolResult = Effect.
|
|
194
|
+
const hostedToolResult = Effect.fnUntraced(function* (item) {
|
|
195
195
|
const isError = item.error !== undefined && item.error !== null;
|
|
196
196
|
if (item.type === "image_generation_call" && item.result) {
|
|
197
197
|
yield* Effect.fromResult(Encoding.decodeBase64(item.result)).pipe(Effect.mapError((cause) => ProviderShared.eventError(ADAPTER, "OpenAI Responses returned invalid image base64", undefined, cause)));
|
|
@@ -51,7 +51,7 @@ const fromRequest = Effect.fn("OpenAISpeech.fromRequest")(function* (request) {
|
|
|
51
51
|
// 6. Stream parsing
|
|
52
52
|
// ---------------------------------------------------------------------------
|
|
53
53
|
const isSse = (body) => body.type === "json" && body.value.stream_format === "sse";
|
|
54
|
-
const onEvent = Effect.
|
|
54
|
+
const onEvent = Effect.fnUntraced(function* (state, frame) {
|
|
55
55
|
const event = yield* decodeEvent(frame);
|
|
56
56
|
if (event.type === "speech.audio.delta")
|
|
57
57
|
return SpeechStream.delta(state, event.audio);
|
|
@@ -131,7 +131,7 @@ const segment = (value) => ({
|
|
|
131
131
|
endSeconds: value.end,
|
|
132
132
|
speaker: value.speaker,
|
|
133
133
|
});
|
|
134
|
-
const onEvent = Effect.
|
|
134
|
+
const onEvent = Effect.fnUntraced(function* (state, frame) {
|
|
135
135
|
if (!EVENT_TYPES.has((yield* decodeEventType(frame)).type))
|
|
136
136
|
return [state, []];
|
|
137
137
|
const event = yield* decodeEvent(frame);
|
package/dist/protocols/shared.js
CHANGED
|
@@ -108,7 +108,7 @@ export const wrapSystemUpdate = (parts) => `<system-update>\n${escapeSystemUpdat
|
|
|
108
108
|
* raw retrieved, tool, or web content into privileged updates: keep untrusted
|
|
109
109
|
* data in ordinary user/tool messages instead.
|
|
110
110
|
*/
|
|
111
|
-
export const systemUpdateText = Effect.
|
|
111
|
+
export const systemUpdateText = Effect.fnUntraced(function* (route, message) {
|
|
112
112
|
const content = [];
|
|
113
113
|
for (const part of message.content) {
|
|
114
114
|
if (!supportsContent(part, ["text"]))
|
|
@@ -118,7 +118,7 @@ export const systemUpdateText = Effect.fn("ProviderShared.systemUpdateText")(fun
|
|
|
118
118
|
return content;
|
|
119
119
|
});
|
|
120
120
|
/** Lower an unsupported privileged update into visible, in-order user text. */
|
|
121
|
-
export const wrappedSystemUpdate = Effect.
|
|
121
|
+
export const wrappedSystemUpdate = Effect.fnUntraced(function* (route, message) {
|
|
122
122
|
const content = yield* systemUpdateText(route, message);
|
|
123
123
|
return { type: "text", text: wrapSystemUpdate(content), cache: content.at(-1)?.cache };
|
|
124
124
|
});
|
|
@@ -30,7 +30,7 @@ const Started = Schema.Struct({ id: Schema.String });
|
|
|
30
30
|
/** `sd3.5-*` ids share the `sd3` endpoint and select the model with a form field. */
|
|
31
31
|
const endpoint = (model) => (model.startsWith("sd3") ? "sd3" : model);
|
|
32
32
|
const RESERVED_FORM_FIELDS = new Set(["image", "prompt", "mode", "model"]);
|
|
33
|
-
const form = Effect.
|
|
33
|
+
const form = Effect.fnUntraced(function* (identity, fields, native, source) {
|
|
34
34
|
if ((source?.length ?? 0) > 1)
|
|
35
35
|
return yield* identity.unsupported("media.images", `${identity.name} takes one source image`);
|
|
36
36
|
const body = new FormData();
|
|
@@ -61,7 +61,7 @@ function documentName(filename, names) {
|
|
|
61
61
|
names.add(name);
|
|
62
62
|
return name;
|
|
63
63
|
}
|
|
64
|
-
const mediaBase64 = Effect.
|
|
64
|
+
const mediaBase64 = Effect.fnUntraced(function* (part) {
|
|
65
65
|
const media = yield* ProviderShared.requireInlineMedia("Bedrock Converse", part.media);
|
|
66
66
|
const bytes = yield* Effect.fromResult(Encoding.decodeBase64(media.base64)).pipe(Effect.mapError((cause) => ProviderShared.invalidRequest("Bedrock Converse media data must be valid base64", cause)));
|
|
67
67
|
return Encoding.encodeBase64(bytes);
|
|
@@ -71,7 +71,7 @@ const mediaBase64 = Effect.fn("BedrockMedia.mediaBase64")(function* (part) {
|
|
|
71
71
|
// document block. Image MIME types not in `IMAGE_FORMATS` (e.g. `image/svg+xml`)
|
|
72
72
|
// get an image-specific error so the caller knows it's a format-support issue,
|
|
73
73
|
// not a kind-detection issue.
|
|
74
|
-
export const lower = Effect.
|
|
74
|
+
export const lower = Effect.fnUntraced(function* (part, documentNames) {
|
|
75
75
|
const mime = part.media.mediaType.toLowerCase();
|
|
76
76
|
const imageFormat = IMAGE_FORMATS[mime];
|
|
77
77
|
if (imageFormat) {
|
|
@@ -4,7 +4,7 @@ import { Protocol } from "../../route/protocol.js";
|
|
|
4
4
|
import { CompactionCheckpointResponse, LLMEvent, LLMRequest } from "../../schema/index.js";
|
|
5
5
|
import { OpenResponses } from "../open-responses.js";
|
|
6
6
|
import { ProviderShared } from "../shared.js";
|
|
7
|
-
const onOutputItem = Effect.
|
|
7
|
+
const onOutputItem = Effect.fnUntraced(function* (state, input) {
|
|
8
8
|
const event = OpenResponses.normalize(state.parser, input);
|
|
9
9
|
const item = event.item;
|
|
10
10
|
if (!item)
|
|
@@ -49,7 +49,7 @@ export const make = (body) => Effect.fn("ResponsesCheckpoint.execute")(function*
|
|
|
49
49
|
checkpoints: {},
|
|
50
50
|
}),
|
|
51
51
|
terminal: OpenResponses.terminal,
|
|
52
|
-
step: Effect.
|
|
52
|
+
step: Effect.fnUntraced(function* (state, event) {
|
|
53
53
|
if (event.response?.id && state.responseID && event.response.id !== state.responseID)
|
|
54
54
|
return yield* ProviderShared.eventError(source.id, "Compaction response ID changed during execution");
|
|
55
55
|
if (event.type === "response.created")
|
|
@@ -3,7 +3,7 @@ import { LLMEvent } from "../../schema/index.js";
|
|
|
3
3
|
import { OpenResponses } from "../open-responses.js";
|
|
4
4
|
import { Lifecycle } from "./lifecycle.js";
|
|
5
5
|
export const isItem = (item, tools) => item.type in tools;
|
|
6
|
-
export const onDone = Effect.
|
|
6
|
+
export const onDone = Effect.fnUntraced(function* (state, item, tools) {
|
|
7
7
|
const tool = tools[item.type];
|
|
8
8
|
if (!tool)
|
|
9
9
|
return [state, []];
|
package/dist/route/framing.js
CHANGED
|
@@ -14,32 +14,32 @@ export const sseFraming = (bytes, events, includeDone = false) => bytes.pipe(Str
|
|
|
14
14
|
return {
|
|
15
15
|
output,
|
|
16
16
|
parser: makeParser((event) => {
|
|
17
|
-
if (event._tag === "Event"
|
|
18
|
-
|
|
17
|
+
if (event._tag === "Event" &&
|
|
18
|
+
(events === undefined || events.has(event.event)) &&
|
|
19
|
+
event.data.length > 0 &&
|
|
20
|
+
// Some OpenAI-compatible proxies serialize an empty flush as a bare
|
|
21
|
+
// `data: null`, between events or after `[DONE]`. No protocol has a
|
|
22
|
+
// null event, so it carries nothing and must not abort the stream.
|
|
23
|
+
event.data !== "null" &&
|
|
24
|
+
// Vertex AI partner models (e.g. `xai/grok-4.6`) send their SSE
|
|
25
|
+
// keepalive comment as `data: : keepalive` while reasoning.
|
|
26
|
+
event.data !== ": keepalive" &&
|
|
27
|
+
(event.data !== "[DONE]" || includeDone || (events !== undefined && event.event !== "message")))
|
|
28
|
+
output.push(event.data);
|
|
19
29
|
}),
|
|
20
30
|
};
|
|
21
|
-
}, (state, chunk) =>
|
|
31
|
+
}, (state, chunk) => {
|
|
22
32
|
const error = state.parser.feed(chunk);
|
|
23
|
-
if (error)
|
|
24
|
-
return
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
})), Stream.filter((event) => (events === undefined || events.has(event.event)) &&
|
|
34
|
-
event.data.length > 0 &&
|
|
35
|
-
// Some OpenAI-compatible proxies serialize an empty flush as a bare
|
|
36
|
-
// `data: null`, between events or after `[DONE]`. No protocol has a
|
|
37
|
-
// null event, so it carries nothing and must not abort the stream.
|
|
38
|
-
event.data !== "null" &&
|
|
39
|
-
// Vertex AI partner models (e.g. `xai/grok-4.6`) send their SSE
|
|
40
|
-
// keepalive comment as `data: : keepalive` while reasoning.
|
|
41
|
-
event.data !== ": keepalive" &&
|
|
42
|
-
(event.data !== "[DONE]" || includeDone || (events !== undefined && event.event !== "message"))), Stream.map((event) => event.data));
|
|
33
|
+
if (!error)
|
|
34
|
+
return Effect.succeed([state, state.output.splice(0)]);
|
|
35
|
+
const reason = new InvalidProviderOutputError({
|
|
36
|
+
route: "sse",
|
|
37
|
+
message: error.message,
|
|
38
|
+
body: chunk,
|
|
39
|
+
cause: error,
|
|
40
|
+
});
|
|
41
|
+
return Effect.fail(new AIError({ reason }));
|
|
42
|
+
}));
|
|
43
43
|
/** Server-Sent Events framing. Used by every JSON-streaming HTTP provider. */
|
|
44
44
|
export const sse = { id: "sse", frame: sseFraming };
|
|
45
45
|
/** Server-Sent Events framing that retains the conventional `[DONE]` sentinel. */
|
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-20538",
|
|
4
4
|
"name": "@opencode/ai",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@clack/prompts": "1.0.0-alpha.1",
|
|
36
36
|
"@effect/platform-node": "4.0.0-rc.112",
|
|
37
|
-
"@opencode/http-recorder": "0.0.0-dev-
|
|
37
|
+
"@opencode/http-recorder": "0.0.0-dev-20538",
|
|
38
38
|
"@tsconfig/bun": "1.0.9",
|
|
39
39
|
"@types/bun": "1.4.0",
|
|
40
40
|
"@typescript/native-preview": "7.0.0-dev.20251207.1",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"@aws-sdk/credential-providers": "3.1057.0",
|
|
45
45
|
"@smithy/eventstream-codec": "4.2.14",
|
|
46
46
|
"@smithy/util-utf8": "4.2.2",
|
|
47
|
-
"@opencode/schema": "0.0.0-dev-
|
|
47
|
+
"@opencode/schema": "0.0.0-dev-20538",
|
|
48
48
|
"aws4fetch": "1.0.20",
|
|
49
49
|
"effect": "4.0.0-rc.112",
|
|
50
50
|
"google-auth-library": "10.5.0"
|