@cloudflare/tanstack-ai 0.0.0 → 0.1.0
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/LICENSE +21 -0
- package/README.md +282 -0
- package/dist/_tsup-dts-rollup.d.cts +820 -0
- package/dist/_tsup-dts-rollup.d.ts +820 -0
- package/dist/adapters/anthropic.cjs +13 -0
- package/dist/adapters/anthropic.cjs.map +1 -0
- package/dist/adapters/anthropic.d.cts +5 -0
- package/dist/adapters/anthropic.d.ts +5 -0
- package/dist/adapters/anthropic.js +13 -0
- package/dist/adapters/anthropic.js.map +1 -0
- package/dist/adapters/gemini.cjs +22 -0
- package/dist/adapters/gemini.cjs.map +1 -0
- package/dist/adapters/gemini.d.cts +14 -0
- package/dist/adapters/gemini.d.ts +14 -0
- package/dist/adapters/gemini.js +22 -0
- package/dist/adapters/gemini.js.map +1 -0
- package/dist/adapters/grok.cjs +17 -0
- package/dist/adapters/grok.cjs.map +1 -0
- package/dist/adapters/grok.d.cts +9 -0
- package/dist/adapters/grok.d.ts +9 -0
- package/dist/adapters/grok.js +17 -0
- package/dist/adapters/grok.js.map +1 -0
- package/dist/adapters/openai.cjs +29 -0
- package/dist/adapters/openai.cjs.map +1 -0
- package/dist/adapters/openai.d.cts +17 -0
- package/dist/adapters/openai.d.ts +17 -0
- package/dist/adapters/openai.js +29 -0
- package/dist/adapters/openai.js.map +1 -0
- package/dist/adapters/openrouter.cjs +13 -0
- package/dist/adapters/openrouter.cjs.map +1 -0
- package/dist/adapters/openrouter.d.cts +7 -0
- package/dist/adapters/openrouter.d.ts +7 -0
- package/dist/adapters/openrouter.js +13 -0
- package/dist/adapters/openrouter.js.map +1 -0
- package/dist/adapters/workers-ai-image.cjs +13 -0
- package/dist/adapters/workers-ai-image.cjs.map +1 -0
- package/dist/adapters/workers-ai-image.d.cts +3 -0
- package/dist/adapters/workers-ai-image.d.ts +3 -0
- package/dist/adapters/workers-ai-image.js +13 -0
- package/dist/adapters/workers-ai-image.js.map +1 -0
- package/dist/adapters/workers-ai-summarize.cjs +12 -0
- package/dist/adapters/workers-ai-summarize.cjs.map +1 -0
- package/dist/adapters/workers-ai-summarize.d.cts +3 -0
- package/dist/adapters/workers-ai-summarize.d.ts +3 -0
- package/dist/adapters/workers-ai-summarize.js +12 -0
- package/dist/adapters/workers-ai-summarize.js.map +1 -0
- package/dist/adapters/workers-ai-transcription.cjs +13 -0
- package/dist/adapters/workers-ai-transcription.cjs.map +1 -0
- package/dist/adapters/workers-ai-transcription.d.cts +3 -0
- package/dist/adapters/workers-ai-transcription.d.ts +3 -0
- package/dist/adapters/workers-ai-transcription.js +13 -0
- package/dist/adapters/workers-ai-transcription.js.map +1 -0
- package/dist/adapters/workers-ai-tts.cjs +13 -0
- package/dist/adapters/workers-ai-tts.cjs.map +1 -0
- package/dist/adapters/workers-ai-tts.d.cts +3 -0
- package/dist/adapters/workers-ai-tts.d.ts +3 -0
- package/dist/adapters/workers-ai-tts.js +13 -0
- package/dist/adapters/workers-ai-tts.js.map +1 -0
- package/dist/adapters/workers-ai.cjs +11 -0
- package/dist/adapters/workers-ai.cjs.map +1 -0
- package/dist/adapters/workers-ai.d.cts +3 -0
- package/dist/adapters/workers-ai.d.ts +3 -0
- package/dist/adapters/workers-ai.js +11 -0
- package/dist/adapters/workers-ai.js.map +1 -0
- package/dist/chunk-2AJ6LV2D.js +84 -0
- package/dist/chunk-2AJ6LV2D.js.map +1 -0
- package/dist/chunk-2VII5BK2.js +42 -0
- package/dist/chunk-2VII5BK2.js.map +1 -0
- package/dist/chunk-3VQDXJLW.cjs +46 -0
- package/dist/chunk-3VQDXJLW.cjs.map +1 -0
- package/dist/chunk-4DE2IREA.cjs +8 -0
- package/dist/chunk-4DE2IREA.cjs.map +1 -0
- package/dist/chunk-7AEFXYJG.js +65 -0
- package/dist/chunk-7AEFXYJG.js.map +1 -0
- package/dist/chunk-7HSUHP63.cjs +42 -0
- package/dist/chunk-7HSUHP63.cjs.map +1 -0
- package/dist/chunk-7T4CVHKD.cjs +84 -0
- package/dist/chunk-7T4CVHKD.cjs.map +1 -0
- package/dist/chunk-BD4CRW3Q.js +389 -0
- package/dist/chunk-BD4CRW3Q.js.map +1 -0
- package/dist/chunk-BPWGWJJV.cjs +389 -0
- package/dist/chunk-BPWGWJJV.cjs.map +1 -0
- package/dist/chunk-F5YJMXZR.js +315 -0
- package/dist/chunk-F5YJMXZR.js.map +1 -0
- package/dist/chunk-GOU66I5T.cjs +315 -0
- package/dist/chunk-GOU66I5T.cjs.map +1 -0
- package/dist/chunk-IWZJCLOE.cjs +31 -0
- package/dist/chunk-IWZJCLOE.cjs.map +1 -0
- package/dist/chunk-LBYDBPHY.cjs +109 -0
- package/dist/chunk-LBYDBPHY.cjs.map +1 -0
- package/dist/chunk-LIUHRGEK.cjs +96 -0
- package/dist/chunk-LIUHRGEK.cjs.map +1 -0
- package/dist/chunk-M6NETFDR.cjs +48 -0
- package/dist/chunk-M6NETFDR.cjs.map +1 -0
- package/dist/chunk-O2C4CR57.cjs +54 -0
- package/dist/chunk-O2C4CR57.cjs.map +1 -0
- package/dist/chunk-PLTFCUMO.js +216 -0
- package/dist/chunk-PLTFCUMO.js.map +1 -0
- package/dist/chunk-QRHKPL75.js +54 -0
- package/dist/chunk-QRHKPL75.js.map +1 -0
- package/dist/chunk-RGDUK5KX.cjs +65 -0
- package/dist/chunk-RGDUK5KX.cjs.map +1 -0
- package/dist/chunk-RQT7M6MU.js +96 -0
- package/dist/chunk-RQT7M6MU.js.map +1 -0
- package/dist/chunk-U5YJQYLZ.cjs +57 -0
- package/dist/chunk-U5YJQYLZ.cjs.map +1 -0
- package/dist/chunk-V6TY7KAL.js +8 -0
- package/dist/chunk-V6TY7KAL.js.map +1 -0
- package/dist/chunk-VTDJEUFS.js +57 -0
- package/dist/chunk-VTDJEUFS.js.map +1 -0
- package/dist/chunk-XCNU7EEC.js +48 -0
- package/dist/chunk-XCNU7EEC.js.map +1 -0
- package/dist/chunk-XI2BOYEI.js +109 -0
- package/dist/chunk-XI2BOYEI.js.map +1 -0
- package/dist/chunk-XKSFDPDY.cjs +216 -0
- package/dist/chunk-XKSFDPDY.cjs.map +1 -0
- package/dist/chunk-XU7YEPML.js +46 -0
- package/dist/chunk-XU7YEPML.js.map +1 -0
- package/dist/chunk-YIA5B3QT.js +31 -0
- package/dist/chunk-YIA5B3QT.js.map +1 -0
- package/dist/index.cjs +97 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +64 -0
- package/dist/index.d.ts +64 -0
- package/dist/index.js +97 -0
- package/dist/index.js.map +1 -0
- package/package.json +119 -10
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createGatewayFetch,
|
|
3
|
+
createWorkersAiBindingFetch,
|
|
4
|
+
isDirectBindingConfig,
|
|
5
|
+
isDirectCredentialsConfig
|
|
6
|
+
} from "./chunk-F5YJMXZR.js";
|
|
7
|
+
import {
|
|
8
|
+
__publicField
|
|
9
|
+
} from "./chunk-V6TY7KAL.js";
|
|
10
|
+
|
|
11
|
+
// src/adapters/workers-ai.ts
|
|
12
|
+
import {
|
|
13
|
+
BaseTextAdapter
|
|
14
|
+
} from "@tanstack/ai/adapters";
|
|
15
|
+
import OpenAI from "openai";
|
|
16
|
+
function buildWorkersAiClient(config) {
|
|
17
|
+
if (isDirectBindingConfig(config)) {
|
|
18
|
+
return new OpenAI({
|
|
19
|
+
apiKey: "unused",
|
|
20
|
+
fetch: createWorkersAiBindingFetch(config.binding)
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
if (isDirectCredentialsConfig(config)) {
|
|
24
|
+
return new OpenAI({
|
|
25
|
+
baseURL: `https://api.cloudflare.com/client/v4/accounts/${config.accountId}/ai/v1`,
|
|
26
|
+
apiKey: config.apiKey
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
const gatewayConfig = config;
|
|
30
|
+
return new OpenAI({
|
|
31
|
+
fetch: createGatewayFetch("workers-ai", gatewayConfig),
|
|
32
|
+
apiKey: gatewayConfig.apiKey ?? "unused"
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
function extractTextContent(content) {
|
|
36
|
+
if (content === null) return "";
|
|
37
|
+
if (typeof content === "string") return content;
|
|
38
|
+
return content.filter((p) => p.type === "text").map((p) => p.content || "").join("");
|
|
39
|
+
}
|
|
40
|
+
function buildOpenAIMessages(systemPrompts, messages, options) {
|
|
41
|
+
const includeTools = options?.includeToolMessages ?? true;
|
|
42
|
+
const openAIMessages = [];
|
|
43
|
+
if (systemPrompts && systemPrompts.length > 0) {
|
|
44
|
+
openAIMessages.push({
|
|
45
|
+
role: "system",
|
|
46
|
+
content: systemPrompts.join("\n")
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
for (const message of messages) {
|
|
50
|
+
if (message.role === "user") {
|
|
51
|
+
openAIMessages.push({
|
|
52
|
+
role: "user",
|
|
53
|
+
content: extractTextContent(message.content)
|
|
54
|
+
});
|
|
55
|
+
} else if (message.role === "assistant") {
|
|
56
|
+
const assistantMessage = {
|
|
57
|
+
role: "assistant",
|
|
58
|
+
content: extractTextContent(message.content)
|
|
59
|
+
};
|
|
60
|
+
if (includeTools && message.toolCalls && message.toolCalls.length > 0) {
|
|
61
|
+
assistantMessage.tool_calls = message.toolCalls.map((tc) => ({
|
|
62
|
+
id: tc.id,
|
|
63
|
+
type: "function",
|
|
64
|
+
function: {
|
|
65
|
+
name: tc.function.name,
|
|
66
|
+
arguments: tc.function.arguments
|
|
67
|
+
}
|
|
68
|
+
}));
|
|
69
|
+
}
|
|
70
|
+
openAIMessages.push(assistantMessage);
|
|
71
|
+
} else if (includeTools && message.role === "tool") {
|
|
72
|
+
let toolContent;
|
|
73
|
+
if (typeof message.content === "string") {
|
|
74
|
+
try {
|
|
75
|
+
JSON.parse(message.content);
|
|
76
|
+
toolContent = message.content;
|
|
77
|
+
} catch {
|
|
78
|
+
toolContent = JSON.stringify(message.content);
|
|
79
|
+
}
|
|
80
|
+
} else {
|
|
81
|
+
toolContent = JSON.stringify(message.content);
|
|
82
|
+
}
|
|
83
|
+
openAIMessages.push({
|
|
84
|
+
role: "tool",
|
|
85
|
+
tool_call_id: message.toolCallId || `tool_${crypto.randomUUID().slice(0, 8)}`,
|
|
86
|
+
content: toolContent
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return openAIMessages;
|
|
91
|
+
}
|
|
92
|
+
function buildOpenAITools(tools) {
|
|
93
|
+
if (!tools) return void 0;
|
|
94
|
+
return tools.map((tool) => ({
|
|
95
|
+
type: "function",
|
|
96
|
+
function: {
|
|
97
|
+
name: tool.name,
|
|
98
|
+
description: tool.description,
|
|
99
|
+
parameters: tool.inputSchema
|
|
100
|
+
}
|
|
101
|
+
}));
|
|
102
|
+
}
|
|
103
|
+
function generateId(prefix) {
|
|
104
|
+
return `${prefix}-${crypto.randomUUID()}`;
|
|
105
|
+
}
|
|
106
|
+
var WorkersAiTextAdapter = class extends BaseTextAdapter {
|
|
107
|
+
constructor(model, config) {
|
|
108
|
+
super({ apiKey: "unused" }, model);
|
|
109
|
+
__publicField(this, "name", "workers-ai");
|
|
110
|
+
__publicField(this, "client");
|
|
111
|
+
this.client = buildWorkersAiClient(config);
|
|
112
|
+
}
|
|
113
|
+
async *chatStream(options) {
|
|
114
|
+
const { systemPrompts, messages, tools, temperature, model } = options;
|
|
115
|
+
const openAIMessages = buildOpenAIMessages(systemPrompts, messages);
|
|
116
|
+
const openAITools = buildOpenAITools(tools);
|
|
117
|
+
const timestamp = Date.now();
|
|
118
|
+
const runId = generateId("workers-ai");
|
|
119
|
+
const messageId = generateId("workers-ai");
|
|
120
|
+
let hasEmittedRunStarted = false;
|
|
121
|
+
let hasEmittedTextMessageStart = false;
|
|
122
|
+
let accumulatedContent = "";
|
|
123
|
+
let hasEmittedStepStarted = false;
|
|
124
|
+
let accumulatedReasoning = "";
|
|
125
|
+
const stepId = generateId("workers-ai-step");
|
|
126
|
+
let hasReceivedFinishReason = false;
|
|
127
|
+
const toolCallsInProgress = /* @__PURE__ */ new Map();
|
|
128
|
+
try {
|
|
129
|
+
const stream = await this.client.chat.completions.create({
|
|
130
|
+
model: model ?? this.model,
|
|
131
|
+
messages: openAIMessages,
|
|
132
|
+
tools: openAITools,
|
|
133
|
+
temperature,
|
|
134
|
+
stream: true,
|
|
135
|
+
stream_options: { include_usage: true }
|
|
136
|
+
});
|
|
137
|
+
for await (const chunk of stream) {
|
|
138
|
+
if (!chunk.choices || chunk.choices.length === 0) continue;
|
|
139
|
+
const choice = chunk.choices[0];
|
|
140
|
+
if (!choice) continue;
|
|
141
|
+
if (!hasEmittedRunStarted) {
|
|
142
|
+
hasEmittedRunStarted = true;
|
|
143
|
+
yield {
|
|
144
|
+
type: "RUN_STARTED",
|
|
145
|
+
runId,
|
|
146
|
+
model: chunk.model || model || this.model,
|
|
147
|
+
timestamp
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
const delta = choice.delta;
|
|
151
|
+
const reasoningContent = delta.reasoning_content;
|
|
152
|
+
if (reasoningContent) {
|
|
153
|
+
if (!hasEmittedStepStarted) {
|
|
154
|
+
hasEmittedStepStarted = true;
|
|
155
|
+
yield {
|
|
156
|
+
type: "STEP_STARTED",
|
|
157
|
+
stepId,
|
|
158
|
+
stepType: "thinking",
|
|
159
|
+
model: chunk.model || model || this.model,
|
|
160
|
+
timestamp
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
accumulatedReasoning += reasoningContent;
|
|
164
|
+
yield {
|
|
165
|
+
type: "STEP_FINISHED",
|
|
166
|
+
stepId,
|
|
167
|
+
delta: reasoningContent,
|
|
168
|
+
content: accumulatedReasoning,
|
|
169
|
+
model: chunk.model || model || this.model,
|
|
170
|
+
timestamp
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
if (delta.content) {
|
|
174
|
+
if (!hasEmittedTextMessageStart) {
|
|
175
|
+
hasEmittedTextMessageStart = true;
|
|
176
|
+
yield {
|
|
177
|
+
type: "TEXT_MESSAGE_START",
|
|
178
|
+
messageId,
|
|
179
|
+
model: chunk.model || model || this.model,
|
|
180
|
+
timestamp,
|
|
181
|
+
role: "assistant"
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
accumulatedContent += delta.content;
|
|
185
|
+
yield {
|
|
186
|
+
type: "TEXT_MESSAGE_CONTENT",
|
|
187
|
+
messageId,
|
|
188
|
+
model: chunk.model || model || this.model,
|
|
189
|
+
timestamp,
|
|
190
|
+
delta: delta.content,
|
|
191
|
+
content: accumulatedContent
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
if (delta.tool_calls) {
|
|
195
|
+
for (const toolCallDelta of delta.tool_calls) {
|
|
196
|
+
const index = toolCallDelta.index;
|
|
197
|
+
if (!toolCallsInProgress.has(index)) {
|
|
198
|
+
toolCallsInProgress.set(index, {
|
|
199
|
+
id: toolCallDelta.id || "",
|
|
200
|
+
name: toolCallDelta.function?.name || "",
|
|
201
|
+
arguments: "",
|
|
202
|
+
started: false
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
const toolCall = toolCallsInProgress.get(index);
|
|
206
|
+
if (toolCallDelta.id) {
|
|
207
|
+
toolCall.id = toolCallDelta.id;
|
|
208
|
+
}
|
|
209
|
+
if (toolCallDelta.function?.name) {
|
|
210
|
+
toolCall.name = toolCallDelta.function.name;
|
|
211
|
+
}
|
|
212
|
+
if (toolCallDelta.function?.arguments) {
|
|
213
|
+
toolCall.arguments += toolCallDelta.function.arguments;
|
|
214
|
+
}
|
|
215
|
+
if (toolCall.id && toolCall.name && !toolCall.started) {
|
|
216
|
+
toolCall.started = true;
|
|
217
|
+
yield {
|
|
218
|
+
type: "TOOL_CALL_START",
|
|
219
|
+
toolCallId: toolCall.id,
|
|
220
|
+
toolName: toolCall.name,
|
|
221
|
+
model: chunk.model || model || this.model,
|
|
222
|
+
timestamp,
|
|
223
|
+
index
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
if (toolCallDelta.function?.arguments && toolCall.started) {
|
|
227
|
+
yield {
|
|
228
|
+
type: "TOOL_CALL_ARGS",
|
|
229
|
+
toolCallId: toolCall.id,
|
|
230
|
+
model: chunk.model || model || this.model,
|
|
231
|
+
timestamp,
|
|
232
|
+
delta: toolCallDelta.function.arguments
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
if (choice.finish_reason) {
|
|
238
|
+
hasReceivedFinishReason = true;
|
|
239
|
+
if (choice.finish_reason === "tool_calls" || toolCallsInProgress.size > 0) {
|
|
240
|
+
for (const [, toolCall] of toolCallsInProgress) {
|
|
241
|
+
let parsedInput = {};
|
|
242
|
+
try {
|
|
243
|
+
parsedInput = toolCall.arguments ? JSON.parse(toolCall.arguments) : {};
|
|
244
|
+
} catch {
|
|
245
|
+
parsedInput = {};
|
|
246
|
+
}
|
|
247
|
+
yield {
|
|
248
|
+
type: "TOOL_CALL_END",
|
|
249
|
+
toolCallId: toolCall.id,
|
|
250
|
+
toolName: toolCall.name,
|
|
251
|
+
model: chunk.model || model || this.model,
|
|
252
|
+
timestamp,
|
|
253
|
+
input: parsedInput
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
const computedFinishReason = choice.finish_reason === "tool_calls" || choice.finish_reason === "function_call" || toolCallsInProgress.size > 0 ? "tool_calls" : choice.finish_reason;
|
|
258
|
+
if (hasEmittedTextMessageStart) {
|
|
259
|
+
yield {
|
|
260
|
+
type: "TEXT_MESSAGE_END",
|
|
261
|
+
messageId,
|
|
262
|
+
model: chunk.model || model || this.model,
|
|
263
|
+
timestamp
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
yield {
|
|
267
|
+
type: "RUN_FINISHED",
|
|
268
|
+
runId,
|
|
269
|
+
model: chunk.model || model || this.model,
|
|
270
|
+
timestamp,
|
|
271
|
+
usage: chunk.usage ? {
|
|
272
|
+
promptTokens: chunk.usage.prompt_tokens,
|
|
273
|
+
completionTokens: chunk.usage.completion_tokens,
|
|
274
|
+
totalTokens: chunk.usage.total_tokens
|
|
275
|
+
} : void 0,
|
|
276
|
+
finishReason: computedFinishReason
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
if (hasEmittedRunStarted && !hasReceivedFinishReason) {
|
|
281
|
+
for (const [, toolCall] of toolCallsInProgress) {
|
|
282
|
+
if (toolCall.started) {
|
|
283
|
+
let parsedInput = {};
|
|
284
|
+
try {
|
|
285
|
+
parsedInput = toolCall.arguments ? JSON.parse(toolCall.arguments) : {};
|
|
286
|
+
} catch {
|
|
287
|
+
parsedInput = {};
|
|
288
|
+
}
|
|
289
|
+
yield {
|
|
290
|
+
type: "TOOL_CALL_END",
|
|
291
|
+
toolCallId: toolCall.id,
|
|
292
|
+
toolName: toolCall.name,
|
|
293
|
+
model: model ?? this.model,
|
|
294
|
+
timestamp,
|
|
295
|
+
input: parsedInput
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
if (hasEmittedTextMessageStart) {
|
|
300
|
+
yield {
|
|
301
|
+
type: "TEXT_MESSAGE_END",
|
|
302
|
+
messageId,
|
|
303
|
+
model: model ?? this.model,
|
|
304
|
+
timestamp
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
yield {
|
|
308
|
+
type: "RUN_FINISHED",
|
|
309
|
+
runId,
|
|
310
|
+
model: model ?? this.model,
|
|
311
|
+
timestamp,
|
|
312
|
+
finishReason: "stop"
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
} catch (error) {
|
|
316
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
317
|
+
const code = error instanceof Error ? error.code : void 0;
|
|
318
|
+
if (!hasEmittedRunStarted) {
|
|
319
|
+
yield {
|
|
320
|
+
type: "RUN_STARTED",
|
|
321
|
+
runId,
|
|
322
|
+
model: model ?? this.model,
|
|
323
|
+
timestamp
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
yield {
|
|
327
|
+
type: "RUN_ERROR",
|
|
328
|
+
runId,
|
|
329
|
+
model: model ?? this.model,
|
|
330
|
+
timestamp,
|
|
331
|
+
error: {
|
|
332
|
+
message: message || "Unknown error",
|
|
333
|
+
code
|
|
334
|
+
}
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
async structuredOutput(options) {
|
|
339
|
+
const { outputSchema, chatOptions } = options;
|
|
340
|
+
const { systemPrompts, messages, temperature, model } = chatOptions;
|
|
341
|
+
const openAIMessages = buildOpenAIMessages(systemPrompts, messages, {
|
|
342
|
+
includeToolMessages: false
|
|
343
|
+
});
|
|
344
|
+
const response = await this.client.chat.completions.create({
|
|
345
|
+
model: model ?? this.model,
|
|
346
|
+
messages: openAIMessages,
|
|
347
|
+
temperature,
|
|
348
|
+
stream: false,
|
|
349
|
+
response_format: {
|
|
350
|
+
type: "json_schema",
|
|
351
|
+
json_schema: {
|
|
352
|
+
name: "structured_output",
|
|
353
|
+
strict: true,
|
|
354
|
+
schema: outputSchema
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
});
|
|
358
|
+
const choice = response.choices?.[0];
|
|
359
|
+
if (!choice) {
|
|
360
|
+
throw new Error(
|
|
361
|
+
`Workers AI structured output returned no choices: ${JSON.stringify(response)}`
|
|
362
|
+
);
|
|
363
|
+
}
|
|
364
|
+
const rawContent = choice.message?.content ?? "";
|
|
365
|
+
let data;
|
|
366
|
+
let rawText;
|
|
367
|
+
if (typeof rawContent === "string") {
|
|
368
|
+
rawText = rawContent;
|
|
369
|
+
try {
|
|
370
|
+
data = JSON.parse(rawText);
|
|
371
|
+
} catch {
|
|
372
|
+
data = rawText;
|
|
373
|
+
}
|
|
374
|
+
} else {
|
|
375
|
+
data = rawContent;
|
|
376
|
+
rawText = JSON.stringify(rawContent);
|
|
377
|
+
}
|
|
378
|
+
return { data, rawText };
|
|
379
|
+
}
|
|
380
|
+
};
|
|
381
|
+
function createWorkersAiChat(model, config) {
|
|
382
|
+
return new WorkersAiTextAdapter(model, config);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
export {
|
|
386
|
+
WorkersAiTextAdapter,
|
|
387
|
+
createWorkersAiChat
|
|
388
|
+
};
|
|
389
|
+
//# sourceMappingURL=chunk-BD4CRW3Q.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/adapters/workers-ai.ts"],"sourcesContent":["import type { AiModels, BaseAiTextGeneration } from \"@cloudflare/workers-types\";\nimport type { StreamChunk, TextOptions } from \"@tanstack/ai\";\nimport {\n\tBaseTextAdapter,\n\ttype StructuredOutputOptions,\n\ttype StructuredOutputResult,\n} from \"@tanstack/ai/adapters\";\nimport OpenAI from \"openai\";\nimport {\n\ttype WorkersAiAdapterConfig,\n\ttype AiGatewayAdapterConfig,\n\tcreateGatewayFetch,\n\tcreateWorkersAiBindingFetch,\n\tisDirectBindingConfig,\n\tisDirectCredentialsConfig,\n} from \"../utils/create-fetcher\";\n\n// ---------------------------------------------------------------------------\n// Model types derived from @cloudflare/workers-types\n// ---------------------------------------------------------------------------\n\nexport type WorkersAiTextModel = {\n\t[K in keyof AiModels]: AiModels[K] extends BaseAiTextGeneration ? K : never;\n}[keyof AiModels];\n\n// ---------------------------------------------------------------------------\n// Helpers: build the right OpenAI client depending on config mode\n// ---------------------------------------------------------------------------\n\nfunction buildWorkersAiClient(config: WorkersAiAdapterConfig): OpenAI {\n\tif (isDirectBindingConfig(config)) {\n\t\t// Plain binding mode: shim translates OpenAI fetch calls to env.AI.run()\n\t\treturn new OpenAI({\n\t\t\tapiKey: \"unused\",\n\t\t\tfetch: createWorkersAiBindingFetch(config.binding),\n\t\t});\n\t}\n\n\tif (isDirectCredentialsConfig(config)) {\n\t\t// Plain REST mode: point OpenAI SDK at Workers AI's OpenAI-compatible endpoint\n\t\treturn new OpenAI({\n\t\t\tbaseURL: `https://api.cloudflare.com/client/v4/accounts/${config.accountId}/ai/v1`,\n\t\t\tapiKey: config.apiKey,\n\t\t});\n\t}\n\n\t// Gateway mode (existing): use createGatewayFetch\n\tconst gatewayConfig = config as AiGatewayAdapterConfig;\n\treturn new OpenAI({\n\t\tfetch: createGatewayFetch(\"workers-ai\", gatewayConfig),\n\t\tapiKey: gatewayConfig.apiKey ?? \"unused\",\n\t});\n}\n\n// ---------------------------------------------------------------------------\n// Shared message-building helpers\n// ---------------------------------------------------------------------------\n\ninterface MessageLike {\n\trole: string;\n\tcontent: string | null | Array<{ type: string; content?: string }>;\n\ttoolCalls?: Array<{\n\t\tid: string;\n\t\tfunction: { name: string; arguments: string };\n\t}>;\n\ttoolCallId?: string;\n}\n\nfunction extractTextContent(\n\tcontent: string | null | Array<{ type: string; content?: string }>,\n): string {\n\tif (content === null) return \"\";\n\tif (typeof content === \"string\") return content;\n\treturn content\n\t\t.filter((p) => p.type === \"text\")\n\t\t.map((p) => p.content || \"\")\n\t\t.join(\"\");\n}\n\nfunction buildOpenAIMessages(\n\tsystemPrompts: string[] | undefined,\n\tmessages: MessageLike[],\n\toptions?: { includeToolMessages?: boolean },\n): OpenAI.Chat.ChatCompletionMessageParam[] {\n\tconst includeTools = options?.includeToolMessages ?? true;\n\tconst openAIMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [];\n\n\tif (systemPrompts && systemPrompts.length > 0) {\n\t\topenAIMessages.push({\n\t\t\trole: \"system\",\n\t\t\tcontent: systemPrompts.join(\"\\n\"),\n\t\t});\n\t}\n\n\tfor (const message of messages) {\n\t\tif (message.role === \"user\") {\n\t\t\topenAIMessages.push({\n\t\t\t\trole: \"user\",\n\t\t\t\tcontent: extractTextContent(message.content),\n\t\t\t});\n\t\t} else if (message.role === \"assistant\") {\n\t\t\tconst assistantMessage: OpenAI.Chat.ChatCompletionAssistantMessageParam = {\n\t\t\t\trole: \"assistant\",\n\t\t\t\tcontent: extractTextContent(message.content),\n\t\t\t};\n\t\t\tif (includeTools && message.toolCalls && message.toolCalls.length > 0) {\n\t\t\t\tassistantMessage.tool_calls = message.toolCalls.map((tc) => ({\n\t\t\t\t\tid: tc.id,\n\t\t\t\t\ttype: \"function\" as const,\n\t\t\t\t\tfunction: {\n\t\t\t\t\t\tname: tc.function.name,\n\t\t\t\t\t\targuments: tc.function.arguments,\n\t\t\t\t\t},\n\t\t\t\t}));\n\t\t\t}\n\t\t\topenAIMessages.push(assistantMessage);\n\t\t} else if (includeTools && message.role === \"tool\") {\n\t\t\tlet toolContent: string;\n\t\t\tif (typeof message.content === \"string\") {\n\t\t\t\ttry {\n\t\t\t\t\tJSON.parse(message.content);\n\t\t\t\t\ttoolContent = message.content;\n\t\t\t\t} catch {\n\t\t\t\t\ttoolContent = JSON.stringify(message.content);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\ttoolContent = JSON.stringify(message.content);\n\t\t\t}\n\t\t\topenAIMessages.push({\n\t\t\t\trole: \"tool\",\n\t\t\t\ttool_call_id: message.toolCallId || `tool_${crypto.randomUUID().slice(0, 8)}`,\n\t\t\t\tcontent: toolContent,\n\t\t\t});\n\t\t}\n\t}\n\n\treturn openAIMessages;\n}\n\nfunction buildOpenAITools(\n\ttools: Array<{ name: string; description: string; inputSchema?: unknown }> | undefined,\n): OpenAI.Chat.ChatCompletionTool[] | undefined {\n\tif (!tools) return undefined;\n\treturn tools.map((tool) => ({\n\t\ttype: \"function\" as const,\n\t\tfunction: {\n\t\t\tname: tool.name,\n\t\t\tdescription: tool.description,\n\t\t\tparameters: tool.inputSchema as Record<string, unknown>,\n\t\t},\n\t}));\n}\n\n// ---------------------------------------------------------------------------\n// ID generation\n// ---------------------------------------------------------------------------\n\nfunction generateId(prefix: string): string {\n\treturn `${prefix}-${crypto.randomUUID()}`;\n}\n\n// ---------------------------------------------------------------------------\n// WorkersAiTextAdapter: chat / structured output via OpenAI Chat Completions\n// ---------------------------------------------------------------------------\n\n// TODO: Replace `any` generic params with proper types once BaseTextAdapter's\n// provider-options generics stabilize. Workers AI doesn't have provider-specific\n// options in the TanStack sense, so `any` is pragmatic for now.\nexport class WorkersAiTextAdapter<TModel extends WorkersAiTextModel> extends BaseTextAdapter<\n\tTModel,\n\t// eslint-disable-next-line @typescript-eslint/no-explicit-any -- BaseTextAdapter generic params are opaque\n\tany,\n\tany,\n\tany\n> {\n\tname = \"workers-ai\" as const;\n\n\tprivate client: OpenAI;\n\n\tconstructor(model: TModel, config: WorkersAiAdapterConfig) {\n\t\tsuper({ apiKey: \"unused\" }, model);\n\t\tthis.client = buildWorkersAiClient(config);\n\t}\n\n\tasync *chatStream(options: TextOptions<any>): AsyncIterable<StreamChunk> {\n\t\tconst { systemPrompts, messages, tools, temperature, model } = options;\n\n\t\tconst openAIMessages = buildOpenAIMessages(systemPrompts, messages);\n\t\tconst openAITools = buildOpenAITools(tools);\n\n\t\tconst timestamp = Date.now();\n\t\tconst runId = generateId(\"workers-ai\");\n\t\tconst messageId = generateId(\"workers-ai\");\n\t\tlet hasEmittedRunStarted = false;\n\t\tlet hasEmittedTextMessageStart = false;\n\t\tlet accumulatedContent = \"\";\n\t\tlet hasEmittedStepStarted = false;\n\t\tlet accumulatedReasoning = \"\";\n\t\tconst stepId = generateId(\"workers-ai-step\");\n\t\tlet hasReceivedFinishReason = false;\n\t\tconst toolCallsInProgress = new Map<\n\t\t\tnumber,\n\t\t\t{ id: string; name: string; arguments: string; started: boolean }\n\t\t>();\n\n\t\ttry {\n\t\t\tconst stream = await this.client.chat.completions.create({\n\t\t\t\tmodel: model ?? this.model,\n\t\t\t\tmessages: openAIMessages,\n\t\t\t\ttools: openAITools,\n\t\t\t\ttemperature,\n\t\t\t\tstream: true,\n\t\t\t\tstream_options: { include_usage: true },\n\t\t\t});\n\n\t\t\tfor await (const chunk of stream) {\n\t\t\t\tif (!chunk.choices || chunk.choices.length === 0) continue;\n\t\t\t\tconst choice = chunk.choices[0];\n\t\t\t\tif (!choice) continue;\n\n\t\t\t\t// Emit RUN_STARTED on first chunk\n\t\t\t\tif (!hasEmittedRunStarted) {\n\t\t\t\t\thasEmittedRunStarted = true;\n\t\t\t\t\tyield {\n\t\t\t\t\t\ttype: \"RUN_STARTED\",\n\t\t\t\t\t\trunId,\n\t\t\t\t\t\tmodel: chunk.model || model || this.model,\n\t\t\t\t\t\ttimestamp,\n\t\t\t\t\t} satisfies StreamChunk;\n\t\t\t\t}\n\n\t\t\t\tconst delta = choice.delta;\n\n\t\t\t\t// Reasoning content (used by models like QwQ, DeepSeek R1, Kimi K2.5)\n\t\t\t\t// The OpenAI SDK doesn't type this field, but models send it as an extension.\n\t\t\t\tconst reasoningContent = (delta as Record<string, unknown>).reasoning_content as\n\t\t\t\t\t| string\n\t\t\t\t\t| undefined;\n\t\t\t\tif (reasoningContent) {\n\t\t\t\t\t// RUN_STARTED is already guaranteed by the guard above\n\t\t\t\t\tif (!hasEmittedStepStarted) {\n\t\t\t\t\t\thasEmittedStepStarted = true;\n\t\t\t\t\t\tyield {\n\t\t\t\t\t\t\ttype: \"STEP_STARTED\",\n\t\t\t\t\t\t\tstepId,\n\t\t\t\t\t\t\tstepType: \"thinking\",\n\t\t\t\t\t\t\tmodel: chunk.model || model || this.model,\n\t\t\t\t\t\t\ttimestamp,\n\t\t\t\t\t\t} satisfies StreamChunk;\n\t\t\t\t\t}\n\t\t\t\t\taccumulatedReasoning += reasoningContent;\n\t\t\t\t\t// TODO: TanStack AI's StreamProcessor currently treats STEP_FINISHED as an\n\t\t\t\t\t// incremental reasoning event (with `delta` + accumulated `content`), so we\n\t\t\t\t\t// emit one per token. If TanStack AI adds a dedicated STEP_CONTENT event\n\t\t\t\t\t// type, this should be updated to emit STEP_CONTENT per token and a single\n\t\t\t\t\t// STEP_FINISHED when reasoning ends (i.e. when the first non-reasoning\n\t\t\t\t\t// content or finish_reason arrives).\n\t\t\t\t\tyield {\n\t\t\t\t\t\ttype: \"STEP_FINISHED\",\n\t\t\t\t\t\tstepId,\n\t\t\t\t\t\tdelta: reasoningContent,\n\t\t\t\t\t\tcontent: accumulatedReasoning,\n\t\t\t\t\t\tmodel: chunk.model || model || this.model,\n\t\t\t\t\t\ttimestamp,\n\t\t\t\t\t} satisfies StreamChunk;\n\t\t\t\t}\n\n\t\t\t\t// Text content\n\t\t\t\tif (delta.content) {\n\t\t\t\t\tif (!hasEmittedTextMessageStart) {\n\t\t\t\t\t\thasEmittedTextMessageStart = true;\n\t\t\t\t\t\tyield {\n\t\t\t\t\t\t\ttype: \"TEXT_MESSAGE_START\",\n\t\t\t\t\t\t\tmessageId,\n\t\t\t\t\t\t\tmodel: chunk.model || model || this.model,\n\t\t\t\t\t\t\ttimestamp,\n\t\t\t\t\t\t\trole: \"assistant\",\n\t\t\t\t\t\t} satisfies StreamChunk;\n\t\t\t\t\t}\n\n\t\t\t\t\taccumulatedContent += delta.content;\n\t\t\t\t\tyield {\n\t\t\t\t\t\ttype: \"TEXT_MESSAGE_CONTENT\",\n\t\t\t\t\t\tmessageId,\n\t\t\t\t\t\tmodel: chunk.model || model || this.model,\n\t\t\t\t\t\ttimestamp,\n\t\t\t\t\t\tdelta: delta.content,\n\t\t\t\t\t\tcontent: accumulatedContent,\n\t\t\t\t\t} satisfies StreamChunk;\n\t\t\t\t}\n\n\t\t\t\t// Tool calls\n\t\t\t\tif (delta.tool_calls) {\n\t\t\t\t\tfor (const toolCallDelta of delta.tool_calls) {\n\t\t\t\t\t\tconst index = toolCallDelta.index;\n\n\t\t\t\t\t\tif (!toolCallsInProgress.has(index)) {\n\t\t\t\t\t\t\ttoolCallsInProgress.set(index, {\n\t\t\t\t\t\t\t\tid: toolCallDelta.id || \"\",\n\t\t\t\t\t\t\t\tname: toolCallDelta.function?.name || \"\",\n\t\t\t\t\t\t\t\targuments: \"\",\n\t\t\t\t\t\t\t\tstarted: false,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst toolCall = toolCallsInProgress.get(index)!;\n\n\t\t\t\t\t\tif (toolCallDelta.id) {\n\t\t\t\t\t\t\ttoolCall.id = toolCallDelta.id;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (toolCallDelta.function?.name) {\n\t\t\t\t\t\t\ttoolCall.name = toolCallDelta.function.name;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (toolCallDelta.function?.arguments) {\n\t\t\t\t\t\t\ttoolCall.arguments += toolCallDelta.function.arguments;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Emit TOOL_CALL_START once we have id and name\n\t\t\t\t\t\tif (toolCall.id && toolCall.name && !toolCall.started) {\n\t\t\t\t\t\t\ttoolCall.started = true;\n\t\t\t\t\t\t\tyield {\n\t\t\t\t\t\t\t\ttype: \"TOOL_CALL_START\",\n\t\t\t\t\t\t\t\ttoolCallId: toolCall.id,\n\t\t\t\t\t\t\t\ttoolName: toolCall.name,\n\t\t\t\t\t\t\t\tmodel: chunk.model || model || this.model,\n\t\t\t\t\t\t\t\ttimestamp,\n\t\t\t\t\t\t\t\tindex,\n\t\t\t\t\t\t\t} satisfies StreamChunk;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Stream tool call arguments\n\t\t\t\t\t\tif (toolCallDelta.function?.arguments && toolCall.started) {\n\t\t\t\t\t\t\tyield {\n\t\t\t\t\t\t\t\ttype: \"TOOL_CALL_ARGS\",\n\t\t\t\t\t\t\t\ttoolCallId: toolCall.id,\n\t\t\t\t\t\t\t\tmodel: chunk.model || model || this.model,\n\t\t\t\t\t\t\t\ttimestamp,\n\t\t\t\t\t\t\t\tdelta: toolCallDelta.function.arguments,\n\t\t\t\t\t\t\t} satisfies StreamChunk;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Finish\n\t\t\t\tif (choice.finish_reason) {\n\t\t\t\t\thasReceivedFinishReason = true;\n\n\t\t\t\t\t// End tool calls\n\t\t\t\t\tif (choice.finish_reason === \"tool_calls\" || toolCallsInProgress.size > 0) {\n\t\t\t\t\t\tfor (const [, toolCall] of toolCallsInProgress) {\n\t\t\t\t\t\t\tlet parsedInput: unknown = {};\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tparsedInput = toolCall.arguments\n\t\t\t\t\t\t\t\t\t? JSON.parse(toolCall.arguments)\n\t\t\t\t\t\t\t\t\t: {};\n\t\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t\tparsedInput = {};\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tyield {\n\t\t\t\t\t\t\t\ttype: \"TOOL_CALL_END\",\n\t\t\t\t\t\t\t\ttoolCallId: toolCall.id,\n\t\t\t\t\t\t\t\ttoolName: toolCall.name,\n\t\t\t\t\t\t\t\tmodel: chunk.model || model || this.model,\n\t\t\t\t\t\t\t\ttimestamp,\n\t\t\t\t\t\t\t\tinput: parsedInput,\n\t\t\t\t\t\t\t} satisfies StreamChunk;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tconst computedFinishReason =\n\t\t\t\t\t\tchoice.finish_reason === \"tool_calls\" ||\n\t\t\t\t\t\tchoice.finish_reason === \"function_call\" ||\n\t\t\t\t\t\ttoolCallsInProgress.size > 0\n\t\t\t\t\t\t\t? \"tool_calls\"\n\t\t\t\t\t\t\t: (choice.finish_reason as \"stop\" | \"length\" | \"content_filter\");\n\n\t\t\t\t\t// End text message if started\n\t\t\t\t\tif (hasEmittedTextMessageStart) {\n\t\t\t\t\t\tyield {\n\t\t\t\t\t\t\ttype: \"TEXT_MESSAGE_END\",\n\t\t\t\t\t\t\tmessageId,\n\t\t\t\t\t\t\tmodel: chunk.model || model || this.model,\n\t\t\t\t\t\t\ttimestamp,\n\t\t\t\t\t\t} satisfies StreamChunk;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Emit RUN_FINISHED\n\t\t\t\t\tyield {\n\t\t\t\t\t\ttype: \"RUN_FINISHED\",\n\t\t\t\t\t\trunId,\n\t\t\t\t\t\tmodel: chunk.model || model || this.model,\n\t\t\t\t\t\ttimestamp,\n\t\t\t\t\t\tusage: chunk.usage\n\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\tpromptTokens: chunk.usage.prompt_tokens,\n\t\t\t\t\t\t\t\t\tcompletionTokens: chunk.usage.completion_tokens,\n\t\t\t\t\t\t\t\t\ttotalTokens: chunk.usage.total_tokens,\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\tfinishReason: computedFinishReason,\n\t\t\t\t\t} satisfies StreamChunk;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Premature stream termination: the stream ended without a finish_reason.\n\t\t\t// This can happen when Workers AI truncates a response or the connection drops.\n\t\t\t// Emit proper closing events so the consumer doesn't hang.\n\t\t\tif (hasEmittedRunStarted && !hasReceivedFinishReason) {\n\t\t\t\t// Close any open tool calls\n\t\t\t\tfor (const [, toolCall] of toolCallsInProgress) {\n\t\t\t\t\tif (toolCall.started) {\n\t\t\t\t\t\tlet parsedInput: unknown = {};\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tparsedInput = toolCall.arguments ? JSON.parse(toolCall.arguments) : {};\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\tparsedInput = {};\n\t\t\t\t\t\t}\n\t\t\t\t\t\tyield {\n\t\t\t\t\t\t\ttype: \"TOOL_CALL_END\",\n\t\t\t\t\t\t\ttoolCallId: toolCall.id,\n\t\t\t\t\t\t\ttoolName: toolCall.name,\n\t\t\t\t\t\t\tmodel: model ?? this.model,\n\t\t\t\t\t\t\ttimestamp,\n\t\t\t\t\t\t\tinput: parsedInput,\n\t\t\t\t\t\t} satisfies StreamChunk;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Close text message if open\n\t\t\t\tif (hasEmittedTextMessageStart) {\n\t\t\t\t\tyield {\n\t\t\t\t\t\ttype: \"TEXT_MESSAGE_END\",\n\t\t\t\t\t\tmessageId,\n\t\t\t\t\t\tmodel: model ?? this.model,\n\t\t\t\t\t\ttimestamp,\n\t\t\t\t\t} satisfies StreamChunk;\n\t\t\t\t}\n\n\t\t\t\tyield {\n\t\t\t\t\ttype: \"RUN_FINISHED\",\n\t\t\t\t\trunId,\n\t\t\t\t\tmodel: model ?? this.model,\n\t\t\t\t\ttimestamp,\n\t\t\t\t\tfinishReason: \"stop\",\n\t\t\t\t} satisfies StreamChunk;\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\tconst code =\n\t\t\t\terror instanceof Error ? (error as Error & { code?: string }).code : undefined;\n\t\t\tif (!hasEmittedRunStarted) {\n\t\t\t\tyield {\n\t\t\t\t\ttype: \"RUN_STARTED\",\n\t\t\t\t\trunId,\n\t\t\t\t\tmodel: model ?? this.model,\n\t\t\t\t\ttimestamp,\n\t\t\t\t} satisfies StreamChunk;\n\t\t\t}\n\t\t\tyield {\n\t\t\t\ttype: \"RUN_ERROR\",\n\t\t\t\trunId,\n\t\t\t\tmodel: model ?? this.model,\n\t\t\t\ttimestamp,\n\t\t\t\terror: {\n\t\t\t\t\tmessage: message || \"Unknown error\",\n\t\t\t\t\tcode,\n\t\t\t\t},\n\t\t\t} satisfies StreamChunk;\n\t\t}\n\t}\n\n\tasync structuredOutput(\n\t\toptions: StructuredOutputOptions<any>,\n\t): Promise<StructuredOutputResult<unknown>> {\n\t\tconst { outputSchema, chatOptions } = options;\n\t\tconst { systemPrompts, messages, temperature, model } = chatOptions;\n\n\t\tconst openAIMessages = buildOpenAIMessages(systemPrompts, messages, {\n\t\t\tincludeToolMessages: false,\n\t\t});\n\n\t\tconst response = await this.client.chat.completions.create({\n\t\t\tmodel: model ?? this.model,\n\t\t\tmessages: openAIMessages,\n\t\t\ttemperature,\n\t\t\tstream: false,\n\t\t\tresponse_format: {\n\t\t\t\ttype: \"json_schema\",\n\t\t\t\tjson_schema: {\n\t\t\t\t\tname: \"structured_output\",\n\t\t\t\t\tstrict: true,\n\t\t\t\t\tschema: outputSchema,\n\t\t\t\t},\n\t\t\t},\n\t\t});\n\n\t\tconst choice = response.choices?.[0];\n\n\t\tif (!choice) {\n\t\t\tthrow new Error(\n\t\t\t\t`Workers AI structured output returned no choices: ${JSON.stringify(response)}`,\n\t\t\t);\n\t\t}\n\n\t\tconst rawContent = choice.message?.content ?? \"\";\n\n\t\t// Workers AI REST endpoint may return `content` as an already-parsed object\n\t\t// when using json_schema response format, so normalise both cases.\n\t\tlet data: unknown;\n\t\tlet rawText: string;\n\n\t\tif (typeof rawContent === \"string\") {\n\t\t\trawText = rawContent;\n\t\t\ttry {\n\t\t\t\tdata = JSON.parse(rawText);\n\t\t\t} catch {\n\t\t\t\tdata = rawText;\n\t\t\t}\n\t\t} else {\n\t\t\t// Already an object — stringify for rawText, use directly for data\n\t\t\tdata = rawContent;\n\t\t\trawText = JSON.stringify(rawContent);\n\t\t}\n\n\t\treturn { data, rawText };\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Factory functions\n// ---------------------------------------------------------------------------\n\nexport function createWorkersAiChat(model: WorkersAiTextModel, config: WorkersAiAdapterConfig) {\n\treturn new WorkersAiTextAdapter(model, config);\n}\n"],"mappings":"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","names":[]}
|