@standardagents/openrouter 0.10.0-dev.ffffff
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.txt +48 -0
- package/dist/index.d.ts +237 -0
- package/dist/index.js +1379 -0
- package/dist/index.js.map +1 -0
- package/package.json +51 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1379 @@
|
|
|
1
|
+
// src/OpenRouterProvider.ts
|
|
2
|
+
import { ProviderError } from "@standardagents/spec";
|
|
3
|
+
|
|
4
|
+
// src/transformers.ts
|
|
5
|
+
function transformContentPart(part) {
|
|
6
|
+
if (part.type === "text") {
|
|
7
|
+
return { type: "input_text", text: part.text };
|
|
8
|
+
}
|
|
9
|
+
if (part.type === "image") {
|
|
10
|
+
const data = part.data || "";
|
|
11
|
+
const imageUrl = data.startsWith("data:") ? data : `data:${part.mediaType || "image/png"};base64,${data}`;
|
|
12
|
+
return {
|
|
13
|
+
type: "input_image",
|
|
14
|
+
image_url: imageUrl,
|
|
15
|
+
detail: part.detail || "auto"
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
if (part.type === "image_url") {
|
|
19
|
+
const url = part.image_url?.url || "";
|
|
20
|
+
const detail = part.image_url?.detail || "auto";
|
|
21
|
+
return {
|
|
22
|
+
type: "input_image",
|
|
23
|
+
image_url: url,
|
|
24
|
+
detail
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
type: "input_text",
|
|
29
|
+
text: `[File: ${part.filename || "file"}]`
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function transformMessageContent(content) {
|
|
33
|
+
if (typeof content === "string") {
|
|
34
|
+
return content;
|
|
35
|
+
}
|
|
36
|
+
return content.map(transformContentPart);
|
|
37
|
+
}
|
|
38
|
+
function transformUserMessage(msg) {
|
|
39
|
+
const content = transformMessageContent(msg.content);
|
|
40
|
+
return {
|
|
41
|
+
role: "user",
|
|
42
|
+
content: typeof content === "string" ? content : content
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function transformAssistantMessage(msg) {
|
|
46
|
+
const items = [];
|
|
47
|
+
const reasoningDetails = msg.reasoningDetails;
|
|
48
|
+
if (reasoningDetails && reasoningDetails.length > 0) {
|
|
49
|
+
const summaryTexts = [];
|
|
50
|
+
let encryptedContent;
|
|
51
|
+
for (const detail of reasoningDetails) {
|
|
52
|
+
if (detail.type === "reasoning.text") {
|
|
53
|
+
summaryTexts.push({ type: "summary_text", text: detail.text });
|
|
54
|
+
} else if (detail.type === "reasoning.encrypted") {
|
|
55
|
+
encryptedContent = detail.text;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (summaryTexts.length > 0) {
|
|
59
|
+
const reasoningItem = {
|
|
60
|
+
type: "reasoning",
|
|
61
|
+
id: "",
|
|
62
|
+
summary: summaryTexts,
|
|
63
|
+
...encryptedContent ? { encrypted_content: encryptedContent } : {}
|
|
64
|
+
};
|
|
65
|
+
items.push(reasoningItem);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const hasToolCalls = msg.toolCalls && msg.toolCalls.length > 0;
|
|
69
|
+
if (msg.content || hasToolCalls) {
|
|
70
|
+
const outputMessage = {
|
|
71
|
+
type: "message",
|
|
72
|
+
role: "assistant",
|
|
73
|
+
id: "",
|
|
74
|
+
status: "completed",
|
|
75
|
+
content: msg.content ? [{ type: "output_text", text: msg.content, annotations: [] }] : []
|
|
76
|
+
};
|
|
77
|
+
items.push(outputMessage);
|
|
78
|
+
}
|
|
79
|
+
if (msg.toolCalls && msg.toolCalls.length > 0) {
|
|
80
|
+
for (const tc of msg.toolCalls) {
|
|
81
|
+
const functionCall = {
|
|
82
|
+
type: "function_call",
|
|
83
|
+
call_id: tc.id,
|
|
84
|
+
name: tc.name,
|
|
85
|
+
arguments: JSON.stringify(tc.arguments)
|
|
86
|
+
};
|
|
87
|
+
items.push(functionCall);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return items;
|
|
91
|
+
}
|
|
92
|
+
function transformToolMessage(msg) {
|
|
93
|
+
let output;
|
|
94
|
+
if (typeof msg.content === "string") {
|
|
95
|
+
output = msg.content;
|
|
96
|
+
} else if ("type" in msg.content) {
|
|
97
|
+
if (msg.content.type === "text") {
|
|
98
|
+
output = msg.content.text;
|
|
99
|
+
} else if (msg.content.type === "error") {
|
|
100
|
+
output = `Error: ${msg.content.error}`;
|
|
101
|
+
} else {
|
|
102
|
+
output = JSON.stringify(msg.content);
|
|
103
|
+
}
|
|
104
|
+
} else {
|
|
105
|
+
output = JSON.stringify(msg.content);
|
|
106
|
+
}
|
|
107
|
+
const imageAttachments = msg.attachments?.filter((a) => a.type === "image" && a.data) || [];
|
|
108
|
+
const toolOutput = {
|
|
109
|
+
type: "function_call_output",
|
|
110
|
+
call_id: msg.toolCallId,
|
|
111
|
+
output: output || (imageAttachments.length > 0 ? "Success" : "")
|
|
112
|
+
};
|
|
113
|
+
if (imageAttachments.length === 0) {
|
|
114
|
+
return { toolOutput };
|
|
115
|
+
}
|
|
116
|
+
const imageContent = [];
|
|
117
|
+
const toolName = msg.toolName || "the tool";
|
|
118
|
+
const isSingle = imageAttachments.length === 1;
|
|
119
|
+
const descriptor = isSingle ? "the file" : `${imageAttachments.length} files`;
|
|
120
|
+
const verb = isSingle ? "is" : "are";
|
|
121
|
+
imageContent.push({
|
|
122
|
+
type: "input_text",
|
|
123
|
+
text: `Here ${verb} ${descriptor} from ${toolName}:`
|
|
124
|
+
});
|
|
125
|
+
for (const attachment of imageAttachments) {
|
|
126
|
+
const attachmentData = attachment.data || "";
|
|
127
|
+
const imageData = attachmentData.startsWith("data:") ? attachmentData : `data:${attachment.mediaType || "image/png"};base64,${attachmentData}`;
|
|
128
|
+
imageContent.push({
|
|
129
|
+
type: "input_image",
|
|
130
|
+
image_url: imageData,
|
|
131
|
+
detail: "auto"
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
const syntheticUserMessage = {
|
|
135
|
+
role: "user",
|
|
136
|
+
content: imageContent
|
|
137
|
+
};
|
|
138
|
+
return { toolOutput, syntheticUserMessage };
|
|
139
|
+
}
|
|
140
|
+
function transformMessages(messages) {
|
|
141
|
+
let instructions;
|
|
142
|
+
const input = [];
|
|
143
|
+
for (const msg of messages) {
|
|
144
|
+
switch (msg.role) {
|
|
145
|
+
case "system":
|
|
146
|
+
instructions = instructions ? `${instructions}
|
|
147
|
+
|
|
148
|
+
${msg.content}` : msg.content;
|
|
149
|
+
break;
|
|
150
|
+
case "user":
|
|
151
|
+
input.push(transformUserMessage(msg));
|
|
152
|
+
break;
|
|
153
|
+
case "assistant":
|
|
154
|
+
input.push(...transformAssistantMessage(msg));
|
|
155
|
+
break;
|
|
156
|
+
case "tool": {
|
|
157
|
+
const result = transformToolMessage(msg);
|
|
158
|
+
input.push(result.toolOutput);
|
|
159
|
+
if (result.syntheticUserMessage) {
|
|
160
|
+
input.push(result.syntheticUserMessage);
|
|
161
|
+
}
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return { input, instructions };
|
|
167
|
+
}
|
|
168
|
+
function transformTool(tool) {
|
|
169
|
+
const inputParams = tool.function.parameters;
|
|
170
|
+
let parameters;
|
|
171
|
+
if (inputParams && typeof inputParams === "object") {
|
|
172
|
+
parameters = {
|
|
173
|
+
...inputParams,
|
|
174
|
+
additionalProperties: false
|
|
175
|
+
};
|
|
176
|
+
} else {
|
|
177
|
+
parameters = {
|
|
178
|
+
type: "object",
|
|
179
|
+
properties: {},
|
|
180
|
+
required: [],
|
|
181
|
+
additionalProperties: false
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
return {
|
|
185
|
+
type: "function",
|
|
186
|
+
name: tool.function.name,
|
|
187
|
+
description: tool.function.description || void 0,
|
|
188
|
+
parameters,
|
|
189
|
+
strict: true
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
function transformTools(tools) {
|
|
193
|
+
return tools.map(transformTool);
|
|
194
|
+
}
|
|
195
|
+
function transformToolChoice(choice) {
|
|
196
|
+
if (choice === "auto") {
|
|
197
|
+
return "auto";
|
|
198
|
+
}
|
|
199
|
+
if (choice === "none") {
|
|
200
|
+
return "none";
|
|
201
|
+
}
|
|
202
|
+
if (choice === "required") {
|
|
203
|
+
return "required";
|
|
204
|
+
}
|
|
205
|
+
if (typeof choice === "object" && "name" in choice) {
|
|
206
|
+
return { type: "function", name: choice.name };
|
|
207
|
+
}
|
|
208
|
+
return "auto";
|
|
209
|
+
}
|
|
210
|
+
function mapFinishReason(response) {
|
|
211
|
+
if (response.status === "failed") {
|
|
212
|
+
return "error";
|
|
213
|
+
}
|
|
214
|
+
if (response.status === "incomplete") {
|
|
215
|
+
if (response.incompleteDetails?.reason === "max_output_tokens") {
|
|
216
|
+
return "length";
|
|
217
|
+
}
|
|
218
|
+
if (response.incompleteDetails?.reason === "content_filter") {
|
|
219
|
+
return "content_filter";
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
const hasToolCalls = response.output.some(
|
|
223
|
+
(item) => item.type === "function_call"
|
|
224
|
+
);
|
|
225
|
+
if (hasToolCalls) {
|
|
226
|
+
return "tool_calls";
|
|
227
|
+
}
|
|
228
|
+
return "stop";
|
|
229
|
+
}
|
|
230
|
+
function extractTextContent(output) {
|
|
231
|
+
const textParts = [];
|
|
232
|
+
for (const item of output) {
|
|
233
|
+
if (item.type === "message" && item.role === "assistant") {
|
|
234
|
+
for (const content of item.content) {
|
|
235
|
+
if (content.type === "output_text") {
|
|
236
|
+
textParts.push(content.text);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return textParts.length > 0 ? textParts.join("") : null;
|
|
242
|
+
}
|
|
243
|
+
function extractToolCalls(output) {
|
|
244
|
+
const toolCalls = [];
|
|
245
|
+
for (const item of output) {
|
|
246
|
+
if (item.type === "function_call") {
|
|
247
|
+
let parsedArgs = {};
|
|
248
|
+
try {
|
|
249
|
+
parsedArgs = item.arguments ? JSON.parse(item.arguments) : {};
|
|
250
|
+
} catch {
|
|
251
|
+
}
|
|
252
|
+
toolCalls.push({
|
|
253
|
+
id: item.callId,
|
|
254
|
+
name: item.name,
|
|
255
|
+
arguments: parsedArgs
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
return toolCalls.length > 0 ? toolCalls : void 0;
|
|
260
|
+
}
|
|
261
|
+
function extractImages(output) {
|
|
262
|
+
const images = [];
|
|
263
|
+
for (const item of output) {
|
|
264
|
+
const itemAny = item;
|
|
265
|
+
if (itemAny.type === "image_generation_call" && itemAny.result) {
|
|
266
|
+
const imageData = itemAny.result;
|
|
267
|
+
const isDataUrl = imageData.startsWith("data:");
|
|
268
|
+
const isHttpUrl = imageData.startsWith("http://") || imageData.startsWith("https://");
|
|
269
|
+
let mediaType = "image/png";
|
|
270
|
+
if (isDataUrl) {
|
|
271
|
+
const match = imageData.match(/^data:([^;,]+)/);
|
|
272
|
+
if (match) {
|
|
273
|
+
mediaType = match[1];
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
images.push({
|
|
277
|
+
id: itemAny.id || void 0,
|
|
278
|
+
data: imageData,
|
|
279
|
+
mediaType,
|
|
280
|
+
// Include revised prompt if available
|
|
281
|
+
revisedPrompt: itemAny.revised_prompt || itemAny.revisedPrompt || void 0
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
if (itemAny.type === "message" && itemAny.content && Array.isArray(itemAny.content)) {
|
|
285
|
+
for (const content of itemAny.content) {
|
|
286
|
+
if (content.type === "output_image" || content.type === "image") {
|
|
287
|
+
const imageData = content.image || content.data || content.url || content.image_url;
|
|
288
|
+
if (imageData) {
|
|
289
|
+
let mediaType = content.media_type || content.mediaType || "image/png";
|
|
290
|
+
if (typeof imageData === "string" && imageData.startsWith("data:")) {
|
|
291
|
+
const match = imageData.match(/^data:([^;,]+)/);
|
|
292
|
+
if (match) {
|
|
293
|
+
mediaType = match[1];
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
images.push({
|
|
297
|
+
id: content.id || void 0,
|
|
298
|
+
data: imageData,
|
|
299
|
+
mediaType
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
return images.length > 0 ? images : void 0;
|
|
307
|
+
}
|
|
308
|
+
function transformUsage(usage, actualProvider) {
|
|
309
|
+
if (!usage) {
|
|
310
|
+
return {
|
|
311
|
+
promptTokens: 0,
|
|
312
|
+
completionTokens: 0,
|
|
313
|
+
totalTokens: 0
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
const inputTokens = usage.native_tokens_prompt || usage.nativeTokensPrompt || usage.input_tokens || usage.inputTokens || 0;
|
|
317
|
+
const outputTokens = usage.native_tokens_completion || usage.nativeTokensCompletion || usage.output_tokens || usage.outputTokens || 0;
|
|
318
|
+
const totalTokens = usage.total_tokens || usage.totalTokens || 0;
|
|
319
|
+
const reasoningTokens = usage.output_tokens_details?.reasoning_tokens || usage.outputTokensDetails?.reasoningTokens;
|
|
320
|
+
const cachedTokens = usage.input_tokens_details?.cached_tokens || usage.inputTokensDetails?.cachedTokens;
|
|
321
|
+
return {
|
|
322
|
+
promptTokens: inputTokens,
|
|
323
|
+
completionTokens: outputTokens,
|
|
324
|
+
totalTokens: totalTokens || inputTokens + outputTokens,
|
|
325
|
+
reasoningTokens,
|
|
326
|
+
cachedTokens,
|
|
327
|
+
cost: usage.cost,
|
|
328
|
+
provider: actualProvider
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
function transformResponse(response) {
|
|
332
|
+
const content = extractTextContent(response.output);
|
|
333
|
+
const toolCalls = extractToolCalls(response.output);
|
|
334
|
+
const images = extractImages(response.output);
|
|
335
|
+
const actualProvider = response.model?.split("/")[0] || void 0;
|
|
336
|
+
return {
|
|
337
|
+
content,
|
|
338
|
+
toolCalls,
|
|
339
|
+
images,
|
|
340
|
+
finishReason: mapFinishReason(response),
|
|
341
|
+
usage: transformUsage(response.usage, actualProvider),
|
|
342
|
+
metadata: {
|
|
343
|
+
model: response.model,
|
|
344
|
+
provider: "openrouter",
|
|
345
|
+
actualProvider,
|
|
346
|
+
requestId: response.id
|
|
347
|
+
}
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
function extractReasoningDetails(output, streamedReasoningContent) {
|
|
351
|
+
const details = [];
|
|
352
|
+
if (output && Array.isArray(output)) {
|
|
353
|
+
for (const item of output) {
|
|
354
|
+
const itemAny = item;
|
|
355
|
+
if (itemAny.type === "reasoning") {
|
|
356
|
+
if (itemAny.summary && Array.isArray(itemAny.summary)) {
|
|
357
|
+
for (const step of itemAny.summary) {
|
|
358
|
+
if (typeof step === "string") {
|
|
359
|
+
details.push({ type: "reasoning.text", text: step });
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
if (details.length === 0 && itemAny.encrypted_content) {
|
|
364
|
+
details.push({ type: "reasoning.encrypted", text: itemAny.encrypted_content });
|
|
365
|
+
}
|
|
366
|
+
if (itemAny.content) {
|
|
367
|
+
if (typeof itemAny.content === "string") {
|
|
368
|
+
details.push({ type: "reasoning.text", text: itemAny.content });
|
|
369
|
+
} else if (Array.isArray(itemAny.content)) {
|
|
370
|
+
for (const part of itemAny.content) {
|
|
371
|
+
if (part.text) {
|
|
372
|
+
details.push({ type: "reasoning.text", text: part.text });
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
if (itemAny.type === "message" && itemAny.content && Array.isArray(itemAny.content)) {
|
|
379
|
+
for (const part of itemAny.content) {
|
|
380
|
+
if (part.type === "reasoning" || part.type === "reasoning_text") {
|
|
381
|
+
details.push({ type: "reasoning.text", text: part.text || "" });
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
if (details.length === 0 && streamedReasoningContent) {
|
|
388
|
+
details.push({ type: "reasoning.text", text: streamedReasoningContent });
|
|
389
|
+
}
|
|
390
|
+
return details.length > 0 ? details : void 0;
|
|
391
|
+
}
|
|
392
|
+
function buildCreateParams(request) {
|
|
393
|
+
const { input, instructions } = transformMessages(request.messages);
|
|
394
|
+
const params = {
|
|
395
|
+
model: request.model,
|
|
396
|
+
input,
|
|
397
|
+
store: false
|
|
398
|
+
// Always stateless
|
|
399
|
+
};
|
|
400
|
+
if (instructions) {
|
|
401
|
+
params.instructions = instructions;
|
|
402
|
+
}
|
|
403
|
+
if (request.tools && request.tools.length > 0) {
|
|
404
|
+
params.tools = transformTools(request.tools);
|
|
405
|
+
if (request.toolChoice !== void 0) {
|
|
406
|
+
params.toolChoice = transformToolChoice(request.toolChoice);
|
|
407
|
+
}
|
|
408
|
+
if (request.parallelToolCalls !== void 0) {
|
|
409
|
+
params.parallelToolCalls = request.parallelToolCalls;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
if (request.maxOutputTokens !== void 0) {
|
|
413
|
+
params.maxOutputTokens = request.maxOutputTokens;
|
|
414
|
+
}
|
|
415
|
+
if (request.temperature !== void 0) {
|
|
416
|
+
params.temperature = request.temperature;
|
|
417
|
+
}
|
|
418
|
+
if (request.topP !== void 0) {
|
|
419
|
+
params.topP = request.topP;
|
|
420
|
+
}
|
|
421
|
+
if (request.reasoning?.level !== void 0) {
|
|
422
|
+
const effortMap = {
|
|
423
|
+
10: "minimal",
|
|
424
|
+
// Basic reasoning
|
|
425
|
+
33: "low",
|
|
426
|
+
// Light reasoning
|
|
427
|
+
66: "medium",
|
|
428
|
+
// Balanced reasoning
|
|
429
|
+
100: "high"
|
|
430
|
+
// Deep reasoning
|
|
431
|
+
};
|
|
432
|
+
const effort = effortMap[request.reasoning.level];
|
|
433
|
+
if (effort) {
|
|
434
|
+
params.reasoning = {
|
|
435
|
+
effort
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
if (request.responseFormat) {
|
|
440
|
+
if (request.responseFormat.type === "json") {
|
|
441
|
+
if (request.responseFormat.schema) {
|
|
442
|
+
params.text = {
|
|
443
|
+
format: {
|
|
444
|
+
type: "json_schema",
|
|
445
|
+
name: "response",
|
|
446
|
+
schema: request.responseFormat.schema,
|
|
447
|
+
strict: true
|
|
448
|
+
}
|
|
449
|
+
};
|
|
450
|
+
} else {
|
|
451
|
+
params.text = {
|
|
452
|
+
format: { type: "json_object" }
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
if (request.providerOptions) {
|
|
458
|
+
Object.assign(params, request.providerOptions);
|
|
459
|
+
}
|
|
460
|
+
return params;
|
|
461
|
+
}
|
|
462
|
+
function createStreamState() {
|
|
463
|
+
return {
|
|
464
|
+
toolCalls: /* @__PURE__ */ new Map(),
|
|
465
|
+
reasoningContent: "",
|
|
466
|
+
hasContent: false,
|
|
467
|
+
hasReasoning: false,
|
|
468
|
+
currentItemId: null,
|
|
469
|
+
imageGenerations: /* @__PURE__ */ new Map()
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
function processStreamEvent(event, state) {
|
|
473
|
+
const chunks = [];
|
|
474
|
+
switch (event.type) {
|
|
475
|
+
// Text content streaming
|
|
476
|
+
case "response.output_text.delta":
|
|
477
|
+
state.hasContent = true;
|
|
478
|
+
chunks.push({ type: "content-delta", delta: event.delta });
|
|
479
|
+
break;
|
|
480
|
+
case "response.output_text.done":
|
|
481
|
+
break;
|
|
482
|
+
// Reasoning streaming - OpenRouter uses 'response.reasoning.delta'
|
|
483
|
+
// Note: SDK types may not include reasoning events, so we check dynamically
|
|
484
|
+
case "response.reasoning_text.delta":
|
|
485
|
+
state.hasReasoning = true;
|
|
486
|
+
state.reasoningContent += event.delta;
|
|
487
|
+
chunks.push({ type: "reasoning-delta", delta: event.delta });
|
|
488
|
+
break;
|
|
489
|
+
case "response.reasoning_text.done":
|
|
490
|
+
break;
|
|
491
|
+
// Function call and image generation streaming
|
|
492
|
+
case "response.output_item.added":
|
|
493
|
+
if (event.item.type === "function_call") {
|
|
494
|
+
const callId = event.item.callId || event.item.call_id || "";
|
|
495
|
+
const name = event.item.name || "";
|
|
496
|
+
state.toolCalls.set(callId, {
|
|
497
|
+
id: callId,
|
|
498
|
+
name,
|
|
499
|
+
arguments: ""
|
|
500
|
+
});
|
|
501
|
+
chunks.push({
|
|
502
|
+
type: "tool-call-start",
|
|
503
|
+
id: callId,
|
|
504
|
+
name
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
if (event.item.type === "image_generation_call") {
|
|
508
|
+
const itemAny = event.item;
|
|
509
|
+
const imageId = itemAny.id || itemAny.call_id || itemAny.callId || `img-${state.imageGenerations.size}`;
|
|
510
|
+
state.imageGenerations.set(imageId, {
|
|
511
|
+
id: imageId,
|
|
512
|
+
data: "",
|
|
513
|
+
status: "in_progress"
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
break;
|
|
517
|
+
case "response.output_item.done": {
|
|
518
|
+
const itemAny = event.item;
|
|
519
|
+
if (itemAny.type === "image_generation_call" && itemAny.result) {
|
|
520
|
+
const imageId = itemAny.id || itemAny.call_id || itemAny.callId || "";
|
|
521
|
+
const imageData = itemAny.result;
|
|
522
|
+
let mediaType = "image/png";
|
|
523
|
+
if (imageData.startsWith("data:")) {
|
|
524
|
+
const match = imageData.match(/^data:([^;,]+)/);
|
|
525
|
+
if (match) mediaType = match[1];
|
|
526
|
+
}
|
|
527
|
+
const existing = state.imageGenerations.get(imageId);
|
|
528
|
+
if (existing) {
|
|
529
|
+
existing.data = imageData;
|
|
530
|
+
existing.status = "completed";
|
|
531
|
+
}
|
|
532
|
+
const imageIndex = Array.from(state.imageGenerations.keys()).indexOf(imageId);
|
|
533
|
+
chunks.push({
|
|
534
|
+
type: "image-done",
|
|
535
|
+
index: imageIndex >= 0 ? imageIndex : state.imageGenerations.size - 1,
|
|
536
|
+
image: {
|
|
537
|
+
id: imageId || void 0,
|
|
538
|
+
data: imageData,
|
|
539
|
+
mediaType,
|
|
540
|
+
revisedPrompt: itemAny.revised_prompt || itemAny.revisedPrompt || void 0
|
|
541
|
+
}
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
break;
|
|
545
|
+
}
|
|
546
|
+
case "response.function_call_arguments.delta": {
|
|
547
|
+
const itemId = event.itemId || event.item_id || "";
|
|
548
|
+
const deltaToolCall = Array.from(state.toolCalls.values()).find(
|
|
549
|
+
(tc) => tc.id === itemId
|
|
550
|
+
);
|
|
551
|
+
if (deltaToolCall) {
|
|
552
|
+
deltaToolCall.arguments += event.delta;
|
|
553
|
+
chunks.push({
|
|
554
|
+
type: "tool-call-delta",
|
|
555
|
+
id: deltaToolCall.id,
|
|
556
|
+
argumentsDelta: event.delta
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
break;
|
|
560
|
+
}
|
|
561
|
+
case "response.function_call_arguments.done": {
|
|
562
|
+
const itemId = event.itemId || event.item_id || "";
|
|
563
|
+
const doneToolCall = Array.from(state.toolCalls.values()).find(
|
|
564
|
+
(tc) => tc.id === itemId
|
|
565
|
+
);
|
|
566
|
+
if (doneToolCall) {
|
|
567
|
+
let parsedArgs = {};
|
|
568
|
+
try {
|
|
569
|
+
parsedArgs = doneToolCall.arguments ? JSON.parse(doneToolCall.arguments) : {};
|
|
570
|
+
} catch {
|
|
571
|
+
}
|
|
572
|
+
chunks.push({
|
|
573
|
+
type: "tool-call-done",
|
|
574
|
+
id: doneToolCall.id,
|
|
575
|
+
arguments: parsedArgs
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
break;
|
|
579
|
+
}
|
|
580
|
+
// Response completion
|
|
581
|
+
case "response.completed": {
|
|
582
|
+
if (state.hasContent) {
|
|
583
|
+
chunks.push({ type: "content-done" });
|
|
584
|
+
}
|
|
585
|
+
if (state.hasReasoning) {
|
|
586
|
+
chunks.push({ type: "reasoning-done" });
|
|
587
|
+
}
|
|
588
|
+
const completedProvider = event.response.model?.split("/")[0] || void 0;
|
|
589
|
+
const reasoningDetails = extractReasoningDetails(event.response.output, state.reasoningContent);
|
|
590
|
+
const generatedImages = extractImages(event.response.output);
|
|
591
|
+
if (generatedImages && generatedImages.length > 0) {
|
|
592
|
+
for (let i = 0; i < generatedImages.length; i++) {
|
|
593
|
+
const img = generatedImages[i];
|
|
594
|
+
const alreadyEmitted = img.id && state.imageGenerations.get(img.id)?.status === "completed";
|
|
595
|
+
if (!alreadyEmitted) {
|
|
596
|
+
chunks.push({
|
|
597
|
+
type: "image-done",
|
|
598
|
+
index: i,
|
|
599
|
+
image: img
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
chunks.push({
|
|
605
|
+
type: "finish",
|
|
606
|
+
finishReason: mapFinishReason(event.response),
|
|
607
|
+
usage: transformUsage(event.response.usage, completedProvider),
|
|
608
|
+
responseId: event.response.id,
|
|
609
|
+
// Used to fetch generation metadata after stream
|
|
610
|
+
reasoningDetails
|
|
611
|
+
});
|
|
612
|
+
break;
|
|
613
|
+
}
|
|
614
|
+
case "response.failed":
|
|
615
|
+
chunks.push({
|
|
616
|
+
type: "error",
|
|
617
|
+
error: event.response.error?.message || "Response generation failed",
|
|
618
|
+
code: event.response.error?.code
|
|
619
|
+
});
|
|
620
|
+
break;
|
|
621
|
+
case "response.incomplete": {
|
|
622
|
+
if (state.hasContent) {
|
|
623
|
+
chunks.push({ type: "content-done" });
|
|
624
|
+
}
|
|
625
|
+
if (state.hasReasoning) {
|
|
626
|
+
chunks.push({ type: "reasoning-done" });
|
|
627
|
+
}
|
|
628
|
+
const incompleteProvider = event.response.model?.split("/")[0] || void 0;
|
|
629
|
+
const incompleteReasoningDetails = extractReasoningDetails(event.response.output, state.reasoningContent);
|
|
630
|
+
const incompleteImages = extractImages(event.response.output);
|
|
631
|
+
if (incompleteImages && incompleteImages.length > 0) {
|
|
632
|
+
for (let i = 0; i < incompleteImages.length; i++) {
|
|
633
|
+
const img = incompleteImages[i];
|
|
634
|
+
const alreadyEmitted = img.id && state.imageGenerations.get(img.id)?.status === "completed";
|
|
635
|
+
if (!alreadyEmitted) {
|
|
636
|
+
chunks.push({
|
|
637
|
+
type: "image-done",
|
|
638
|
+
index: i,
|
|
639
|
+
image: img
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
chunks.push({
|
|
645
|
+
type: "finish",
|
|
646
|
+
finishReason: mapFinishReason(event.response),
|
|
647
|
+
usage: transformUsage(event.response.usage, incompleteProvider),
|
|
648
|
+
responseId: event.response.id,
|
|
649
|
+
// Used to fetch generation metadata after stream
|
|
650
|
+
reasoningDetails: incompleteReasoningDetails
|
|
651
|
+
});
|
|
652
|
+
break;
|
|
653
|
+
}
|
|
654
|
+
// Handle reasoning and image events dynamically (SDK types may not include them)
|
|
655
|
+
default: {
|
|
656
|
+
const eventType = event.type;
|
|
657
|
+
const eventAny = event;
|
|
658
|
+
if (eventType === "response.reasoning.delta") {
|
|
659
|
+
state.hasReasoning = true;
|
|
660
|
+
state.reasoningContent += eventAny.delta || "";
|
|
661
|
+
chunks.push({ type: "reasoning-delta", delta: eventAny.delta || "" });
|
|
662
|
+
}
|
|
663
|
+
if (eventType === "response.image_generation_call.partial_image" || eventType === "response.image_generation.partial") {
|
|
664
|
+
const imageId = eventAny.item_id || eventAny.itemId || eventAny.id || "";
|
|
665
|
+
const partialData = eventAny.partial_image || eventAny.delta || eventAny.data || "";
|
|
666
|
+
const existing = state.imageGenerations.get(imageId);
|
|
667
|
+
if (existing) {
|
|
668
|
+
existing.data += partialData;
|
|
669
|
+
} else {
|
|
670
|
+
state.imageGenerations.set(imageId, {
|
|
671
|
+
id: imageId,
|
|
672
|
+
data: partialData,
|
|
673
|
+
status: "generating"
|
|
674
|
+
});
|
|
675
|
+
}
|
|
676
|
+
const imageIndex = Array.from(state.imageGenerations.keys()).indexOf(imageId);
|
|
677
|
+
chunks.push({
|
|
678
|
+
type: "image-delta",
|
|
679
|
+
index: imageIndex >= 0 ? imageIndex : state.imageGenerations.size - 1,
|
|
680
|
+
data: partialData
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
if (eventType === "response.image_generation_call.completed" || eventType === "response.image_generation.done") {
|
|
684
|
+
const imageId = eventAny.item_id || eventAny.itemId || eventAny.id || "";
|
|
685
|
+
const imageData = eventAny.result || eventAny.image || eventAny.data || "";
|
|
686
|
+
if (imageData) {
|
|
687
|
+
let mediaType = "image/png";
|
|
688
|
+
if (imageData.startsWith("data:")) {
|
|
689
|
+
const match = imageData.match(/^data:([^;,]+)/);
|
|
690
|
+
if (match) mediaType = match[1];
|
|
691
|
+
}
|
|
692
|
+
const existing = state.imageGenerations.get(imageId);
|
|
693
|
+
if (existing) {
|
|
694
|
+
existing.data = imageData;
|
|
695
|
+
existing.status = "completed";
|
|
696
|
+
}
|
|
697
|
+
const imageIndex = Array.from(state.imageGenerations.keys()).indexOf(imageId);
|
|
698
|
+
chunks.push({
|
|
699
|
+
type: "image-done",
|
|
700
|
+
index: imageIndex >= 0 ? imageIndex : state.imageGenerations.size - 1,
|
|
701
|
+
image: {
|
|
702
|
+
id: imageId || void 0,
|
|
703
|
+
data: imageData,
|
|
704
|
+
mediaType,
|
|
705
|
+
revisedPrompt: eventAny.revised_prompt || eventAny.revisedPrompt || void 0
|
|
706
|
+
}
|
|
707
|
+
});
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
break;
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
return chunks;
|
|
714
|
+
}
|
|
715
|
+
function createErrorChunk(error, code) {
|
|
716
|
+
return { type: "error", error, code };
|
|
717
|
+
}
|
|
718
|
+
function isBase64Like(str) {
|
|
719
|
+
if (str.startsWith("data:")) return true;
|
|
720
|
+
if (str.length > 200) {
|
|
721
|
+
const base64Pattern = /^[A-Za-z0-9+/]+=*$/;
|
|
722
|
+
return base64Pattern.test(str.substring(0, 200));
|
|
723
|
+
}
|
|
724
|
+
return false;
|
|
725
|
+
}
|
|
726
|
+
function truncateBase64String(str, maxLength = 50) {
|
|
727
|
+
if (str.length <= maxLength) return str;
|
|
728
|
+
const preview = str.substring(0, maxLength);
|
|
729
|
+
return `${preview}...[truncated, ${str.length.toLocaleString()} chars]`;
|
|
730
|
+
}
|
|
731
|
+
function truncateBase64(obj, maxLength = 50) {
|
|
732
|
+
if (obj === null || obj === void 0) {
|
|
733
|
+
return obj;
|
|
734
|
+
}
|
|
735
|
+
if (typeof obj === "string") {
|
|
736
|
+
if (isBase64Like(obj)) {
|
|
737
|
+
return truncateBase64String(obj, maxLength);
|
|
738
|
+
}
|
|
739
|
+
return obj;
|
|
740
|
+
}
|
|
741
|
+
if (Array.isArray(obj)) {
|
|
742
|
+
return obj.map((item) => truncateBase64(item, maxLength));
|
|
743
|
+
}
|
|
744
|
+
if (typeof obj === "object") {
|
|
745
|
+
const result = {};
|
|
746
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
747
|
+
result[key] = truncateBase64(value, maxLength);
|
|
748
|
+
}
|
|
749
|
+
return result;
|
|
750
|
+
}
|
|
751
|
+
return obj;
|
|
752
|
+
}
|
|
753
|
+
function parseRawStreamEvent(jsonStr) {
|
|
754
|
+
try {
|
|
755
|
+
const event = JSON.parse(jsonStr);
|
|
756
|
+
return event;
|
|
757
|
+
} catch {
|
|
758
|
+
return null;
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
async function* parseSSEStream(response, state) {
|
|
762
|
+
const reader = response.body?.getReader();
|
|
763
|
+
if (!reader) {
|
|
764
|
+
throw new Error("No response body");
|
|
765
|
+
}
|
|
766
|
+
const decoder = new TextDecoder();
|
|
767
|
+
let buffer = "";
|
|
768
|
+
try {
|
|
769
|
+
while (true) {
|
|
770
|
+
const { done, value } = await reader.read();
|
|
771
|
+
if (done) break;
|
|
772
|
+
buffer += decoder.decode(value, { stream: true });
|
|
773
|
+
const lines = buffer.split("\n");
|
|
774
|
+
buffer = lines.pop() || "";
|
|
775
|
+
for (const line of lines) {
|
|
776
|
+
const trimmed = line.trim();
|
|
777
|
+
if (!trimmed || trimmed.startsWith(":")) continue;
|
|
778
|
+
if (trimmed.startsWith("data: ")) {
|
|
779
|
+
const data = trimmed.slice(6);
|
|
780
|
+
if (data === "[DONE]") continue;
|
|
781
|
+
const event = parseRawStreamEvent(data);
|
|
782
|
+
if (event) {
|
|
783
|
+
const chunks = processStreamEvent(event, state);
|
|
784
|
+
for (const chunk of chunks) {
|
|
785
|
+
yield chunk;
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
if (buffer.trim()) {
|
|
792
|
+
const trimmed = buffer.trim();
|
|
793
|
+
if (trimmed.startsWith("data: ")) {
|
|
794
|
+
const data = trimmed.slice(6);
|
|
795
|
+
if (data !== "[DONE]") {
|
|
796
|
+
const event = parseRawStreamEvent(data);
|
|
797
|
+
if (event) {
|
|
798
|
+
const chunks = processStreamEvent(event, state);
|
|
799
|
+
for (const chunk of chunks) {
|
|
800
|
+
yield chunk;
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
} finally {
|
|
807
|
+
reader.releaseLock();
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
async function fetchGenerationMetadata(apiKey, generationId, baseUrl = "https://openrouter.ai/api/v1", signal) {
|
|
811
|
+
const url = `${baseUrl}/generation?id=${encodeURIComponent(generationId)}`;
|
|
812
|
+
const maxRetries = 3;
|
|
813
|
+
const delays = [500, 1e3, 2e3];
|
|
814
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
815
|
+
try {
|
|
816
|
+
if (attempt > 0) {
|
|
817
|
+
await new Promise((resolve) => setTimeout(resolve, delays[attempt - 1]));
|
|
818
|
+
}
|
|
819
|
+
const response = await fetch(url, {
|
|
820
|
+
method: "GET",
|
|
821
|
+
headers: {
|
|
822
|
+
"Authorization": `Bearer ${apiKey}`
|
|
823
|
+
},
|
|
824
|
+
signal
|
|
825
|
+
});
|
|
826
|
+
if (response.status === 404 && attempt < maxRetries - 1) {
|
|
827
|
+
continue;
|
|
828
|
+
}
|
|
829
|
+
if (!response.ok) {
|
|
830
|
+
console.error(`Failed to fetch generation metadata: ${response.status}`);
|
|
831
|
+
return null;
|
|
832
|
+
}
|
|
833
|
+
const result = await response.json();
|
|
834
|
+
if (result.error) {
|
|
835
|
+
console.error("Generation metadata error:", result.error.message);
|
|
836
|
+
return null;
|
|
837
|
+
}
|
|
838
|
+
const data = result.data;
|
|
839
|
+
if (!data) {
|
|
840
|
+
return null;
|
|
841
|
+
}
|
|
842
|
+
return {
|
|
843
|
+
id: data.id || generationId,
|
|
844
|
+
totalCost: data.total_cost || 0,
|
|
845
|
+
promptTokens: data.tokens_prompt || 0,
|
|
846
|
+
completionTokens: data.tokens_completion || 0,
|
|
847
|
+
nativePromptTokens: data.native_tokens_prompt,
|
|
848
|
+
nativeCompletionTokens: data.native_tokens_completion,
|
|
849
|
+
providerName: data.provider_name || "unknown",
|
|
850
|
+
latency: data.latency || data.generation_time,
|
|
851
|
+
model: data.model,
|
|
852
|
+
finishReason: data.finish_reason,
|
|
853
|
+
createdAt: data.created_at
|
|
854
|
+
};
|
|
855
|
+
} catch (error) {
|
|
856
|
+
if (attempt < maxRetries - 1) {
|
|
857
|
+
continue;
|
|
858
|
+
}
|
|
859
|
+
console.error("Error fetching generation metadata:", error);
|
|
860
|
+
return null;
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
return null;
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
// src/icons.ts
|
|
867
|
+
var ANTHROPIC_ICON = `<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48" fill="none">
|
|
868
|
+
<rect width="48" height="48" fill="#F0EFEA"/>
|
|
869
|
+
<path d="M32.84 10H26.72L37.88 38H44L32.84 10Z" fill="black"/>
|
|
870
|
+
<path d="M15.16 10L4 38H10.24L12.5224 32.12H24.1976L26.48 38H32.72L21.56 10H15.16ZM14.5408 26.92L18.36 17.08L22.1793 26.92H14.5408Z" fill="black"/>
|
|
871
|
+
</svg>`;
|
|
872
|
+
var GOOGLE_ICON = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none" width="48" height="48"><rect width="16" height="16" fill="white"/><path fill="#4285F4" d="M14.9 8.161c0-.476-.039-.954-.121-1.422h-6.64v2.695h3.802a3.24 3.24 0 01-1.407 2.127v1.75h2.269c1.332-1.22 2.097-3.02 2.097-5.15z"/><path fill="#34A853" d="M8.14 15c1.898 0 3.499-.62 4.665-1.69l-2.268-1.749c-.631.427-1.446.669-2.395.669-1.836 0-3.393-1.232-3.952-2.888H1.85v1.803A7.044 7.044 0 008.14 15z"/><path fill="#FBBC04" d="M4.187 9.342a4.17 4.17 0 010-2.68V4.859H1.849a6.97 6.97 0 000 6.286l2.338-1.803z"/><path fill="#EA4335" d="M8.14 3.77a3.837 3.837 0 012.7 1.05l2.01-1.999a6.786 6.786 0 00-4.71-1.82 7.042 7.042 0 00-6.29 3.858L4.186 6.66c.556-1.658 2.116-2.89 3.952-2.89z"/></svg>`;
|
|
873
|
+
var META_ICON = `<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48" fill="none">
|
|
874
|
+
<rect width="48" height="48" fill="#0668E1"/>
|
|
875
|
+
<path d="M35.5 16c-2.8 0-5.2 2.2-7.5 6.5-2.3-4.3-4.7-6.5-7.5-6.5-4.4 0-8 5.6-8 12s3.6 12 8 12c2.8 0 5.2-2.2 7.5-6.5 2.3 4.3 4.7 6.5 7.5 6.5 4.4 0 8-5.6 8-12s-3.6-12-8-12zm-15 20c-2.8 0-5-4-5-8s2.2-8 5-8c1.4 0 2.8 1 4.2 3-1.8 3.2-2.8 5.8-2.8 8 0 2.2 1 4.8 2.8 8-1.4 2-2.8 3-4.2 3zm15 0c-1.4 0-2.8-1-4.2-3 1.8-3.2 2.8-5.8 2.8-8 0-2.2-1-4.8-2.8-8 1.4-2 2.8-3 4.2-3 2.8 0 5 4 5 8s-2.2 8-5 8z" fill="white"/>
|
|
876
|
+
</svg>`;
|
|
877
|
+
var NVIDIA_ICON = `<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48" fill="none">
|
|
878
|
+
<rect width="48" height="48" fill="white"/>
|
|
879
|
+
<path d="M17.8962 17.7465V14.9519C18.1789 14.9346 18.4615 14.9174 18.7442 14.9174C26.5881 14.6758 31.7291 21.5072 31.7291 21.5072C31.7291 21.5072 26.1818 29.0287 20.2282 29.0287C19.4332 29.0287 18.6559 28.9079 17.9139 28.6664V20.1789C20.9702 20.5412 21.5885 21.8523 23.4082 24.8367L27.4891 21.49C27.4891 21.49 24.5035 17.6775 19.4862 17.6775C18.9562 17.6603 18.4262 17.6948 17.8962 17.7465ZM17.8962 8.5V12.6747L18.7442 12.623C29.6445 12.2607 36.7641 21.352 36.7641 21.352C36.7641 21.352 28.6021 31.047 20.1045 31.047C19.3625 31.047 18.6382 30.978 17.9139 30.8573V33.4449C18.5145 33.5139 19.1329 33.5657 19.7335 33.5657C27.6481 33.5657 33.3721 29.6152 38.9194 24.9574C39.8381 25.682 43.601 27.4243 44.3784 28.1834C39.1137 32.4961 26.8355 35.9636 19.8749 35.9636C19.2035 35.9636 18.5675 35.9291 17.9315 35.86V39.5H48V8.5L17.8962 8.5ZM17.8962 28.6664V30.8745C10.5823 29.5979 8.55061 22.1628 8.55061 22.1628C8.55061 22.1628 12.0662 18.3676 17.8962 17.7465V20.1617H17.8785C14.8222 19.7994 12.4196 22.594 12.4196 22.594C12.4196 22.594 13.7799 27.3036 17.8962 28.6664ZM4.9113 21.8523C4.9113 21.8523 9.2396 15.6074 17.9139 14.9519V12.6747C8.30327 13.4338 0 21.3692 0 21.3692C0 21.3692 4.6993 34.6525 17.8962 35.86V33.4449C8.21494 32.2718 4.9113 21.8523 4.9113 21.8523Z" fill="#76B900"/>
|
|
880
|
+
</svg>`;
|
|
881
|
+
var ALIBABA_ICON = `<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48" fill="none">
|
|
882
|
+
<rect width="48" height="48" fill="#FF5A00"/>
|
|
883
|
+
<path d="M31.5 12c3 .2 6.2.7 8.6 2.7 1.9 1.6 2.7 4.2 2.3 6.6-.4 1.8-1.2 3.4-2.1 5-1.8 3.1-4 6-6.1 8.9-.4.5-.7 1.1-.6 1.7 0 .5.4.9.9 1.1.9.4 1.8.3 2.7.2 2.9-.3 5.6-1.2 8.4-2.1.2-.1.3.1.4.2-2.9 1.9-6 3.6-9.3 4.8-1.8.6-3.7 1.2-5.7 1-1-.1-2-.7-2.4-1.7-.4-1.3-.2-2.8.3-4 .9-1.9 2.2-3.6 3.5-5.3 2.1-2.8 4.3-5.5 6-8.6.6-1.1 1.1-2.4.6-3.6-.5-1.1-1.6-1.8-2.7-2.3-1.9-.8-3.9-1.4-5.9-1.9l.9.6c.7.5 1.4 1.1 2.1convergence 1.6-2.6.5-5.3 1-7.9 1.7-3.9 1-7.8 2.3-11.6 3.7.4.7.7 1.5 1.1 2.2-.9 1-1.7 1.9-2.6 2.9 2.1.6 4.3.7 6.4.2 1.8-.4 3.5-1.3 4.9-2.5-.4-.5-.9-.8-1.4-1.2 1.7 0 3.2 1.5 3.2 3.2l-1 .1c-.1-.4-.2-.7-.3-1.1-1.9 1.7-4.4 2.8-6.9 3.1-2.2.3-4.5-.1-6.7-.8.1 1.3.3 2.6.4 3.9-1.2.5-2.3 1.1-3.3 2-.8.7-1.6 1.5-1.9 2.5-.3.9.2 1.9 1 2.4 1 .6 2.1.9 3.2 1 1.5.2 3 .2 4.4.1 2.9-.2 5.8-.8 8.6-1.5.3-.1.4.4.1.4-2.3 1.2-4.6 2.1-7.1 2.8-2.3.6-4.7.9-7.1 1-2.4-.1-5-.5-7-1.9-1.4-1.2-2.2-3-2.2-4.8v-.1c0-1.9.7-3.8 1.6-5.5 1.2-2.4 2.8-4.6 4.6-6.5 3.1-3.4 7-6.1 11.2-8 4.4-2 9-3.4 13.8-3.7z" fill="white"/>
|
|
884
|
+
</svg>`;
|
|
885
|
+
var DEEPSEEK_ICON = `<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48" fill="none">
|
|
886
|
+
<rect width="48" height="48" fill="white"/>
|
|
887
|
+
<path d="M47.4964 9.43757C46.988 9.19072 46.769 9.66115 46.4717 9.90013C46.37 9.97722 46.284 10.0774 46.1979 10.1699C45.4548 10.9562 44.5866 11.4729 43.4523 11.4112C41.794 11.3186 40.3781 11.8353 39.1265 13.092C38.8605 11.5423 37.9767 10.617 36.6312 10.0234C35.9273 9.71497 35.2154 9.40651 34.7226 8.7357C34.3784 8.25783 34.2845 7.7258 34.1124 7.20147C34.0029 6.8854 33.8934 6.56146 33.5257 6.50747C33.1268 6.44587 32.9703 6.77725 32.8138 7.05499C32.1881 8.18844 31.9456 9.43749 31.969 10.7019C32.0237 13.5471 33.2363 15.8137 35.6455 17.4252C35.9193 17.6103 35.9896 17.7952 35.9036 18.0652C35.7393 18.6204 35.5437 19.16 35.3717 19.7151C35.2622 20.0699 35.0978 20.147 34.7146 19.9926C33.3926 19.4453 32.2505 18.6357 31.2414 17.6566C29.5284 16.0143 27.9795 14.2024 26.0475 12.7837C25.5938 12.4521 25.1401 12.1438 24.6707 11.8507C22.6994 9.95412 24.9289 8.39653 25.4451 8.21163C25.9848 8.01885 25.6329 7.35566 23.8884 7.36353C22.144 7.37114 20.5483 7.94946 18.5146 8.72047C18.2173 8.83615 17.9043 8.92086 17.5836 8.99025C15.7376 8.64338 13.8211 8.56612 11.8186 8.78986C8.04819 9.20613 5.03671 10.9718 2.823 13.9865C0.16347 17.6103 -0.462297 21.7274 0.304303 26.0221C1.11002 30.5478 3.44099 34.295 7.02367 37.225C10.7393 40.2626 15.0181 41.7507 19.8992 41.4655C22.8639 41.2959 26.1649 40.9027 29.8884 37.7801C30.827 38.2427 31.8126 38.4276 33.4475 38.5664C34.7069 38.6821 35.9194 38.5048 36.858 38.3121C38.3286 38.0037 38.227 36.6544 37.6951 36.4077C33.385 34.4185 34.3314 35.228 33.471 34.5726C35.6613 32.0052 38.9624 29.3374 40.253 20.6942C40.3546 20.0081 40.2686 19.5763 40.253 19.0211C40.2451 18.682 40.3234 18.551 40.7145 18.5123C41.794 18.3889 42.8422 18.096 43.8043 17.5717C46.5969 16.0606 47.7233 13.5779 47.9892 10.6017C48.0284 10.1468 47.9814 9.67638 47.4964 9.43757ZM23.1611 36.2225C18.984 32.969 16.9581 31.8971 16.1211 31.9434C15.3388 31.9897 15.4797 32.8764 15.6517 33.4547C15.8316 34.0252 16.0663 34.4185 16.3948 34.9196C16.6216 35.2511 16.7782 35.7445 16.168 36.1147C14.8226 36.9397 12.4836 35.8371 12.3741 35.7832C9.65202 34.1948 7.37571 32.0975 5.77205 29.2295C4.22322 26.4691 3.32373 23.5086 3.17504 20.3474C3.13592 19.5841 3.36276 19.3141 4.12936 19.1755C5.13843 18.9904 6.17884 18.9517 7.1879 19.0982C11.4512 19.7151 15.0807 21.6041 18.1235 24.5956C19.86 26.2995 21.1742 28.3352 22.5275 30.3243C23.9668 32.4368 25.5156 34.4493 27.4868 36.0992C28.183 36.6775 28.7384 37.1169 29.2703 37.4408C27.6668 37.6181 24.9915 37.6568 23.1611 36.2225ZM25.1636 23.4623C25.1636 23.123 25.4374 22.8532 25.7817 22.8532C25.8599 22.8532 25.9303 22.8685 25.9929 22.8916C26.079 22.9225 26.1572 22.9688 26.2198 23.038C26.3293 23.1461 26.3919 23.3002 26.3919 23.4622C26.3919 23.8014 26.1181 24.0712 25.774 24.0712C25.4299 24.0712 25.1636 23.8015 25.1636 23.4623ZM31.3824 26.6235C30.9835 26.7854 30.5846 26.9241 30.2013 26.9395C29.6068 26.9703 28.9575 26.7313 28.6056 26.4383C28.058 25.9834 27.6669 25.7291 27.5026 24.9349C27.4322 24.5956 27.4713 24.0713 27.534 23.7707C27.6747 23.123 27.5183 22.7067 27.0568 22.3288C26.6813 22.0204 26.2041 21.9357 25.68 21.9357C25.4844 21.9357 25.3046 21.8508 25.1716 21.7814C24.9525 21.6735 24.7727 21.4036 24.9447 21.0721C24.9995 20.9643 25.2655 20.702 25.3281 20.6558C26.04 20.2548 26.8613 20.3859 27.6201 20.6866C28.3242 20.9719 28.8561 21.4962 29.6226 22.2363C30.4048 23.1306 30.5456 23.3776 30.9915 24.0482C31.3436 24.5725 31.6643 25.1122 31.8833 25.7291C32.0162 26.1146 31.844 26.4307 31.3824 26.6235Z" fill="#4D6BFE"/>
|
|
888
|
+
</svg>`;
|
|
889
|
+
var QWEN_ICON = `<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48" fill="none">
|
|
890
|
+
<rect width="48" height="48" fill="#FF5A00"/>
|
|
891
|
+
<path d="M31.5 12c3 .2 6.2.7 8.6 2.7 1.9 1.6 2.7 4.2 2.3 6.6-.4 1.8-1.2 3.4-2.1 5-1.8 3.1-4 6-6.1 8.9-.4.5-.7 1.1-.6 1.7 0 .5.4.9.9 1.1.9.4 1.8.3 2.7.2 2.9-.3 5.6-1.2 8.4-2.1.2-.1.3.1.4.2-2.9 1.9-6 3.6-9.3 4.8-1.8.6-3.7 1.2-5.7 1-1-.1-2-.7-2.4-1.7-.4-1.3-.2-2.8.3-4 .9-1.9 2.2-3.6 3.5-5.3 2.1-2.8 4.3-5.5 6-8.6.6-1.1 1.1-2.4.6-3.6-.5-1.1-1.6-1.8-2.7-2.3-1.9-.8-3.9-1.4-5.9-1.9l.9.6c.7.5 1.4 1.1 2.1 1.6-2.6.5-5.3 1-7.9 1.7-3.9 1-7.8 2.3-11.6 3.7.4.7.7 1.5 1.1 2.2-.9 1-1.7 1.9-2.6 2.9 2.1.6 4.3.7 6.4.2 1.8-.4 3.5-1.3 4.9-2.5-.4-.5-.9-.8-1.4-1.2 1.7 0 3.2 1.5 3.2 3.2l-1 .1c-.1-.4-.2-.7-.3-1.1-1.9 1.7-4.4 2.8-6.9 3.1-2.2.3-4.5-.1-6.7-.8.1 1.3.3 2.6.4 3.9-1.2.5-2.3 1.1-3.3 2-.8.7-1.6 1.5-1.9 2.5-.3.9.2 1.9 1 2.4 1 .6 2.1.9 3.2 1 1.5.2 3 .2 4.4.1 2.9-.2 5.8-.8 8.6-1.5.3-.1.4.4.1.4-2.3 1.2-4.6 2.1-7.1 2.8-2.3.6-4.7.9-7.1 1-2.4-.1-5-.5-7-1.9-1.4-1.2-2.2-3-2.2-4.8v-.1c0-1.9.7-3.8 1.6-5.5 1.2-2.4 2.8-4.6 4.6-6.5 3.1-3.4 7-6.1 11.2-8 4.4-2 9-3.4 13.8-3.7z" fill="white"/>
|
|
892
|
+
</svg>`;
|
|
893
|
+
var MINIMAX_ICON = `<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48" fill="none">
|
|
894
|
+
<rect width="48" height="48" fill="#FF6B6B"/>
|
|
895
|
+
<text x="50%" y="50%" dominant-baseline="middle" text-anchor="middle" fill="white" font-family="Arial, sans-serif" font-size="16" font-weight="bold">MM</text>
|
|
896
|
+
</svg>`;
|
|
897
|
+
var MOONSHOTAI_ICON = `<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48" fill="none">
|
|
898
|
+
<rect width="48" height="48" rx="24" fill="#16191E"/>
|
|
899
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.57811 31.3729L21.8866 35.2005C21.8673 36.2177 21.8973 37.2353 21.9766 38.2497L30.9105 40.6389C28.2608 41.7325 25.388 42.1773 22.5316 41.9363L22.2616 41.9123L22.1956 41.9063L22.0696 41.8928L21.9286 41.8778C21.85 41.8683 21.7715 41.8583 21.6931 41.8478L21.5326 41.8268L21.3676 41.8028C21.2072 41.7795 21.0472 41.754 20.8876 41.7263L20.8246 41.7143L20.7121 41.6948L20.5516 41.6648L20.4466 41.6423L20.3071 41.6138L20.1946 41.5898L20.0521 41.5598L19.9066 41.5253L19.7656 41.4923L19.6636 41.4668L19.5316 41.4338L19.3966 41.3978L19.2541 41.3603L19.1311 41.3258L18.9676 41.2808L18.8746 41.2508L18.7486 41.2133L18.6091 41.1713L18.4516 41.1204L18.3646 41.0919L18.2446 41.0529L18.1096 41.0064L18.0106 40.9704C17.9885 40.963 17.9665 40.9555 17.9446 40.9479L17.8426 40.9104L17.6911 40.8549L17.6056 40.8219L17.4856 40.7769L17.3551 40.7244L17.2231 40.6719L17.1046 40.6239L16.9621 40.5639L16.8676 40.5219L16.7731 40.4814C16.7525 40.4725 16.732 40.4635 16.7116 40.4544L16.6126 40.4094L16.4581 40.3389L16.3801 40.3029L16.2361 40.2339L16.1431 40.189L16.0171 40.129L15.8881 40.063L15.7486 39.9925L15.6706 39.952L15.5161 39.8695L15.4306 39.8245L15.3436 39.7765C15.3205 39.7636 15.2975 39.7506 15.2746 39.7375L15.1336 39.658L15.0436 39.607L14.9671 39.562L14.8591 39.5005L14.7361 39.4255L14.5966 39.3415L14.5186 39.2935L14.3926 39.2141L14.3011 39.1556L14.1826 39.0806L14.0776 39.0101L13.9981 38.9576C13.971 38.9397 13.944 38.9217 13.9171 38.9036L13.8511 38.8586L13.7851 38.8136C13.765 38.7997 13.745 38.7857 13.7251 38.7716L13.6396 38.7116L13.5256 38.6306L13.4221 38.5556L13.3111 38.4746L13.2271 38.4116L13.1131 38.3261L12.9991 38.2377L12.8701 38.1372L12.8026 38.0847L12.7066 38.0067L12.5956 37.9167L12.4621 37.8072L12.3931 37.7487L12.3241 37.6902C12.3025 37.6718 12.281 37.6533 12.2596 37.6347L12.1921 37.5747L12.1006 37.4952L11.9956 37.4022L11.8936 37.3122L11.8006 37.2253L11.7001 37.1323L11.6206 37.0573L11.4886 36.9313C11.4388 36.8831 11.3893 36.8346 11.3401 36.7858L11.2966 36.7438L11.2351 36.6808L11.1316 36.5758L11.0566 36.4993L10.9816 36.4198C10.8959 36.332 10.8119 36.2424 10.7296 36.1514L10.6096 36.0194L10.5166 35.9144L10.4101 35.7944L10.3471 35.7209L10.2676 35.6279L10.1806 35.5259L10.1116 35.4419C10.098 35.4255 10.0845 35.409 10.0711 35.3924L10.0036 35.31L9.9046 35.187L9.8431 35.109L9.7681 35.013L9.7381 34.9755C8.87983 33.8631 8.15489 32.654 7.57811 31.3729ZM6.04811 22.6828L23.0776 27.2378C22.7925 28.2307 22.5577 29.2374 22.3741 30.254L38.5995 34.5945C37.7934 35.6981 36.865 36.7069 35.832 37.6017L6.98561 29.8835L6.96161 29.8145L6.90911 29.6585C6.88359 29.5822 6.85859 29.5057 6.83411 29.4291L6.82361 29.3946C6.70815 29.0276 6.6046 28.6569 6.51311 28.2832L6.46811 28.0942L6.44111 27.9742L6.40961 27.8287L6.38261 27.7072L6.35561 27.5723L6.33011 27.4463L6.30311 27.3053C6.26411 27.0938 6.22811 26.8808 6.19661 26.6664L6.17111 26.4894L6.15461 26.3649L6.13511 26.2119C6.12507 26.1315 6.11557 26.051 6.10661 25.9704L6.09911 25.8999C5.98704 24.8312 5.96997 23.7546 6.04811 22.6828ZM8.4376 14.9586L26.3595 19.7521C25.8076 20.6595 25.3021 21.5984 24.8446 22.5643L41.787 27.0968C41.574 28.3267 41.235 29.5146 40.785 30.6439L23.4601 26.0094L6.18611 21.3899L6.20861 21.2399L6.22061 21.1664L6.23561 21.0659L6.25811 20.9354L6.28511 20.7885C6.32411 20.5665 6.36911 20.346 6.41711 20.1255L6.45911 19.9396L6.48911 19.8121L6.52511 19.6666C6.55811 19.5316 6.59261 19.3966 6.63011 19.2646L6.67211 19.1116L6.70661 18.9872L6.75161 18.8372L6.78911 18.7142L6.83411 18.5702L6.87311 18.4472L6.91961 18.3047C7.30804 17.1397 7.81573 16.0178 8.4346 14.9571L8.4376 14.9586ZM15.1006 8.35926L32.028 12.8858C31.1348 13.6965 30.2893 14.5583 29.496 15.467L41.2305 18.6062C41.631 19.8841 41.8935 21.2219 42 22.6033L9.1591 13.8202L9.2266 13.7227L9.2671 13.6627L9.3271 13.5802L9.3961 13.4827L9.4786 13.3687L9.5596 13.2607L9.6556 13.1318L9.7306 13.0343L9.8161 12.9248L9.8986 12.8198L9.9886 12.7088L10.0711 12.6053L10.1686 12.4898L10.2496 12.3908L10.3486 12.2753L10.4281 12.1854L10.5361 12.0624L10.6156 11.9724L10.7161 11.8614L10.7971 11.7744L10.9066 11.6574L10.9936 11.5674L11.0881 11.4669L11.3401 11.212L11.4901 11.065L11.5786 10.981L11.6926 10.8745C12.7264 9.90579 13.8702 9.06161 15.1006 8.35926ZM24.0256 6.0015H24.1711L24.2941 6.003L24.3976 6.0045L24.4786 6.0075L24.5806 6.0105L24.6496 6.012L24.7636 6.0165L24.8341 6.0195L24.9241 6.024L25.0051 6.027L25.1356 6.0345L25.2931 6.045L25.5091 6.06149L25.6411 6.07199L25.707 6.07799L25.8226 6.08999L25.9455 6.10199L26.016 6.10949L26.169 6.12749L26.244 6.13649L26.406 6.15748L26.5275 6.17248L26.5905 6.18148L26.688 6.19648L26.9985 6.24447L27.1035 6.26247L27.201 6.27897L27.411 6.31797L27.549 6.34496L27.714 6.37796L27.783 6.39296L27.8955 6.41696L27.957 6.43196L28.05 6.45145L28.113 6.46645L28.2105 6.48895L28.284 6.50695L28.3905 6.53244L28.5345 6.56844L28.7025 6.61344L28.872 6.65843L29.0415 6.70643L29.1165 6.72892L29.2215 6.75892L29.3385 6.79492L29.448 6.82941L29.523 6.85341L29.598 6.87741L29.712 6.91491L29.8605 6.9644L30.0135 7.01839L30.0855 7.04389L30.1815 7.07839L30.321 7.12938L30.486 7.19088L30.66 7.25837L30.81 7.31836L30.8805 7.34836L30.9705 7.38436L31.032 7.41135L31.1265 7.45035L31.1865 7.47735L31.272 7.51484L31.437 7.58684L31.587 7.65583L31.698 7.70832L31.8105 7.76232L31.9005 7.80431L32.0385 7.87331L32.175 7.9408L32.328 8.01879L32.4075 8.06079L32.481 8.09978L32.55 8.13578L32.64 8.18527L32.7015 8.21827L32.7795 8.26177L32.9115 8.33676L33.0705 8.42675L33.201 8.50324L33.2865 8.55423L33.366 8.60223L33.51 8.69072L33.642 8.77321L33.789 8.8662L33.843 8.9022L33.939 8.96369L34.065 9.04768L34.125 9.08818L34.218 9.15117L34.311 9.21567L34.3455 9.24116C34.4265 9.29666 34.5075 9.35365 34.587 9.41215L34.7115 9.50214L34.809 9.57413L34.893 9.63862L35.022 9.73611L35.145 9.8321L35.205 9.8771L35.28 9.93859L35.409 10.0421L35.5275 10.1396L35.655 10.2461C36.723 11.146 37.6845 12.1704 38.5185 13.2922L16.8331 7.49235L16.9261 7.45185L17.0236 7.40985L17.1451 7.35886L17.2741 7.30636C17.4436 7.23887 17.6146 7.17138 17.7856 7.10988L17.9296 7.05739L18.0691 7.0079L18.1951 6.9629L18.3391 6.9164C18.4696 6.87141 18.6031 6.82941 18.7351 6.78892L18.8716 6.74842L19.0006 6.71093L19.1536 6.66593L19.2811 6.63143L19.4311 6.59244L19.5601 6.55644L19.6951 6.52195L19.8316 6.48895L19.9741 6.45595L20.1091 6.42596L20.2561 6.39446L20.3926 6.36446L20.5351 6.33746L20.6731 6.31047L20.8231 6.28347L20.9596 6.25947L21.1066 6.23398L21.2446 6.21298L21.3901 6.19048L21.5281 6.17098L21.6811 6.15148L21.8176 6.13349L21.9751 6.11549L22.1101 6.10049L22.2676 6.08549C22.4071 6.07049 22.5466 6.05849 22.6876 6.04949L22.8466 6.0375L22.9816 6.03L23.1466 6.021L23.2861 6.015L23.4361 6.009L23.5816 6.006L23.7301 6.003L24.0256 6V6.0015Z" fill="white"/>
|
|
900
|
+
</svg>`;
|
|
901
|
+
var XAI_ICON = `<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48" fill="none">
|
|
902
|
+
<rect width="48" height="48" fill="black"/>
|
|
903
|
+
<path d="M10.008 19.311L24.0672 40H30.3165L16.2557 19.311H10.008ZM16.2509 30.8017L10 40H16.2541L19.3771 35.4017L16.2509 30.8017ZM31.7459 8L20.941 23.8993L24.0672 28.5009L38 8H31.7459ZM32.8777 17.8382V40H38V10.3008L32.8777 17.8382Z" fill="white"/>
|
|
904
|
+
</svg>`;
|
|
905
|
+
var ZAI_ICON = `<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48" fill="none">
|
|
906
|
+
<rect width="48" height="48" rx="4" fill="#2D2D2D"/>
|
|
907
|
+
<path d="M35 10H13v4h14L13 34v4h22v-4H21l14-20V10z" fill="white"/>
|
|
908
|
+
</svg>`;
|
|
909
|
+
var EXAONE_ICON = `<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48" fill="none">
|
|
910
|
+
<rect width="48" height="48" fill="#A8E6A3"/>
|
|
911
|
+
<text x="50%" y="50%" dominant-baseline="middle" text-anchor="middle" fill="#2C3E50" font-family="Arial, sans-serif" font-size="14" font-weight="bold">EXA</text>
|
|
912
|
+
</svg>`;
|
|
913
|
+
var OPENROUTER_ICON = `<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none">
|
|
914
|
+
<rect width="24" height="24" fill="#6366F1"/>
|
|
915
|
+
<path fill="white" d="M16.804 1.957l7.22 4.105v.087L16.73 10.21l.017-2.117-.821-.03c-1.059-.028-1.611.002-2.268.11-1.064.175-2.038.577-3.147 1.352L8.345 11.03c-.284.195-.495.336-.68.455l-.515.322-.397.234.385.23.53.338c.476.314 1.17.796 2.701 1.866 1.11.775 2.083 1.177 3.147 1.352l.3.045c.694.091 1.375.094 2.825.033l.022-2.159 7.22 4.105v.087L16.589 22l.014-1.862-.635.022c-1.386.042-2.137.002-3.138-.162-1.694-.28-3.26-.926-4.881-2.059l-2.158-1.5a21.997 21.997 0 00-.755-.498l-.467-.28a55.927 55.927 0 00-.76-.43C2.908 14.73.563 14.116 0 14.116V9.888l.14.004c.564-.007 2.91-.622 3.809-1.124l1.016-.58.438-.274c.428-.28 1.072-.726 2.686-1.853 1.621-1.133 3.186-1.78 4.881-2.059 1.152-.19 1.974-.213 3.814-.138l.02-1.907z" transform="scale(0.75) translate(4 4)"/>
|
|
916
|
+
</svg>`;
|
|
917
|
+
var OPENAI_ICON = `<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48" fill="none">
|
|
918
|
+
<rect width="48" height="48" rx="24" fill="white"/>
|
|
919
|
+
<path d="M19.3418 18.5599V14.7599C19.3418 14.4399 19.4608 14.1998 19.7382 14.04L27.3102 9.63997C28.3409 9.03999 29.5699 8.76014 30.8383 8.76014C35.5954 8.76014 38.6085 12.4802 38.6085 16.4401C38.6085 16.72 38.6085 17.04 38.5687 17.3601L30.7194 12.72C30.2437 12.4401 29.7678 12.4401 29.2922 12.72L19.3418 18.5599ZM37.0226 33.36V24.2799C37.0226 23.7197 36.7846 23.3197 36.309 23.0398L26.3586 17.1998L29.6093 15.3197C29.8868 15.1599 30.1247 15.1599 30.4022 15.3197L37.9741 19.7197C40.1547 20.9999 41.6213 23.7197 41.6213 26.3596C41.6213 29.3995 39.8375 32.1999 37.0226 33.36ZM17.0029 25.3601L13.7522 23.4402C13.4748 23.2804 13.3557 23.0402 13.3557 22.7202V13.9203C13.3557 9.64039 16.6065 6.40016 21.0069 6.40016C22.6722 6.40016 24.2179 6.96029 25.5265 7.96025L17.7168 12.5204C17.2412 12.8003 17.0033 13.2002 17.0033 13.7605L17.0029 25.3601ZM24 29.44L19.3418 26.8001V21.2003L24 18.5604L28.6578 21.2003V26.8001L24 29.44ZM26.993 41.6002C25.3278 41.6002 23.7821 41.04 22.4735 40.0402L30.2831 35.4799C30.7588 35.2001 30.9967 34.8001 30.9967 34.2399V22.6399L34.2873 24.5598C34.5646 24.7196 34.6837 24.9597 34.6837 25.2798V34.0797C34.6837 38.3596 31.3931 41.6002 26.993 41.6002ZM17.5975 32.6802L10.0255 28.2803C7.84493 27.0001 6.37833 24.2803 6.37833 21.6404C6.37833 18.5604 8.20193 15.8004 11.0164 14.6403V23.7602C11.0164 24.3204 11.2544 24.7204 11.73 25.0003L21.641 30.8001L18.3902 32.6802C18.1129 32.84 17.8749 32.84 17.5975 32.6802ZM17.1617 39.2402C12.682 39.2402 9.39151 35.8402 9.39151 31.6401C9.39151 31.3201 9.43125 31.0001 9.47066 30.68L17.2803 35.2402C17.7559 35.5201 18.2319 35.5201 18.7074 35.2402L28.6578 29.4404V33.2404C28.6578 33.5605 28.5388 33.8005 28.2614 33.9604L20.6894 38.3604C19.6586 38.9603 18.4301 39.2402 17.1617 39.2402ZM26.993 44C31.7899 44 35.7936 40.5601 36.7057 36C41.1457 34.8399 44 30.6399 44 26.36C44 23.5598 42.8108 20.8401 40.6701 18.88C40.8683 18.0399 40.9872 17.1998 40.9872 16.3602C40.9872 10.6403 36.3885 6.35998 31.0763 6.35998C30.0062 6.35998 28.9754 6.51979 27.9446 6.88001C26.1604 5.11992 23.7025 4 21.0069 4C16.2101 4 12.2064 7.4398 11.2943 12C6.8543 13.1601 4 17.3601 4 21.6399C4 24.4401 5.18916 27.1599 7.32995 29.1199C7.13174 29.96 7.01277 30.8001 7.01277 31.6398C7.01277 37.3597 11.6114 41.6399 16.9236 41.6399C17.9938 41.6399 19.0246 41.4801 20.0554 41.1199C21.8392 42.88 24.2971 44 26.993 44Z" fill="black"/>
|
|
920
|
+
</svg>`;
|
|
921
|
+
var MISTRAL_ICON = `<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48" fill="none">
|
|
922
|
+
<rect width="48" height="48" fill="#FF7000"/>
|
|
923
|
+
<path d="M10 10h8v8h-8zM18 10h4v8h-4zM26 10h4v8h-4zM30 10h8v8h-8zM10 18h8v4h-8zM30 18h8v4h-8zM10 22h8v4h-8zM18 22h12v4H18zM30 22h8v4h-8zM10 26h8v4h-8zM30 26h8v4h-8zM10 30h8v8h-8zM18 30h4v8h-4zM26 30h4v8h-4zM30 30h8v8h-8z" fill="white"/>
|
|
924
|
+
</svg>`;
|
|
925
|
+
var COHERE_ICON = `<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48" fill="none">
|
|
926
|
+
<rect width="48" height="48" fill="#39594D"/>
|
|
927
|
+
<circle cx="24" cy="24" r="12" fill="#D18EE2"/>
|
|
928
|
+
<circle cx="24" cy="24" r="6" fill="#39594D"/>
|
|
929
|
+
</svg>`;
|
|
930
|
+
var PERPLEXITY_ICON = `<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48" fill="none">
|
|
931
|
+
<rect width="48" height="48" fill="#20808D"/>
|
|
932
|
+
<path d="M24 8L36 20v16L24 40 12 28V16L24 8z" stroke="white" stroke-width="2" fill="none"/>
|
|
933
|
+
<path d="M24 8v32M12 20h24M12 28h24" stroke="white" stroke-width="2"/>
|
|
934
|
+
</svg>`;
|
|
935
|
+
var LAB_ICONS = {
|
|
936
|
+
anthropic: ANTHROPIC_ICON,
|
|
937
|
+
google: GOOGLE_ICON,
|
|
938
|
+
"google-ai": GOOGLE_ICON,
|
|
939
|
+
meta: META_ICON,
|
|
940
|
+
"meta-llama": META_ICON,
|
|
941
|
+
nvidia: NVIDIA_ICON,
|
|
942
|
+
alibaba: ALIBABA_ICON,
|
|
943
|
+
deepseek: DEEPSEEK_ICON,
|
|
944
|
+
qwen: QWEN_ICON,
|
|
945
|
+
minimax: MINIMAX_ICON,
|
|
946
|
+
moonshotai: MOONSHOTAI_ICON,
|
|
947
|
+
moonshot: MOONSHOTAI_ICON,
|
|
948
|
+
"x-ai": XAI_ICON,
|
|
949
|
+
xai: XAI_ICON,
|
|
950
|
+
"z-ai": ZAI_ICON,
|
|
951
|
+
zai: ZAI_ICON,
|
|
952
|
+
zhipu: ZAI_ICON,
|
|
953
|
+
exaone: EXAONE_ICON,
|
|
954
|
+
lgai: EXAONE_ICON,
|
|
955
|
+
openrouter: OPENROUTER_ICON,
|
|
956
|
+
openai: OPENAI_ICON,
|
|
957
|
+
"openai-sdk": OPENAI_ICON,
|
|
958
|
+
mistral: MISTRAL_ICON,
|
|
959
|
+
mistralai: MISTRAL_ICON,
|
|
960
|
+
cohere: COHERE_ICON,
|
|
961
|
+
perplexity: PERPLEXITY_ICON
|
|
962
|
+
};
|
|
963
|
+
function svgToDataUri(svg) {
|
|
964
|
+
const encoded = encodeURIComponent(svg).replace(/'/g, "%27").replace(/"/g, "%22");
|
|
965
|
+
return `data:image/svg+xml,${encoded}`;
|
|
966
|
+
}
|
|
967
|
+
function getLabIconDataUri(labId) {
|
|
968
|
+
const icon = LAB_ICONS[labId.toLowerCase()];
|
|
969
|
+
if (icon) {
|
|
970
|
+
return svgToDataUri(icon);
|
|
971
|
+
}
|
|
972
|
+
return void 0;
|
|
973
|
+
}
|
|
974
|
+
function extractLabFromModelId(modelId) {
|
|
975
|
+
if (!modelId || !modelId.includes("/")) {
|
|
976
|
+
return void 0;
|
|
977
|
+
}
|
|
978
|
+
return modelId.split("/")[0].toLowerCase();
|
|
979
|
+
}
|
|
980
|
+
function getModelIconDataUri(modelId) {
|
|
981
|
+
const lab = extractLabFromModelId(modelId);
|
|
982
|
+
if (lab) {
|
|
983
|
+
return getLabIconDataUri(lab);
|
|
984
|
+
}
|
|
985
|
+
return void 0;
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
// src/OpenRouterProvider.ts
|
|
989
|
+
var OpenRouterProvider = class _OpenRouterProvider {
|
|
990
|
+
name = "openrouter";
|
|
991
|
+
specificationVersion = "1";
|
|
992
|
+
client = null;
|
|
993
|
+
config;
|
|
994
|
+
constructor(config) {
|
|
995
|
+
this.config = config;
|
|
996
|
+
}
|
|
997
|
+
async getClient() {
|
|
998
|
+
if (!this.client) {
|
|
999
|
+
const { OpenRouter } = await import("@openrouter/sdk");
|
|
1000
|
+
this.client = new OpenRouter({
|
|
1001
|
+
apiKey: this.config.apiKey,
|
|
1002
|
+
serverURL: this.config.baseUrl,
|
|
1003
|
+
timeoutMs: this.config.timeout
|
|
1004
|
+
});
|
|
1005
|
+
}
|
|
1006
|
+
return this.client;
|
|
1007
|
+
}
|
|
1008
|
+
supportsModel(_modelId) {
|
|
1009
|
+
return true;
|
|
1010
|
+
}
|
|
1011
|
+
/**
|
|
1012
|
+
* Get the icon for a model as a data URI.
|
|
1013
|
+
* Extracts the AI lab/organization from the model ID prefix and returns
|
|
1014
|
+
* the corresponding SVG icon as a data URI.
|
|
1015
|
+
* For example, 'anthropic/claude-3-opus' returns the Anthropic icon.
|
|
1016
|
+
* Falls back to the OpenRouter icon for unknown labs.
|
|
1017
|
+
*/
|
|
1018
|
+
getIcon(modelId) {
|
|
1019
|
+
if (!modelId) {
|
|
1020
|
+
return svgToDataUri(OPENROUTER_ICON);
|
|
1021
|
+
}
|
|
1022
|
+
const modelIcon = getModelIconDataUri(modelId);
|
|
1023
|
+
if (modelIcon) {
|
|
1024
|
+
return modelIcon;
|
|
1025
|
+
}
|
|
1026
|
+
return svgToDataUri(OPENROUTER_ICON);
|
|
1027
|
+
}
|
|
1028
|
+
// ============================================================================
|
|
1029
|
+
// Model Capabilities
|
|
1030
|
+
// ============================================================================
|
|
1031
|
+
/** Cache for model capabilities to avoid repeated API calls */
|
|
1032
|
+
static modelsCache = null;
|
|
1033
|
+
static modelsCacheTime = 0;
|
|
1034
|
+
static CACHE_TTL = 5 * 60 * 1e3;
|
|
1035
|
+
// 5 minutes
|
|
1036
|
+
/**
|
|
1037
|
+
* Get capabilities for a specific model.
|
|
1038
|
+
* Fetches from OpenRouter's models API which provides rich metadata.
|
|
1039
|
+
*/
|
|
1040
|
+
async getModelCapabilities(modelId) {
|
|
1041
|
+
try {
|
|
1042
|
+
const models = await this.fetchModelsWithCache();
|
|
1043
|
+
const model = models.find((m) => m.id === modelId);
|
|
1044
|
+
if (!model) {
|
|
1045
|
+
return null;
|
|
1046
|
+
}
|
|
1047
|
+
return this.mapOpenRouterCapabilities(model);
|
|
1048
|
+
} catch (error) {
|
|
1049
|
+
console.error(`Failed to fetch capabilities for ${modelId}:`, error);
|
|
1050
|
+
return null;
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
async fetchModelsWithCache() {
|
|
1054
|
+
const now = Date.now();
|
|
1055
|
+
if (_OpenRouterProvider.modelsCache && now - _OpenRouterProvider.modelsCacheTime < _OpenRouterProvider.CACHE_TTL) {
|
|
1056
|
+
return _OpenRouterProvider.modelsCache;
|
|
1057
|
+
}
|
|
1058
|
+
const response = await fetch("https://openrouter.ai/api/v1/models");
|
|
1059
|
+
if (!response.ok) {
|
|
1060
|
+
throw new Error(`OpenRouter API error: ${response.status}`);
|
|
1061
|
+
}
|
|
1062
|
+
const data = await response.json();
|
|
1063
|
+
_OpenRouterProvider.modelsCache = data.data || [];
|
|
1064
|
+
_OpenRouterProvider.modelsCacheTime = now;
|
|
1065
|
+
return _OpenRouterProvider.modelsCache;
|
|
1066
|
+
}
|
|
1067
|
+
mapOpenRouterCapabilities(model) {
|
|
1068
|
+
const capabilities = {
|
|
1069
|
+
maxContextTokens: model.context_length,
|
|
1070
|
+
supportsStreaming: true,
|
|
1071
|
+
// OpenRouter always supports streaming
|
|
1072
|
+
supportsToolCalls: true,
|
|
1073
|
+
// Most models support it, exceptions below
|
|
1074
|
+
supportsJsonMode: true
|
|
1075
|
+
// Most modern models support it
|
|
1076
|
+
};
|
|
1077
|
+
if (model.architecture?.modality?.includes("image") || model.architecture?.modality?.includes("multimodal") || model.architecture?.modality === "text+image->text") {
|
|
1078
|
+
capabilities.supportsImages = true;
|
|
1079
|
+
} else {
|
|
1080
|
+
capabilities.supportsImages = false;
|
|
1081
|
+
}
|
|
1082
|
+
if (model.top_provider?.max_completion_tokens) {
|
|
1083
|
+
capabilities.maxOutputTokens = model.top_provider.max_completion_tokens;
|
|
1084
|
+
}
|
|
1085
|
+
if (model.id.includes("o1") || model.id.includes("o3") || model.id.includes("o4")) {
|
|
1086
|
+
capabilities.reasoningLevels = { 0: null, 33: "low", 66: "medium", 100: "high" };
|
|
1087
|
+
if (model.id.includes("o1-preview") || model.id.includes("o1-mini")) {
|
|
1088
|
+
capabilities.supportsToolCalls = false;
|
|
1089
|
+
capabilities.supportsJsonMode = false;
|
|
1090
|
+
}
|
|
1091
|
+
} else if (model.id.includes("claude") && model.id.includes("thinking")) {
|
|
1092
|
+
capabilities.reasoningLevels = { 0: null, 100: "enabled" };
|
|
1093
|
+
}
|
|
1094
|
+
return capabilities;
|
|
1095
|
+
}
|
|
1096
|
+
/**
|
|
1097
|
+
* Get list of available models from OpenRouter.
|
|
1098
|
+
* Fetches from OpenRouter's public models API with caching.
|
|
1099
|
+
*
|
|
1100
|
+
* @param filter - Optional search string to filter models by name/id
|
|
1101
|
+
*/
|
|
1102
|
+
async getModels(filter) {
|
|
1103
|
+
try {
|
|
1104
|
+
const rawModels = await this.fetchModelsWithCache();
|
|
1105
|
+
let models = rawModels.map((m) => this.mapToProviderModelInfo(m));
|
|
1106
|
+
if (filter) {
|
|
1107
|
+
const lowerFilter = filter.toLowerCase();
|
|
1108
|
+
models = models.filter(
|
|
1109
|
+
(m) => m.id.toLowerCase().includes(lowerFilter) || m.name.toLowerCase().includes(lowerFilter) || m.description && m.description.toLowerCase().includes(lowerFilter)
|
|
1110
|
+
);
|
|
1111
|
+
}
|
|
1112
|
+
return models;
|
|
1113
|
+
} catch (error) {
|
|
1114
|
+
console.error("Failed to fetch models from OpenRouter:", error);
|
|
1115
|
+
return [];
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
mapToProviderModelInfo(model) {
|
|
1119
|
+
return {
|
|
1120
|
+
id: model.id,
|
|
1121
|
+
name: model.name,
|
|
1122
|
+
description: model.architecture?.modality || void 0,
|
|
1123
|
+
contextLength: model.context_length,
|
|
1124
|
+
iconId: this.getIcon(model.id),
|
|
1125
|
+
slug: model.id.replace(/[/:]/g, "-")
|
|
1126
|
+
// URL-safe slug
|
|
1127
|
+
};
|
|
1128
|
+
}
|
|
1129
|
+
// ============================================================================
|
|
1130
|
+
// Generation Methods
|
|
1131
|
+
// ============================================================================
|
|
1132
|
+
async generate(request) {
|
|
1133
|
+
const client = await this.getClient();
|
|
1134
|
+
try {
|
|
1135
|
+
const params = buildCreateParams(request);
|
|
1136
|
+
if (this.config.providers && this.config.providers.length > 0) {
|
|
1137
|
+
params.provider = { only: this.config.providers };
|
|
1138
|
+
}
|
|
1139
|
+
const response = await client.beta.responses.send({
|
|
1140
|
+
...params,
|
|
1141
|
+
stream: false
|
|
1142
|
+
});
|
|
1143
|
+
return transformResponse(response);
|
|
1144
|
+
} catch (error) {
|
|
1145
|
+
throw this.toProviderError(error);
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
async stream(request) {
|
|
1149
|
+
const self = this;
|
|
1150
|
+
const apiKey = this.config.apiKey;
|
|
1151
|
+
const baseUrl = this.config.baseUrl || "https://openrouter.ai/api/v1";
|
|
1152
|
+
try {
|
|
1153
|
+
const params = buildCreateParams(request);
|
|
1154
|
+
if (this.config.providers && this.config.providers.length > 0) {
|
|
1155
|
+
params.provider = { only: this.config.providers };
|
|
1156
|
+
}
|
|
1157
|
+
const response = await fetch(`${baseUrl}/responses`, {
|
|
1158
|
+
method: "POST",
|
|
1159
|
+
headers: {
|
|
1160
|
+
"Content-Type": "application/json",
|
|
1161
|
+
"Authorization": `Bearer ${apiKey}`
|
|
1162
|
+
},
|
|
1163
|
+
body: JSON.stringify({
|
|
1164
|
+
...params,
|
|
1165
|
+
stream: true
|
|
1166
|
+
}),
|
|
1167
|
+
signal: request.signal
|
|
1168
|
+
});
|
|
1169
|
+
if (!response.ok) {
|
|
1170
|
+
const errorText = await response.text();
|
|
1171
|
+
let errorMessage = `OpenRouter API error: ${response.status}`;
|
|
1172
|
+
try {
|
|
1173
|
+
const errorJson = JSON.parse(errorText);
|
|
1174
|
+
errorMessage = errorJson.error?.message || errorJson.message || errorMessage;
|
|
1175
|
+
} catch {
|
|
1176
|
+
errorMessage = errorText || errorMessage;
|
|
1177
|
+
}
|
|
1178
|
+
throw new ProviderError(errorMessage, "invalid_request", response.status);
|
|
1179
|
+
}
|
|
1180
|
+
return {
|
|
1181
|
+
async *[Symbol.asyncIterator]() {
|
|
1182
|
+
const state = createStreamState();
|
|
1183
|
+
let finishChunk = null;
|
|
1184
|
+
let responseId = null;
|
|
1185
|
+
try {
|
|
1186
|
+
for await (const chunk of parseSSEStream(response, state)) {
|
|
1187
|
+
if (chunk.type === "finish") {
|
|
1188
|
+
finishChunk = chunk;
|
|
1189
|
+
responseId = chunk.responseId || null;
|
|
1190
|
+
} else {
|
|
1191
|
+
yield chunk;
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
if (finishChunk) {
|
|
1195
|
+
const finishWithMeta = {
|
|
1196
|
+
...finishChunk,
|
|
1197
|
+
// Include what's needed to fetch metadata later
|
|
1198
|
+
_asyncMetadata: responseId ? {
|
|
1199
|
+
generationId: responseId,
|
|
1200
|
+
apiKey,
|
|
1201
|
+
baseUrl
|
|
1202
|
+
} : void 0
|
|
1203
|
+
};
|
|
1204
|
+
yield finishWithMeta;
|
|
1205
|
+
}
|
|
1206
|
+
} catch (error) {
|
|
1207
|
+
const providerError = self.toProviderError(error);
|
|
1208
|
+
yield createErrorChunk(providerError.message, providerError.code);
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
};
|
|
1212
|
+
} catch (error) {
|
|
1213
|
+
throw this.toProviderError(error);
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
// ============================================================================
|
|
1217
|
+
// Response Metadata (Async)
|
|
1218
|
+
// ============================================================================
|
|
1219
|
+
/**
|
|
1220
|
+
* Fetch additional metadata about a completed response.
|
|
1221
|
+
* Called asynchronously after the response is received.
|
|
1222
|
+
*
|
|
1223
|
+
* Uses OpenRouter's /generation endpoint to fetch accurate provider info,
|
|
1224
|
+
* token counts, and cost data.
|
|
1225
|
+
*/
|
|
1226
|
+
async getResponseMetadata(summary, signal) {
|
|
1227
|
+
if (!summary.responseId) {
|
|
1228
|
+
return null;
|
|
1229
|
+
}
|
|
1230
|
+
const metadata = await fetchGenerationMetadata(
|
|
1231
|
+
this.config.apiKey,
|
|
1232
|
+
summary.responseId,
|
|
1233
|
+
this.config.baseUrl || "https://openrouter.ai/api/v1",
|
|
1234
|
+
signal
|
|
1235
|
+
);
|
|
1236
|
+
if (!metadata) {
|
|
1237
|
+
return null;
|
|
1238
|
+
}
|
|
1239
|
+
return {
|
|
1240
|
+
actual_provider: metadata.providerName,
|
|
1241
|
+
native_tokens_prompt: metadata.nativePromptTokens,
|
|
1242
|
+
native_tokens_completion: metadata.nativeCompletionTokens,
|
|
1243
|
+
generation_cost: metadata.totalCost,
|
|
1244
|
+
generation_latency: metadata.latency
|
|
1245
|
+
};
|
|
1246
|
+
}
|
|
1247
|
+
// ============================================================================
|
|
1248
|
+
// Error Handling
|
|
1249
|
+
// ============================================================================
|
|
1250
|
+
toProviderError(error) {
|
|
1251
|
+
if (error instanceof ProviderError) {
|
|
1252
|
+
return error;
|
|
1253
|
+
}
|
|
1254
|
+
if (error instanceof Error) {
|
|
1255
|
+
const anyError = error;
|
|
1256
|
+
const status = anyError.status || anyError.statusCode;
|
|
1257
|
+
const retryAfter = anyError.headers?.["retry-after"] ? parseInt(anyError.headers["retry-after"], 10) : void 0;
|
|
1258
|
+
if (status === 429) {
|
|
1259
|
+
return new ProviderError(error.message, "rate_limit", status, retryAfter);
|
|
1260
|
+
}
|
|
1261
|
+
if (status === 401 || status === 403) {
|
|
1262
|
+
return new ProviderError(error.message, "auth_error", status);
|
|
1263
|
+
}
|
|
1264
|
+
if (status === 400) {
|
|
1265
|
+
return new ProviderError(error.message, "invalid_request", status);
|
|
1266
|
+
}
|
|
1267
|
+
if (status >= 500) {
|
|
1268
|
+
return new ProviderError(error.message, "server_error", status);
|
|
1269
|
+
}
|
|
1270
|
+
if (error.name === "AbortError" || anyError.code === "ETIMEDOUT") {
|
|
1271
|
+
return new ProviderError(error.message, "timeout");
|
|
1272
|
+
}
|
|
1273
|
+
return new ProviderError(error.message, "unknown", status);
|
|
1274
|
+
}
|
|
1275
|
+
return new ProviderError(String(error), "unknown");
|
|
1276
|
+
}
|
|
1277
|
+
// ============================================================================
|
|
1278
|
+
// Inspection
|
|
1279
|
+
// ============================================================================
|
|
1280
|
+
/**
|
|
1281
|
+
* Transform a ProviderRequest to OpenRouter Responses API format for inspection.
|
|
1282
|
+
* Returns the exact request body that would be sent to OpenRouter, with base64 data truncated.
|
|
1283
|
+
*/
|
|
1284
|
+
async inspectRequest(request) {
|
|
1285
|
+
const params = buildCreateParams(request);
|
|
1286
|
+
if (this.config.providers && this.config.providers.length > 0) {
|
|
1287
|
+
params.provider = { only: this.config.providers };
|
|
1288
|
+
}
|
|
1289
|
+
return {
|
|
1290
|
+
body: truncateBase64(params),
|
|
1291
|
+
messagesPath: "input",
|
|
1292
|
+
metadata: {
|
|
1293
|
+
endpoint: "https://openrouter.ai/api/v1/responses"
|
|
1294
|
+
}
|
|
1295
|
+
};
|
|
1296
|
+
}
|
|
1297
|
+
};
|
|
1298
|
+
|
|
1299
|
+
// src/providerOptions.ts
|
|
1300
|
+
import { z } from "zod";
|
|
1301
|
+
var percentileSchema = z.union([
|
|
1302
|
+
z.number(),
|
|
1303
|
+
z.object({
|
|
1304
|
+
p50: z.number().optional(),
|
|
1305
|
+
p75: z.number().optional(),
|
|
1306
|
+
p90: z.number().optional(),
|
|
1307
|
+
p99: z.number().optional()
|
|
1308
|
+
})
|
|
1309
|
+
]);
|
|
1310
|
+
var providerRoutingSchema = z.object({
|
|
1311
|
+
/** Provider slugs to try in order (e.g., ['anthropic', 'openai']) */
|
|
1312
|
+
order: z.array(z.string()).optional(),
|
|
1313
|
+
/** Allow fallback to other providers if preferred unavailable (default: true) */
|
|
1314
|
+
allow_fallbacks: z.boolean().optional(),
|
|
1315
|
+
/** Only use providers that support all parameters in the request */
|
|
1316
|
+
require_parameters: z.boolean().optional(),
|
|
1317
|
+
/** Control data storage policies: 'allow' or 'deny' */
|
|
1318
|
+
data_collection: z.enum(["allow", "deny"]).optional(),
|
|
1319
|
+
/** Restrict to Zero Data Retention endpoints only */
|
|
1320
|
+
zdr: z.boolean().optional(),
|
|
1321
|
+
/** Restrict to these providers only (exclusive list) */
|
|
1322
|
+
only: z.array(z.string()).optional(),
|
|
1323
|
+
/** Skip these providers */
|
|
1324
|
+
ignore: z.array(z.string()).optional(),
|
|
1325
|
+
/** Sort providers by price, throughput, or latency */
|
|
1326
|
+
sort: z.union([
|
|
1327
|
+
z.enum(["price", "throughput", "latency"]),
|
|
1328
|
+
z.object({
|
|
1329
|
+
by: z.enum(["price", "throughput", "latency"]),
|
|
1330
|
+
partition: z.enum(["model", "none"]).optional()
|
|
1331
|
+
})
|
|
1332
|
+
]).optional(),
|
|
1333
|
+
/** Maximum price constraints (hard limit - request fails if no provider meets threshold) */
|
|
1334
|
+
max_price: z.object({
|
|
1335
|
+
/** Max price per million prompt tokens */
|
|
1336
|
+
prompt: z.number().optional(),
|
|
1337
|
+
/** Max price per million completion tokens */
|
|
1338
|
+
completion: z.number().optional(),
|
|
1339
|
+
/** Max price per request */
|
|
1340
|
+
request: z.number().optional(),
|
|
1341
|
+
/** Max price per image */
|
|
1342
|
+
image: z.number().optional()
|
|
1343
|
+
}).optional(),
|
|
1344
|
+
/** Allowed quantization levels */
|
|
1345
|
+
quantizations: z.array(z.enum([
|
|
1346
|
+
"int4",
|
|
1347
|
+
"int8",
|
|
1348
|
+
"fp4",
|
|
1349
|
+
"fp6",
|
|
1350
|
+
"fp8",
|
|
1351
|
+
"fp16",
|
|
1352
|
+
"bf16",
|
|
1353
|
+
"fp32",
|
|
1354
|
+
"unknown"
|
|
1355
|
+
])).optional(),
|
|
1356
|
+
/** Minimum throughput in tokens/sec (soft preference) */
|
|
1357
|
+
preferred_min_throughput: percentileSchema.optional(),
|
|
1358
|
+
/** Maximum latency in seconds (soft preference) */
|
|
1359
|
+
preferred_max_latency: percentileSchema.optional(),
|
|
1360
|
+
/** Restrict to models allowing text distillation */
|
|
1361
|
+
enforce_distillable_text: z.boolean().optional()
|
|
1362
|
+
}).passthrough();
|
|
1363
|
+
var openrouterProviderOptions = z.object({
|
|
1364
|
+
/** Provider routing configuration */
|
|
1365
|
+
provider: providerRoutingSchema.optional()
|
|
1366
|
+
}).passthrough();
|
|
1367
|
+
|
|
1368
|
+
// src/index.ts
|
|
1369
|
+
var openrouter = Object.assign(
|
|
1370
|
+
(config) => new OpenRouterProvider(config),
|
|
1371
|
+
{ providerOptions: openrouterProviderOptions }
|
|
1372
|
+
);
|
|
1373
|
+
export {
|
|
1374
|
+
OpenRouterProvider,
|
|
1375
|
+
fetchGenerationMetadata,
|
|
1376
|
+
openrouter,
|
|
1377
|
+
openrouterProviderOptions
|
|
1378
|
+
};
|
|
1379
|
+
//# sourceMappingURL=index.js.map
|