@anyr/ai-sdk-provider 0.1.4 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -20
- package/dist/index.d.cts +177 -27
- package/dist/index.d.ts +177 -27
- package/dist/index.js +247 -146
- package/dist/index.mjs +244 -146
- package/package.json +17 -13
package/dist/index.js
CHANGED
|
@@ -1,12 +1,35 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var provider = require('@ai-sdk/provider');
|
|
3
4
|
var providerUtils = require('@ai-sdk/provider-utils');
|
|
4
5
|
var zod = require('zod');
|
|
5
|
-
var provider = require('@ai-sdk/provider');
|
|
6
6
|
var sdk = require('@anyr/sdk');
|
|
7
7
|
|
|
8
|
-
// src/
|
|
9
|
-
|
|
8
|
+
// src/app-attribution.ts
|
|
9
|
+
var APP_SOURCE = "anyr-ai-sdk-provider";
|
|
10
|
+
var APP_TITLE = "AnyRouter AI SDK Provider";
|
|
11
|
+
var APP_VERSION = "0.1.7";
|
|
12
|
+
var PROVIDER_ATTRIBUTION = {
|
|
13
|
+
source: APP_SOURCE,
|
|
14
|
+
title: APP_TITLE,
|
|
15
|
+
version: APP_VERSION
|
|
16
|
+
};
|
|
17
|
+
function buildAppAttributionHeaders(app) {
|
|
18
|
+
if (!app) return {};
|
|
19
|
+
const headers = {};
|
|
20
|
+
const put = (name, value) => {
|
|
21
|
+
const trimmed = value?.trim();
|
|
22
|
+
if (trimmed) headers[name] = trimmed;
|
|
23
|
+
};
|
|
24
|
+
put("X-AnyRouter-Source", app.source);
|
|
25
|
+
put("X-AnyRouter-Title", app.title);
|
|
26
|
+
put("X-AnyRouter-Version", app.version);
|
|
27
|
+
put("HTTP-Referer", app.url);
|
|
28
|
+
const categories = app.categories?.map((c) => c.trim()).filter(Boolean);
|
|
29
|
+
if (categories?.length) headers["X-AnyRouter-Categories"] = categories.join(",");
|
|
30
|
+
return headers;
|
|
31
|
+
}
|
|
32
|
+
function convertToAnyRouterChatMessages(prompt, fileCache) {
|
|
10
33
|
const messages = [];
|
|
11
34
|
for (const message of prompt) {
|
|
12
35
|
switch (message.role) {
|
|
@@ -14,7 +37,7 @@ function convertToAnyRouterChatMessages(prompt) {
|
|
|
14
37
|
messages.push({ role: "system", content: message.content });
|
|
15
38
|
break;
|
|
16
39
|
case "user":
|
|
17
|
-
messages.push(convertUserMessage(message));
|
|
40
|
+
messages.push(convertUserMessage(message, fileCache));
|
|
18
41
|
break;
|
|
19
42
|
case "assistant":
|
|
20
43
|
messages.push(convertAssistantMessage(message));
|
|
@@ -34,7 +57,7 @@ function convertToAnyRouterChatMessages(prompt) {
|
|
|
34
57
|
}
|
|
35
58
|
return messages;
|
|
36
59
|
}
|
|
37
|
-
function convertUserMessage(message) {
|
|
60
|
+
function convertUserMessage(message, fileCache) {
|
|
38
61
|
if (message.content.length === 1 && message.content[0].type === "text") {
|
|
39
62
|
return { role: "user", content: message.content[0].text };
|
|
40
63
|
}
|
|
@@ -45,7 +68,7 @@ function convertUserMessage(message) {
|
|
|
45
68
|
} else if (part.type === "file") {
|
|
46
69
|
const filePart = part;
|
|
47
70
|
const mediaType = filePart.mediaType;
|
|
48
|
-
const url =
|
|
71
|
+
const url = fileDataToString(filePart, mediaType, fileCache);
|
|
49
72
|
if (mediaType.startsWith("image/")) {
|
|
50
73
|
parts.push({ type: "image_url", image_url: { url } });
|
|
51
74
|
} else {
|
|
@@ -125,13 +148,28 @@ function toolResultOutputToString(output) {
|
|
|
125
148
|
return JSON.stringify(output);
|
|
126
149
|
}
|
|
127
150
|
}
|
|
128
|
-
function
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
151
|
+
function fileDataToString(filePart, mediaType, fileCache) {
|
|
152
|
+
const d = filePart.data;
|
|
153
|
+
switch (d.type) {
|
|
154
|
+
case "data": {
|
|
155
|
+
if (typeof d.data === "string") {
|
|
156
|
+
if (d.data.startsWith("data:")) return d.data;
|
|
157
|
+
return `data:${mediaType};base64,${d.data}`;
|
|
158
|
+
}
|
|
159
|
+
return `data:${mediaType};base64,${uint8ArrayToBase64(d.data)}`;
|
|
160
|
+
}
|
|
161
|
+
case "url":
|
|
162
|
+
return d.url.toString();
|
|
163
|
+
case "text":
|
|
164
|
+
return `data:${mediaType};base64,${btoa(d.text)}`;
|
|
165
|
+
case "reference": {
|
|
166
|
+
const entry = fileCache?.lookup(d.reference);
|
|
167
|
+
if (entry) return entry.url;
|
|
168
|
+
throw new provider.UnsupportedFunctionalityError({
|
|
169
|
+
functionality: "Unresolved provider file reference \u2014 call provider.files().uploadFile() first to register the file in the session cache"
|
|
170
|
+
});
|
|
171
|
+
}
|
|
133
172
|
}
|
|
134
|
-
return `data:${mediaType};base64,${uint8ArrayToBase64(data)}`;
|
|
135
173
|
}
|
|
136
174
|
function uint8ArrayToBase64(bytes) {
|
|
137
175
|
let binary = "";
|
|
@@ -148,7 +186,11 @@ var anyRouterProviderOptionsSchema = zod.z.object({
|
|
|
148
186
|
/** Session ID forwarded as `session_id` in the request body. */
|
|
149
187
|
sessionId: zod.z.string().optional(),
|
|
150
188
|
/** Extra body fields merged in last (highest precedence). */
|
|
151
|
-
extraBody: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
|
|
189
|
+
extraBody: zod.z.record(zod.z.string(), zod.z.unknown()).optional(),
|
|
190
|
+
/** Cache this response for N seconds, forwarded as the `cf-aig-cache-ttl` header. */
|
|
191
|
+
cacheTtl: zod.z.number().optional(),
|
|
192
|
+
/** Skip response caching for this call, forwarded as `cf-aig-skip-cache: true`. */
|
|
193
|
+
skipCache: zod.z.boolean().optional()
|
|
152
194
|
});
|
|
153
195
|
var anyRouterErrorSchema = zod.z.object({
|
|
154
196
|
error: zod.z.object({
|
|
@@ -182,7 +224,7 @@ var toolCallDeltaSchema = zod.z.object({
|
|
|
182
224
|
var messageSchema = zod.z.object({
|
|
183
225
|
content: zod.z.string().nullish(),
|
|
184
226
|
reasoning_content: zod.z.string().nullish(),
|
|
185
|
-
tool_calls: zod.z.array(toolCallSchema).
|
|
227
|
+
tool_calls: zod.z.array(toolCallSchema).nullish()
|
|
186
228
|
});
|
|
187
229
|
var choiceSchema = zod.z.object({
|
|
188
230
|
message: messageSchema.optional(),
|
|
@@ -192,12 +234,12 @@ var generateResponseSchema = zod.z.object({
|
|
|
192
234
|
id: zod.z.string().optional(),
|
|
193
235
|
model: zod.z.string().optional(),
|
|
194
236
|
choices: zod.z.array(choiceSchema).optional(),
|
|
195
|
-
usage: completionUsageSchema.
|
|
237
|
+
usage: completionUsageSchema.nullish()
|
|
196
238
|
});
|
|
197
239
|
var deltaSchema = zod.z.object({
|
|
198
240
|
content: zod.z.string().nullish(),
|
|
199
241
|
reasoning_content: zod.z.string().nullish(),
|
|
200
|
-
tool_calls: zod.z.array(toolCallDeltaSchema).
|
|
242
|
+
tool_calls: zod.z.array(toolCallDeltaSchema).nullish()
|
|
201
243
|
});
|
|
202
244
|
var streamChoiceSchema = zod.z.object({
|
|
203
245
|
delta: deltaSchema.optional(),
|
|
@@ -207,7 +249,10 @@ var streamChunkSchema = zod.z.object({
|
|
|
207
249
|
id: zod.z.string().optional(),
|
|
208
250
|
model: zod.z.string().optional(),
|
|
209
251
|
choices: zod.z.array(streamChoiceSchema).optional(),
|
|
210
|
-
usage: completionUsageSchema.
|
|
252
|
+
usage: completionUsageSchema.nullish(),
|
|
253
|
+
// A mid-stream failure arrives as a `data: {"error":{...}}` frame on a 200
|
|
254
|
+
// response, in the same envelope the gateway uses for non-2xx bodies.
|
|
255
|
+
error: anyRouterErrorSchema.shape.error
|
|
211
256
|
});
|
|
212
257
|
function mapFinishReason(raw) {
|
|
213
258
|
switch (raw) {
|
|
@@ -221,6 +266,8 @@ function mapFinishReason(raw) {
|
|
|
221
266
|
return { unified: "tool-calls", raw };
|
|
222
267
|
case "content_filter":
|
|
223
268
|
return { unified: "content-filter", raw };
|
|
269
|
+
case "error":
|
|
270
|
+
return { unified: "error", raw };
|
|
224
271
|
default:
|
|
225
272
|
return { unified: "other", raw: raw ?? void 0 };
|
|
226
273
|
}
|
|
@@ -240,6 +287,11 @@ function buildUsage(raw) {
|
|
|
240
287
|
}
|
|
241
288
|
};
|
|
242
289
|
}
|
|
290
|
+
function mapReasoningEffort(reasoning) {
|
|
291
|
+
if (!reasoning || reasoning === "provider-default") return void 0;
|
|
292
|
+
if (reasoning === "xhigh") return "high";
|
|
293
|
+
return reasoning;
|
|
294
|
+
}
|
|
243
295
|
function emitResponseMetadata(chunk, state, fallbackModelId, controller) {
|
|
244
296
|
if (state.metadataEmitted || !(chunk.id || chunk.model)) return;
|
|
245
297
|
state.metadataEmitted = true;
|
|
@@ -292,7 +344,31 @@ function emitToolCallDeltas(toolCalls, acc, controller) {
|
|
|
292
344
|
}
|
|
293
345
|
}
|
|
294
346
|
}
|
|
295
|
-
function
|
|
347
|
+
function emitDelta(delta, state, acc, controller) {
|
|
348
|
+
if (typeof delta.reasoning_content === "string" && delta.reasoning_content.length > 0) {
|
|
349
|
+
emitReasoningDelta(delta.reasoning_content, state, controller);
|
|
350
|
+
}
|
|
351
|
+
if (typeof delta.content === "string" && delta.content.length > 0) {
|
|
352
|
+
emitTextDelta(delta.content, state, controller);
|
|
353
|
+
}
|
|
354
|
+
if (delta.tool_calls) {
|
|
355
|
+
emitToolCallDeltas(delta.tool_calls, acc, controller);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
function errorFramePart(chunk, request) {
|
|
359
|
+
return {
|
|
360
|
+
type: "error",
|
|
361
|
+
error: new provider.APICallError({
|
|
362
|
+
message: chunk.error?.message ?? "AnyRouter API error",
|
|
363
|
+
url: request.url,
|
|
364
|
+
requestBodyValues: request.body,
|
|
365
|
+
responseHeaders: request.responseHeaders,
|
|
366
|
+
responseBody: JSON.stringify(chunk),
|
|
367
|
+
data: chunk
|
|
368
|
+
})
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
function emitFinish(choice, usage, state, acc, controller) {
|
|
296
372
|
closeReasoning(state, controller);
|
|
297
373
|
if (state.activeTextId !== null) {
|
|
298
374
|
controller.enqueue({ type: "text-end", id: state.activeTextId });
|
|
@@ -311,8 +387,8 @@ function emitFinish(choice, chunk, state, acc, controller) {
|
|
|
311
387
|
}
|
|
312
388
|
controller.enqueue({
|
|
313
389
|
type: "finish",
|
|
314
|
-
usage: buildUsage(
|
|
315
|
-
finishReason: mapFinishReason(choice
|
|
390
|
+
usage: buildUsage(usage),
|
|
391
|
+
finishReason: mapFinishReason(choice?.finish_reason)
|
|
316
392
|
});
|
|
317
393
|
}
|
|
318
394
|
function mapToolChoice(toolChoice) {
|
|
@@ -360,14 +436,15 @@ function buildTools(tools) {
|
|
|
360
436
|
}));
|
|
361
437
|
return fns.length > 0 ? fns : void 0;
|
|
362
438
|
}
|
|
363
|
-
function buildRequestBody(modelId, options, settings, stream, perCallOptions) {
|
|
364
|
-
const messages = convertToAnyRouterChatMessages(options.prompt);
|
|
439
|
+
function buildRequestBody(modelId, options, settings, stream, perCallOptions, fileCache) {
|
|
440
|
+
const messages = convertToAnyRouterChatMessages(options.prompt, fileCache);
|
|
365
441
|
const responseFormat = buildResponseFormat(options.responseFormat);
|
|
366
442
|
const tools = buildTools(options.tools);
|
|
367
443
|
const toolChoice = mapToolChoice(options.toolChoice);
|
|
368
444
|
const provider = perCallOptions.provider ?? settings.provider;
|
|
369
445
|
const sessionId = perCallOptions.sessionId ?? settings.sessionId;
|
|
370
446
|
const extraBody = { ...settings.extraBody, ...perCallOptions.extraBody };
|
|
447
|
+
const reasoningEffort = mapReasoningEffort(options.reasoning);
|
|
371
448
|
const body = {
|
|
372
449
|
model: modelId,
|
|
373
450
|
messages,
|
|
@@ -385,6 +462,7 @@ function buildRequestBody(modelId, options, settings, stream, perCallOptions) {
|
|
|
385
462
|
tool_choice: toolChoice,
|
|
386
463
|
provider: provider ?? void 0,
|
|
387
464
|
session_id: sessionId ?? void 0,
|
|
465
|
+
reasoning_effort: reasoningEffort,
|
|
388
466
|
...extraBody
|
|
389
467
|
};
|
|
390
468
|
for (const key of Object.keys(body)) {
|
|
@@ -392,8 +470,16 @@ function buildRequestBody(modelId, options, settings, stream, perCallOptions) {
|
|
|
392
470
|
}
|
|
393
471
|
return body;
|
|
394
472
|
}
|
|
473
|
+
function buildCacheHeaders(settings, perCallOptions) {
|
|
474
|
+
const cacheTtl = perCallOptions.cacheTtl ?? settings.cacheTtl;
|
|
475
|
+
const skipCache = perCallOptions.skipCache ?? settings.skipCache;
|
|
476
|
+
const headers = {};
|
|
477
|
+
if (cacheTtl !== void 0) headers["cf-aig-cache-ttl"] = String(cacheTtl);
|
|
478
|
+
if (skipCache === true) headers["cf-aig-skip-cache"] = "true";
|
|
479
|
+
return headers;
|
|
480
|
+
}
|
|
395
481
|
var AnyRouterChatLanguageModel = class {
|
|
396
|
-
specificationVersion = "
|
|
482
|
+
specificationVersion = "v4";
|
|
397
483
|
provider;
|
|
398
484
|
modelId;
|
|
399
485
|
supportedUrls = {};
|
|
@@ -403,7 +489,6 @@ var AnyRouterChatLanguageModel = class {
|
|
|
403
489
|
this.provider = config.provider;
|
|
404
490
|
this.config = config;
|
|
405
491
|
}
|
|
406
|
-
// ─── doGenerate ─────────────────────────────────────────────────────────
|
|
407
492
|
async doGenerate(options) {
|
|
408
493
|
const perCallOptions = await providerUtils.parseProviderOptions({
|
|
409
494
|
provider: "anyrouter",
|
|
@@ -415,11 +500,16 @@ var AnyRouterChatLanguageModel = class {
|
|
|
415
500
|
options,
|
|
416
501
|
this.config.settings,
|
|
417
502
|
false,
|
|
418
|
-
perCallOptions
|
|
503
|
+
perCallOptions,
|
|
504
|
+
this.config.fileCache
|
|
419
505
|
);
|
|
420
506
|
const { value: response, responseHeaders } = await providerUtils.postJsonToApi({
|
|
421
507
|
url: `${this.config.baseURL}/chat/completions`,
|
|
422
|
-
headers: providerUtils.combineHeaders(
|
|
508
|
+
headers: providerUtils.combineHeaders(
|
|
509
|
+
this.config.headers(),
|
|
510
|
+
buildCacheHeaders(this.config.settings, perCallOptions),
|
|
511
|
+
options.headers
|
|
512
|
+
),
|
|
423
513
|
body,
|
|
424
514
|
failedResponseHandler: providerUtils.createJsonErrorResponseHandler({
|
|
425
515
|
errorSchema: anyRouterErrorSchema,
|
|
@@ -464,17 +554,28 @@ var AnyRouterChatLanguageModel = class {
|
|
|
464
554
|
}
|
|
465
555
|
};
|
|
466
556
|
}
|
|
467
|
-
// ─── doStream ───────────────────────────────────────────────────────────
|
|
468
557
|
async doStream(options) {
|
|
469
558
|
const perCallOptions = await providerUtils.parseProviderOptions({
|
|
470
559
|
provider: "anyrouter",
|
|
471
560
|
providerOptions: options.providerOptions,
|
|
472
561
|
schema: anyRouterProviderOptionsSchema
|
|
473
562
|
}) ?? {};
|
|
474
|
-
const body = buildRequestBody(
|
|
563
|
+
const body = buildRequestBody(
|
|
564
|
+
this.modelId,
|
|
565
|
+
options,
|
|
566
|
+
this.config.settings,
|
|
567
|
+
true,
|
|
568
|
+
perCallOptions,
|
|
569
|
+
this.config.fileCache
|
|
570
|
+
);
|
|
571
|
+
const url = `${this.config.baseURL}/chat/completions`;
|
|
475
572
|
const { value: stream, responseHeaders } = await providerUtils.postJsonToApi({
|
|
476
|
-
url
|
|
477
|
-
headers: providerUtils.combineHeaders(
|
|
573
|
+
url,
|
|
574
|
+
headers: providerUtils.combineHeaders(
|
|
575
|
+
this.config.headers(),
|
|
576
|
+
buildCacheHeaders(this.config.settings, perCallOptions),
|
|
577
|
+
options.headers
|
|
578
|
+
),
|
|
478
579
|
body,
|
|
479
580
|
failedResponseHandler: providerUtils.createJsonErrorResponseHandler({
|
|
480
581
|
errorSchema: anyRouterErrorSchema,
|
|
@@ -492,6 +593,8 @@ var AnyRouterChatLanguageModel = class {
|
|
|
492
593
|
metadataEmitted: false
|
|
493
594
|
};
|
|
494
595
|
const selfModelId = this.modelId;
|
|
596
|
+
let streamUsage;
|
|
597
|
+
let finishChoice;
|
|
495
598
|
const transformStream = new TransformStream({
|
|
496
599
|
start(controller) {
|
|
497
600
|
controller.enqueue({ type: "stream-start", warnings: [] });
|
|
@@ -502,29 +605,21 @@ var AnyRouterChatLanguageModel = class {
|
|
|
502
605
|
return;
|
|
503
606
|
}
|
|
504
607
|
const chunk = chunkResult.value;
|
|
608
|
+
if (chunk.error) {
|
|
609
|
+
controller.enqueue(errorFramePart(chunk, { url, body, responseHeaders }));
|
|
610
|
+
finishChoice = { finish_reason: "error" };
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
505
613
|
const choice = chunk.choices?.[0];
|
|
614
|
+
if (chunk.usage) streamUsage = chunk.usage;
|
|
506
615
|
emitResponseMetadata(chunk, state, selfModelId, controller);
|
|
507
|
-
|
|
508
|
-
if (delta) {
|
|
509
|
-
if (typeof delta.reasoning_content === "string" && delta.reasoning_content.length > 0) {
|
|
510
|
-
emitReasoningDelta(delta.reasoning_content, state, controller);
|
|
511
|
-
}
|
|
512
|
-
if (typeof delta.content === "string" && delta.content.length > 0) {
|
|
513
|
-
emitTextDelta(delta.content, state, controller);
|
|
514
|
-
}
|
|
515
|
-
if (delta.tool_calls) {
|
|
516
|
-
emitToolCallDeltas(delta.tool_calls, toolCallAccumulator, controller);
|
|
517
|
-
}
|
|
518
|
-
}
|
|
616
|
+
if (choice?.delta) emitDelta(choice.delta, state, toolCallAccumulator, controller);
|
|
519
617
|
if (choice?.finish_reason) {
|
|
520
|
-
|
|
618
|
+
finishChoice = choice;
|
|
521
619
|
}
|
|
522
620
|
},
|
|
523
621
|
flush(controller) {
|
|
524
|
-
|
|
525
|
-
if (state.activeTextId !== null) {
|
|
526
|
-
controller.enqueue({ type: "text-end", id: state.activeTextId });
|
|
527
|
-
}
|
|
622
|
+
emitFinish(finishChoice, streamUsage, state, toolCallAccumulator, controller);
|
|
528
623
|
}
|
|
529
624
|
});
|
|
530
625
|
return {
|
|
@@ -552,7 +647,7 @@ var embeddingResponseSchema = zod.z.object({
|
|
|
552
647
|
}).optional()
|
|
553
648
|
});
|
|
554
649
|
var AnyRouterEmbeddingModel = class {
|
|
555
|
-
specificationVersion = "
|
|
650
|
+
specificationVersion = "v4";
|
|
556
651
|
provider;
|
|
557
652
|
modelId;
|
|
558
653
|
/** No hard per-call limit imposed by the provider layer. */
|
|
@@ -590,100 +685,109 @@ var AnyRouterEmbeddingModel = class {
|
|
|
590
685
|
};
|
|
591
686
|
}
|
|
592
687
|
};
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
});
|
|
599
|
-
var imageGenerationResponseSchema = zod.z.object({
|
|
600
|
-
created: zod.z.number().optional(),
|
|
601
|
-
data: zod.z.array(
|
|
602
|
-
zod.z.object({
|
|
603
|
-
url: zod.z.string().optional(),
|
|
604
|
-
b64_json: zod.z.string().optional(),
|
|
605
|
-
revised_prompt: zod.z.string().optional()
|
|
606
|
-
})
|
|
607
|
-
)
|
|
608
|
-
});
|
|
609
|
-
var AnyRouterImageModel = class {
|
|
610
|
-
specificationVersion = "v3";
|
|
611
|
-
provider;
|
|
612
|
-
modelId;
|
|
688
|
+
|
|
689
|
+
// src/file-reference-cache.ts
|
|
690
|
+
var PROVIDER_KEY = "anyrouter";
|
|
691
|
+
var FileReferenceCache = class {
|
|
692
|
+
store = /* @__PURE__ */ new Map();
|
|
613
693
|
/**
|
|
614
|
-
*
|
|
615
|
-
*
|
|
694
|
+
* Look up a cached entry via a `SharedV4ProviderReference`.
|
|
695
|
+
* Returns `undefined` when the reference was not created by this cache.
|
|
616
696
|
*/
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
this.modelId = modelId;
|
|
621
|
-
this.provider = config.provider;
|
|
622
|
-
this.config = config;
|
|
697
|
+
lookup(reference) {
|
|
698
|
+
const hash = reference[PROVIDER_KEY];
|
|
699
|
+
return hash ? this.store.get(hash) : void 0;
|
|
623
700
|
}
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
})
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
701
|
+
/** Add an entry and return the corresponding provider reference. */
|
|
702
|
+
set(hash, entry) {
|
|
703
|
+
this.store.set(hash, entry);
|
|
704
|
+
return { [PROVIDER_KEY]: hash };
|
|
705
|
+
}
|
|
706
|
+
/** Returns true when the content hash is already cached. */
|
|
707
|
+
has(hash) {
|
|
708
|
+
return this.store.has(hash);
|
|
709
|
+
}
|
|
710
|
+
/**
|
|
711
|
+
* Build a `SharedV4ProviderReference` for the given hash without writing
|
|
712
|
+
* to the cache. Use this to construct the return value of `uploadFile`
|
|
713
|
+
* after confirming the hash is already stored.
|
|
714
|
+
*/
|
|
715
|
+
referenceFor(hash) {
|
|
716
|
+
return { [PROVIDER_KEY]: hash };
|
|
717
|
+
}
|
|
718
|
+
};
|
|
719
|
+
async function hashFileData(data) {
|
|
720
|
+
let bytes;
|
|
721
|
+
if (data.type === "text") {
|
|
722
|
+
bytes = new TextEncoder().encode(data.text);
|
|
723
|
+
} else if (typeof data.data === "string") {
|
|
724
|
+
bytes = new TextEncoder().encode(data.data);
|
|
725
|
+
} else {
|
|
726
|
+
bytes = data.data;
|
|
727
|
+
}
|
|
728
|
+
const hashBuffer = await crypto.subtle.digest("SHA-256", Uint8Array.from(bytes));
|
|
729
|
+
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
730
|
+
return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
731
|
+
}
|
|
732
|
+
function fileDataVariantToUrl(data, mediaType) {
|
|
733
|
+
if (data.type === "text") {
|
|
734
|
+
return `data:${mediaType};base64,${btoa(data.text)}`;
|
|
735
|
+
}
|
|
736
|
+
if (typeof data.data === "string") {
|
|
737
|
+
if (data.data.startsWith("data:")) return data.data;
|
|
738
|
+
return `data:${mediaType};base64,${data.data}`;
|
|
739
|
+
}
|
|
740
|
+
let binary = "";
|
|
741
|
+
for (let i = 0; i < data.data.length; i++) {
|
|
742
|
+
binary += String.fromCharCode(data.data[i]);
|
|
743
|
+
}
|
|
744
|
+
return `data:${mediaType};base64,${btoa(binary)}`;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
// src/anyrouter-files.ts
|
|
748
|
+
var AnyRouterFiles = class {
|
|
749
|
+
specificationVersion = "v4";
|
|
750
|
+
provider;
|
|
751
|
+
cache;
|
|
752
|
+
constructor(provider, cache) {
|
|
753
|
+
this.provider = provider;
|
|
754
|
+
this.cache = cache;
|
|
755
|
+
}
|
|
756
|
+
/**
|
|
757
|
+
* Register a file in the in-session cache and return a provider reference.
|
|
758
|
+
*
|
|
759
|
+
* Calling this multiple times with the same file content is idempotent —
|
|
760
|
+
* the hash check prevents redundant re-encoding.
|
|
761
|
+
*/
|
|
762
|
+
async uploadFile(options) {
|
|
763
|
+
const hash = await hashFileData(options.data);
|
|
764
|
+
if (!this.cache.has(hash)) {
|
|
765
|
+
const url = fileDataVariantToUrl(options.data, options.mediaType);
|
|
766
|
+
this.cache.set(hash, { url, mediaType: options.mediaType });
|
|
665
767
|
}
|
|
666
768
|
return {
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
modelId: this.modelId,
|
|
672
|
-
headers: responseHeaders
|
|
673
|
-
}
|
|
769
|
+
providerReference: this.cache.referenceFor(hash),
|
|
770
|
+
mediaType: options.mediaType,
|
|
771
|
+
filename: options.filename,
|
|
772
|
+
warnings: []
|
|
674
773
|
};
|
|
675
774
|
}
|
|
676
775
|
};
|
|
677
776
|
var PROVIDER_ID = "anyrouter";
|
|
678
777
|
function createAnyRouter(options = {}) {
|
|
679
778
|
const baseURL = providerUtils.withoutTrailingSlash(options.baseURL ?? sdk.DEFAULT_BASE_URL) ?? sdk.DEFAULT_BASE_URL;
|
|
779
|
+
const sharedFileCache = new FileReferenceCache();
|
|
680
780
|
function getHeaders() {
|
|
781
|
+
const attribution = options.app ? buildAppAttributionHeaders(options.app) : {};
|
|
782
|
+
if (options.keyless) {
|
|
783
|
+
return providerUtils.combineHeaders(attribution, options.headers);
|
|
784
|
+
}
|
|
681
785
|
const apiKey = providerUtils.loadApiKey({
|
|
682
786
|
apiKey: options.apiKey,
|
|
683
787
|
environmentVariableName: "ANYROUTER_API_KEY",
|
|
684
788
|
description: "AnyRouter"
|
|
685
789
|
});
|
|
686
|
-
return providerUtils.combineHeaders({ Authorization: `Bearer ${apiKey}
|
|
790
|
+
return providerUtils.combineHeaders({ Authorization: `Bearer ${apiKey}`, ...attribution }, options.headers);
|
|
687
791
|
}
|
|
688
792
|
function chatModel(modelId) {
|
|
689
793
|
return new AnyRouterChatLanguageModel(modelId, {
|
|
@@ -691,7 +795,8 @@ function createAnyRouter(options = {}) {
|
|
|
691
795
|
baseURL,
|
|
692
796
|
headers: getHeaders,
|
|
693
797
|
fetch: options.fetch,
|
|
694
|
-
settings: options
|
|
798
|
+
settings: options,
|
|
799
|
+
fileCache: sharedFileCache
|
|
695
800
|
});
|
|
696
801
|
}
|
|
697
802
|
function embeddingModel(modelId) {
|
|
@@ -702,37 +807,30 @@ function createAnyRouter(options = {}) {
|
|
|
702
807
|
fetch: options.fetch
|
|
703
808
|
});
|
|
704
809
|
}
|
|
705
|
-
function imageModel(modelId) {
|
|
706
|
-
return new AnyRouterImageModel(modelId, {
|
|
707
|
-
provider: PROVIDER_ID,
|
|
708
|
-
baseURL,
|
|
709
|
-
headers: getHeaders,
|
|
710
|
-
fetch: options.fetch
|
|
711
|
-
});
|
|
712
|
-
}
|
|
713
810
|
const provider$1 = (modelId) => chatModel(modelId);
|
|
714
811
|
const providerObj = Object.assign(provider$1, {
|
|
715
|
-
//
|
|
716
|
-
specificationVersion: "
|
|
717
|
-
//
|
|
812
|
+
// ProviderV4 required fields
|
|
813
|
+
specificationVersion: "v4",
|
|
814
|
+
// ProviderV4 required methods
|
|
718
815
|
languageModel(modelId) {
|
|
719
816
|
return chatModel(modelId);
|
|
720
817
|
},
|
|
721
818
|
embeddingModel(modelId) {
|
|
722
819
|
return embeddingModel(modelId);
|
|
723
820
|
},
|
|
724
|
-
imageModel(modelId) {
|
|
725
|
-
return imageModel(modelId);
|
|
726
|
-
},
|
|
727
|
-
// ProviderV3 optional methods (deprecated textEmbeddingModel alias)
|
|
728
|
-
textEmbeddingModel(modelId) {
|
|
729
|
-
return embeddingModel(modelId);
|
|
730
|
-
},
|
|
731
821
|
// AnyRouter convenience alias
|
|
732
822
|
chat(modelId) {
|
|
733
823
|
return chatModel(modelId);
|
|
734
824
|
},
|
|
735
|
-
//
|
|
825
|
+
// ProviderV4 file-management interface
|
|
826
|
+
files() {
|
|
827
|
+
return new AnyRouterFiles(PROVIDER_ID, sharedFileCache);
|
|
828
|
+
},
|
|
829
|
+
// Unsupported optional model types — throw NoSuchModelError per ProviderV4 contract.
|
|
830
|
+
// Image/video generation is not an AnyRouter product (text + embeddings only).
|
|
831
|
+
imageModel(modelId) {
|
|
832
|
+
throw new provider.NoSuchModelError({ modelId, modelType: "imageModel" });
|
|
833
|
+
},
|
|
736
834
|
transcriptionModel(modelId) {
|
|
737
835
|
throw new provider.NoSuchModelError({ modelId, modelType: "transcriptionModel" });
|
|
738
836
|
},
|
|
@@ -758,6 +856,9 @@ var anyrouter = createAnyRouter();
|
|
|
758
856
|
|
|
759
857
|
exports.AnyRouterChatLanguageModel = AnyRouterChatLanguageModel;
|
|
760
858
|
exports.AnyRouterEmbeddingModel = AnyRouterEmbeddingModel;
|
|
761
|
-
exports.
|
|
859
|
+
exports.AnyRouterFiles = AnyRouterFiles;
|
|
860
|
+
exports.FileReferenceCache = FileReferenceCache;
|
|
861
|
+
exports.PROVIDER_ATTRIBUTION = PROVIDER_ATTRIBUTION;
|
|
762
862
|
exports.anyrouter = anyrouter;
|
|
863
|
+
exports.buildAppAttributionHeaders = buildAppAttributionHeaders;
|
|
763
864
|
exports.createAnyRouter = createAnyRouter;
|