@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.mjs
CHANGED
|
@@ -1,10 +1,33 @@
|
|
|
1
|
+
import { NoSuchModelError, APICallError, UnsupportedFunctionalityError } from '@ai-sdk/provider';
|
|
1
2
|
import { withoutTrailingSlash, parseProviderOptions, postJsonToApi, createJsonResponseHandler, createJsonErrorResponseHandler, combineHeaders, createEventSourceResponseHandler, loadApiKey, generateId } from '@ai-sdk/provider-utils';
|
|
2
3
|
import { z } from 'zod';
|
|
3
|
-
import { NoSuchModelError, UnsupportedFunctionalityError } from '@ai-sdk/provider';
|
|
4
4
|
import { DEFAULT_BASE_URL, AnyRouter } from '@anyr/sdk';
|
|
5
5
|
|
|
6
|
-
// src/
|
|
7
|
-
|
|
6
|
+
// src/app-attribution.ts
|
|
7
|
+
var APP_SOURCE = "anyr-ai-sdk-provider";
|
|
8
|
+
var APP_TITLE = "AnyRouter AI SDK Provider";
|
|
9
|
+
var APP_VERSION = "0.1.7";
|
|
10
|
+
var PROVIDER_ATTRIBUTION = {
|
|
11
|
+
source: APP_SOURCE,
|
|
12
|
+
title: APP_TITLE,
|
|
13
|
+
version: APP_VERSION
|
|
14
|
+
};
|
|
15
|
+
function buildAppAttributionHeaders(app) {
|
|
16
|
+
if (!app) return {};
|
|
17
|
+
const headers = {};
|
|
18
|
+
const put = (name, value) => {
|
|
19
|
+
const trimmed = value?.trim();
|
|
20
|
+
if (trimmed) headers[name] = trimmed;
|
|
21
|
+
};
|
|
22
|
+
put("X-AnyRouter-Source", app.source);
|
|
23
|
+
put("X-AnyRouter-Title", app.title);
|
|
24
|
+
put("X-AnyRouter-Version", app.version);
|
|
25
|
+
put("HTTP-Referer", app.url);
|
|
26
|
+
const categories = app.categories?.map((c) => c.trim()).filter(Boolean);
|
|
27
|
+
if (categories?.length) headers["X-AnyRouter-Categories"] = categories.join(",");
|
|
28
|
+
return headers;
|
|
29
|
+
}
|
|
30
|
+
function convertToAnyRouterChatMessages(prompt, fileCache) {
|
|
8
31
|
const messages = [];
|
|
9
32
|
for (const message of prompt) {
|
|
10
33
|
switch (message.role) {
|
|
@@ -12,7 +35,7 @@ function convertToAnyRouterChatMessages(prompt) {
|
|
|
12
35
|
messages.push({ role: "system", content: message.content });
|
|
13
36
|
break;
|
|
14
37
|
case "user":
|
|
15
|
-
messages.push(convertUserMessage(message));
|
|
38
|
+
messages.push(convertUserMessage(message, fileCache));
|
|
16
39
|
break;
|
|
17
40
|
case "assistant":
|
|
18
41
|
messages.push(convertAssistantMessage(message));
|
|
@@ -32,7 +55,7 @@ function convertToAnyRouterChatMessages(prompt) {
|
|
|
32
55
|
}
|
|
33
56
|
return messages;
|
|
34
57
|
}
|
|
35
|
-
function convertUserMessage(message) {
|
|
58
|
+
function convertUserMessage(message, fileCache) {
|
|
36
59
|
if (message.content.length === 1 && message.content[0].type === "text") {
|
|
37
60
|
return { role: "user", content: message.content[0].text };
|
|
38
61
|
}
|
|
@@ -43,7 +66,7 @@ function convertUserMessage(message) {
|
|
|
43
66
|
} else if (part.type === "file") {
|
|
44
67
|
const filePart = part;
|
|
45
68
|
const mediaType = filePart.mediaType;
|
|
46
|
-
const url =
|
|
69
|
+
const url = fileDataToString(filePart, mediaType, fileCache);
|
|
47
70
|
if (mediaType.startsWith("image/")) {
|
|
48
71
|
parts.push({ type: "image_url", image_url: { url } });
|
|
49
72
|
} else {
|
|
@@ -123,13 +146,28 @@ function toolResultOutputToString(output) {
|
|
|
123
146
|
return JSON.stringify(output);
|
|
124
147
|
}
|
|
125
148
|
}
|
|
126
|
-
function
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
149
|
+
function fileDataToString(filePart, mediaType, fileCache) {
|
|
150
|
+
const d = filePart.data;
|
|
151
|
+
switch (d.type) {
|
|
152
|
+
case "data": {
|
|
153
|
+
if (typeof d.data === "string") {
|
|
154
|
+
if (d.data.startsWith("data:")) return d.data;
|
|
155
|
+
return `data:${mediaType};base64,${d.data}`;
|
|
156
|
+
}
|
|
157
|
+
return `data:${mediaType};base64,${uint8ArrayToBase64(d.data)}`;
|
|
158
|
+
}
|
|
159
|
+
case "url":
|
|
160
|
+
return d.url.toString();
|
|
161
|
+
case "text":
|
|
162
|
+
return `data:${mediaType};base64,${btoa(d.text)}`;
|
|
163
|
+
case "reference": {
|
|
164
|
+
const entry = fileCache?.lookup(d.reference);
|
|
165
|
+
if (entry) return entry.url;
|
|
166
|
+
throw new UnsupportedFunctionalityError({
|
|
167
|
+
functionality: "Unresolved provider file reference \u2014 call provider.files().uploadFile() first to register the file in the session cache"
|
|
168
|
+
});
|
|
169
|
+
}
|
|
131
170
|
}
|
|
132
|
-
return `data:${mediaType};base64,${uint8ArrayToBase64(data)}`;
|
|
133
171
|
}
|
|
134
172
|
function uint8ArrayToBase64(bytes) {
|
|
135
173
|
let binary = "";
|
|
@@ -146,7 +184,11 @@ var anyRouterProviderOptionsSchema = z.object({
|
|
|
146
184
|
/** Session ID forwarded as `session_id` in the request body. */
|
|
147
185
|
sessionId: z.string().optional(),
|
|
148
186
|
/** Extra body fields merged in last (highest precedence). */
|
|
149
|
-
extraBody: z.record(z.string(), z.unknown()).optional()
|
|
187
|
+
extraBody: z.record(z.string(), z.unknown()).optional(),
|
|
188
|
+
/** Cache this response for N seconds, forwarded as the `cf-aig-cache-ttl` header. */
|
|
189
|
+
cacheTtl: z.number().optional(),
|
|
190
|
+
/** Skip response caching for this call, forwarded as `cf-aig-skip-cache: true`. */
|
|
191
|
+
skipCache: z.boolean().optional()
|
|
150
192
|
});
|
|
151
193
|
var anyRouterErrorSchema = z.object({
|
|
152
194
|
error: z.object({
|
|
@@ -180,7 +222,7 @@ var toolCallDeltaSchema = z.object({
|
|
|
180
222
|
var messageSchema = z.object({
|
|
181
223
|
content: z.string().nullish(),
|
|
182
224
|
reasoning_content: z.string().nullish(),
|
|
183
|
-
tool_calls: z.array(toolCallSchema).
|
|
225
|
+
tool_calls: z.array(toolCallSchema).nullish()
|
|
184
226
|
});
|
|
185
227
|
var choiceSchema = z.object({
|
|
186
228
|
message: messageSchema.optional(),
|
|
@@ -190,12 +232,12 @@ var generateResponseSchema = z.object({
|
|
|
190
232
|
id: z.string().optional(),
|
|
191
233
|
model: z.string().optional(),
|
|
192
234
|
choices: z.array(choiceSchema).optional(),
|
|
193
|
-
usage: completionUsageSchema.
|
|
235
|
+
usage: completionUsageSchema.nullish()
|
|
194
236
|
});
|
|
195
237
|
var deltaSchema = z.object({
|
|
196
238
|
content: z.string().nullish(),
|
|
197
239
|
reasoning_content: z.string().nullish(),
|
|
198
|
-
tool_calls: z.array(toolCallDeltaSchema).
|
|
240
|
+
tool_calls: z.array(toolCallDeltaSchema).nullish()
|
|
199
241
|
});
|
|
200
242
|
var streamChoiceSchema = z.object({
|
|
201
243
|
delta: deltaSchema.optional(),
|
|
@@ -205,7 +247,10 @@ var streamChunkSchema = z.object({
|
|
|
205
247
|
id: z.string().optional(),
|
|
206
248
|
model: z.string().optional(),
|
|
207
249
|
choices: z.array(streamChoiceSchema).optional(),
|
|
208
|
-
usage: completionUsageSchema.
|
|
250
|
+
usage: completionUsageSchema.nullish(),
|
|
251
|
+
// A mid-stream failure arrives as a `data: {"error":{...}}` frame on a 200
|
|
252
|
+
// response, in the same envelope the gateway uses for non-2xx bodies.
|
|
253
|
+
error: anyRouterErrorSchema.shape.error
|
|
209
254
|
});
|
|
210
255
|
function mapFinishReason(raw) {
|
|
211
256
|
switch (raw) {
|
|
@@ -219,6 +264,8 @@ function mapFinishReason(raw) {
|
|
|
219
264
|
return { unified: "tool-calls", raw };
|
|
220
265
|
case "content_filter":
|
|
221
266
|
return { unified: "content-filter", raw };
|
|
267
|
+
case "error":
|
|
268
|
+
return { unified: "error", raw };
|
|
222
269
|
default:
|
|
223
270
|
return { unified: "other", raw: raw ?? void 0 };
|
|
224
271
|
}
|
|
@@ -238,6 +285,11 @@ function buildUsage(raw) {
|
|
|
238
285
|
}
|
|
239
286
|
};
|
|
240
287
|
}
|
|
288
|
+
function mapReasoningEffort(reasoning) {
|
|
289
|
+
if (!reasoning || reasoning === "provider-default") return void 0;
|
|
290
|
+
if (reasoning === "xhigh") return "high";
|
|
291
|
+
return reasoning;
|
|
292
|
+
}
|
|
241
293
|
function emitResponseMetadata(chunk, state, fallbackModelId, controller) {
|
|
242
294
|
if (state.metadataEmitted || !(chunk.id || chunk.model)) return;
|
|
243
295
|
state.metadataEmitted = true;
|
|
@@ -290,7 +342,31 @@ function emitToolCallDeltas(toolCalls, acc, controller) {
|
|
|
290
342
|
}
|
|
291
343
|
}
|
|
292
344
|
}
|
|
293
|
-
function
|
|
345
|
+
function emitDelta(delta, state, acc, controller) {
|
|
346
|
+
if (typeof delta.reasoning_content === "string" && delta.reasoning_content.length > 0) {
|
|
347
|
+
emitReasoningDelta(delta.reasoning_content, state, controller);
|
|
348
|
+
}
|
|
349
|
+
if (typeof delta.content === "string" && delta.content.length > 0) {
|
|
350
|
+
emitTextDelta(delta.content, state, controller);
|
|
351
|
+
}
|
|
352
|
+
if (delta.tool_calls) {
|
|
353
|
+
emitToolCallDeltas(delta.tool_calls, acc, controller);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
function errorFramePart(chunk, request) {
|
|
357
|
+
return {
|
|
358
|
+
type: "error",
|
|
359
|
+
error: new APICallError({
|
|
360
|
+
message: chunk.error?.message ?? "AnyRouter API error",
|
|
361
|
+
url: request.url,
|
|
362
|
+
requestBodyValues: request.body,
|
|
363
|
+
responseHeaders: request.responseHeaders,
|
|
364
|
+
responseBody: JSON.stringify(chunk),
|
|
365
|
+
data: chunk
|
|
366
|
+
})
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
function emitFinish(choice, usage, state, acc, controller) {
|
|
294
370
|
closeReasoning(state, controller);
|
|
295
371
|
if (state.activeTextId !== null) {
|
|
296
372
|
controller.enqueue({ type: "text-end", id: state.activeTextId });
|
|
@@ -309,8 +385,8 @@ function emitFinish(choice, chunk, state, acc, controller) {
|
|
|
309
385
|
}
|
|
310
386
|
controller.enqueue({
|
|
311
387
|
type: "finish",
|
|
312
|
-
usage: buildUsage(
|
|
313
|
-
finishReason: mapFinishReason(choice
|
|
388
|
+
usage: buildUsage(usage),
|
|
389
|
+
finishReason: mapFinishReason(choice?.finish_reason)
|
|
314
390
|
});
|
|
315
391
|
}
|
|
316
392
|
function mapToolChoice(toolChoice) {
|
|
@@ -358,14 +434,15 @@ function buildTools(tools) {
|
|
|
358
434
|
}));
|
|
359
435
|
return fns.length > 0 ? fns : void 0;
|
|
360
436
|
}
|
|
361
|
-
function buildRequestBody(modelId, options, settings, stream, perCallOptions) {
|
|
362
|
-
const messages = convertToAnyRouterChatMessages(options.prompt);
|
|
437
|
+
function buildRequestBody(modelId, options, settings, stream, perCallOptions, fileCache) {
|
|
438
|
+
const messages = convertToAnyRouterChatMessages(options.prompt, fileCache);
|
|
363
439
|
const responseFormat = buildResponseFormat(options.responseFormat);
|
|
364
440
|
const tools = buildTools(options.tools);
|
|
365
441
|
const toolChoice = mapToolChoice(options.toolChoice);
|
|
366
442
|
const provider = perCallOptions.provider ?? settings.provider;
|
|
367
443
|
const sessionId = perCallOptions.sessionId ?? settings.sessionId;
|
|
368
444
|
const extraBody = { ...settings.extraBody, ...perCallOptions.extraBody };
|
|
445
|
+
const reasoningEffort = mapReasoningEffort(options.reasoning);
|
|
369
446
|
const body = {
|
|
370
447
|
model: modelId,
|
|
371
448
|
messages,
|
|
@@ -383,6 +460,7 @@ function buildRequestBody(modelId, options, settings, stream, perCallOptions) {
|
|
|
383
460
|
tool_choice: toolChoice,
|
|
384
461
|
provider: provider ?? void 0,
|
|
385
462
|
session_id: sessionId ?? void 0,
|
|
463
|
+
reasoning_effort: reasoningEffort,
|
|
386
464
|
...extraBody
|
|
387
465
|
};
|
|
388
466
|
for (const key of Object.keys(body)) {
|
|
@@ -390,8 +468,16 @@ function buildRequestBody(modelId, options, settings, stream, perCallOptions) {
|
|
|
390
468
|
}
|
|
391
469
|
return body;
|
|
392
470
|
}
|
|
471
|
+
function buildCacheHeaders(settings, perCallOptions) {
|
|
472
|
+
const cacheTtl = perCallOptions.cacheTtl ?? settings.cacheTtl;
|
|
473
|
+
const skipCache = perCallOptions.skipCache ?? settings.skipCache;
|
|
474
|
+
const headers = {};
|
|
475
|
+
if (cacheTtl !== void 0) headers["cf-aig-cache-ttl"] = String(cacheTtl);
|
|
476
|
+
if (skipCache === true) headers["cf-aig-skip-cache"] = "true";
|
|
477
|
+
return headers;
|
|
478
|
+
}
|
|
393
479
|
var AnyRouterChatLanguageModel = class {
|
|
394
|
-
specificationVersion = "
|
|
480
|
+
specificationVersion = "v4";
|
|
395
481
|
provider;
|
|
396
482
|
modelId;
|
|
397
483
|
supportedUrls = {};
|
|
@@ -401,7 +487,6 @@ var AnyRouterChatLanguageModel = class {
|
|
|
401
487
|
this.provider = config.provider;
|
|
402
488
|
this.config = config;
|
|
403
489
|
}
|
|
404
|
-
// ─── doGenerate ─────────────────────────────────────────────────────────
|
|
405
490
|
async doGenerate(options) {
|
|
406
491
|
const perCallOptions = await parseProviderOptions({
|
|
407
492
|
provider: "anyrouter",
|
|
@@ -413,11 +498,16 @@ var AnyRouterChatLanguageModel = class {
|
|
|
413
498
|
options,
|
|
414
499
|
this.config.settings,
|
|
415
500
|
false,
|
|
416
|
-
perCallOptions
|
|
501
|
+
perCallOptions,
|
|
502
|
+
this.config.fileCache
|
|
417
503
|
);
|
|
418
504
|
const { value: response, responseHeaders } = await postJsonToApi({
|
|
419
505
|
url: `${this.config.baseURL}/chat/completions`,
|
|
420
|
-
headers: combineHeaders(
|
|
506
|
+
headers: combineHeaders(
|
|
507
|
+
this.config.headers(),
|
|
508
|
+
buildCacheHeaders(this.config.settings, perCallOptions),
|
|
509
|
+
options.headers
|
|
510
|
+
),
|
|
421
511
|
body,
|
|
422
512
|
failedResponseHandler: createJsonErrorResponseHandler({
|
|
423
513
|
errorSchema: anyRouterErrorSchema,
|
|
@@ -462,17 +552,28 @@ var AnyRouterChatLanguageModel = class {
|
|
|
462
552
|
}
|
|
463
553
|
};
|
|
464
554
|
}
|
|
465
|
-
// ─── doStream ───────────────────────────────────────────────────────────
|
|
466
555
|
async doStream(options) {
|
|
467
556
|
const perCallOptions = await parseProviderOptions({
|
|
468
557
|
provider: "anyrouter",
|
|
469
558
|
providerOptions: options.providerOptions,
|
|
470
559
|
schema: anyRouterProviderOptionsSchema
|
|
471
560
|
}) ?? {};
|
|
472
|
-
const body = buildRequestBody(
|
|
561
|
+
const body = buildRequestBody(
|
|
562
|
+
this.modelId,
|
|
563
|
+
options,
|
|
564
|
+
this.config.settings,
|
|
565
|
+
true,
|
|
566
|
+
perCallOptions,
|
|
567
|
+
this.config.fileCache
|
|
568
|
+
);
|
|
569
|
+
const url = `${this.config.baseURL}/chat/completions`;
|
|
473
570
|
const { value: stream, responseHeaders } = await postJsonToApi({
|
|
474
|
-
url
|
|
475
|
-
headers: combineHeaders(
|
|
571
|
+
url,
|
|
572
|
+
headers: combineHeaders(
|
|
573
|
+
this.config.headers(),
|
|
574
|
+
buildCacheHeaders(this.config.settings, perCallOptions),
|
|
575
|
+
options.headers
|
|
576
|
+
),
|
|
476
577
|
body,
|
|
477
578
|
failedResponseHandler: createJsonErrorResponseHandler({
|
|
478
579
|
errorSchema: anyRouterErrorSchema,
|
|
@@ -490,6 +591,8 @@ var AnyRouterChatLanguageModel = class {
|
|
|
490
591
|
metadataEmitted: false
|
|
491
592
|
};
|
|
492
593
|
const selfModelId = this.modelId;
|
|
594
|
+
let streamUsage;
|
|
595
|
+
let finishChoice;
|
|
493
596
|
const transformStream = new TransformStream({
|
|
494
597
|
start(controller) {
|
|
495
598
|
controller.enqueue({ type: "stream-start", warnings: [] });
|
|
@@ -500,29 +603,21 @@ var AnyRouterChatLanguageModel = class {
|
|
|
500
603
|
return;
|
|
501
604
|
}
|
|
502
605
|
const chunk = chunkResult.value;
|
|
606
|
+
if (chunk.error) {
|
|
607
|
+
controller.enqueue(errorFramePart(chunk, { url, body, responseHeaders }));
|
|
608
|
+
finishChoice = { finish_reason: "error" };
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
503
611
|
const choice = chunk.choices?.[0];
|
|
612
|
+
if (chunk.usage) streamUsage = chunk.usage;
|
|
504
613
|
emitResponseMetadata(chunk, state, selfModelId, controller);
|
|
505
|
-
|
|
506
|
-
if (delta) {
|
|
507
|
-
if (typeof delta.reasoning_content === "string" && delta.reasoning_content.length > 0) {
|
|
508
|
-
emitReasoningDelta(delta.reasoning_content, state, controller);
|
|
509
|
-
}
|
|
510
|
-
if (typeof delta.content === "string" && delta.content.length > 0) {
|
|
511
|
-
emitTextDelta(delta.content, state, controller);
|
|
512
|
-
}
|
|
513
|
-
if (delta.tool_calls) {
|
|
514
|
-
emitToolCallDeltas(delta.tool_calls, toolCallAccumulator, controller);
|
|
515
|
-
}
|
|
516
|
-
}
|
|
614
|
+
if (choice?.delta) emitDelta(choice.delta, state, toolCallAccumulator, controller);
|
|
517
615
|
if (choice?.finish_reason) {
|
|
518
|
-
|
|
616
|
+
finishChoice = choice;
|
|
519
617
|
}
|
|
520
618
|
},
|
|
521
619
|
flush(controller) {
|
|
522
|
-
|
|
523
|
-
if (state.activeTextId !== null) {
|
|
524
|
-
controller.enqueue({ type: "text-end", id: state.activeTextId });
|
|
525
|
-
}
|
|
620
|
+
emitFinish(finishChoice, streamUsage, state, toolCallAccumulator, controller);
|
|
526
621
|
}
|
|
527
622
|
});
|
|
528
623
|
return {
|
|
@@ -550,7 +645,7 @@ var embeddingResponseSchema = z.object({
|
|
|
550
645
|
}).optional()
|
|
551
646
|
});
|
|
552
647
|
var AnyRouterEmbeddingModel = class {
|
|
553
|
-
specificationVersion = "
|
|
648
|
+
specificationVersion = "v4";
|
|
554
649
|
provider;
|
|
555
650
|
modelId;
|
|
556
651
|
/** No hard per-call limit imposed by the provider layer. */
|
|
@@ -588,100 +683,109 @@ var AnyRouterEmbeddingModel = class {
|
|
|
588
683
|
};
|
|
589
684
|
}
|
|
590
685
|
};
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
});
|
|
597
|
-
var imageGenerationResponseSchema = z.object({
|
|
598
|
-
created: z.number().optional(),
|
|
599
|
-
data: z.array(
|
|
600
|
-
z.object({
|
|
601
|
-
url: z.string().optional(),
|
|
602
|
-
b64_json: z.string().optional(),
|
|
603
|
-
revised_prompt: z.string().optional()
|
|
604
|
-
})
|
|
605
|
-
)
|
|
606
|
-
});
|
|
607
|
-
var AnyRouterImageModel = class {
|
|
608
|
-
specificationVersion = "v3";
|
|
609
|
-
provider;
|
|
610
|
-
modelId;
|
|
686
|
+
|
|
687
|
+
// src/file-reference-cache.ts
|
|
688
|
+
var PROVIDER_KEY = "anyrouter";
|
|
689
|
+
var FileReferenceCache = class {
|
|
690
|
+
store = /* @__PURE__ */ new Map();
|
|
611
691
|
/**
|
|
612
|
-
*
|
|
613
|
-
*
|
|
692
|
+
* Look up a cached entry via a `SharedV4ProviderReference`.
|
|
693
|
+
* Returns `undefined` when the reference was not created by this cache.
|
|
614
694
|
*/
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
this.modelId = modelId;
|
|
619
|
-
this.provider = config.provider;
|
|
620
|
-
this.config = config;
|
|
695
|
+
lookup(reference) {
|
|
696
|
+
const hash = reference[PROVIDER_KEY];
|
|
697
|
+
return hash ? this.store.get(hash) : void 0;
|
|
621
698
|
}
|
|
622
|
-
|
|
623
|
-
|
|
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
|
-
|
|
699
|
+
/** Add an entry and return the corresponding provider reference. */
|
|
700
|
+
set(hash, entry) {
|
|
701
|
+
this.store.set(hash, entry);
|
|
702
|
+
return { [PROVIDER_KEY]: hash };
|
|
703
|
+
}
|
|
704
|
+
/** Returns true when the content hash is already cached. */
|
|
705
|
+
has(hash) {
|
|
706
|
+
return this.store.has(hash);
|
|
707
|
+
}
|
|
708
|
+
/**
|
|
709
|
+
* Build a `SharedV4ProviderReference` for the given hash without writing
|
|
710
|
+
* to the cache. Use this to construct the return value of `uploadFile`
|
|
711
|
+
* after confirming the hash is already stored.
|
|
712
|
+
*/
|
|
713
|
+
referenceFor(hash) {
|
|
714
|
+
return { [PROVIDER_KEY]: hash };
|
|
715
|
+
}
|
|
716
|
+
};
|
|
717
|
+
async function hashFileData(data) {
|
|
718
|
+
let bytes;
|
|
719
|
+
if (data.type === "text") {
|
|
720
|
+
bytes = new TextEncoder().encode(data.text);
|
|
721
|
+
} else if (typeof data.data === "string") {
|
|
722
|
+
bytes = new TextEncoder().encode(data.data);
|
|
723
|
+
} else {
|
|
724
|
+
bytes = data.data;
|
|
725
|
+
}
|
|
726
|
+
const hashBuffer = await crypto.subtle.digest("SHA-256", Uint8Array.from(bytes));
|
|
727
|
+
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
728
|
+
return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
729
|
+
}
|
|
730
|
+
function fileDataVariantToUrl(data, mediaType) {
|
|
731
|
+
if (data.type === "text") {
|
|
732
|
+
return `data:${mediaType};base64,${btoa(data.text)}`;
|
|
733
|
+
}
|
|
734
|
+
if (typeof data.data === "string") {
|
|
735
|
+
if (data.data.startsWith("data:")) return data.data;
|
|
736
|
+
return `data:${mediaType};base64,${data.data}`;
|
|
737
|
+
}
|
|
738
|
+
let binary = "";
|
|
739
|
+
for (let i = 0; i < data.data.length; i++) {
|
|
740
|
+
binary += String.fromCharCode(data.data[i]);
|
|
741
|
+
}
|
|
742
|
+
return `data:${mediaType};base64,${btoa(binary)}`;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
// src/anyrouter-files.ts
|
|
746
|
+
var AnyRouterFiles = class {
|
|
747
|
+
specificationVersion = "v4";
|
|
748
|
+
provider;
|
|
749
|
+
cache;
|
|
750
|
+
constructor(provider, cache) {
|
|
751
|
+
this.provider = provider;
|
|
752
|
+
this.cache = cache;
|
|
753
|
+
}
|
|
754
|
+
/**
|
|
755
|
+
* Register a file in the in-session cache and return a provider reference.
|
|
756
|
+
*
|
|
757
|
+
* Calling this multiple times with the same file content is idempotent —
|
|
758
|
+
* the hash check prevents redundant re-encoding.
|
|
759
|
+
*/
|
|
760
|
+
async uploadFile(options) {
|
|
761
|
+
const hash = await hashFileData(options.data);
|
|
762
|
+
if (!this.cache.has(hash)) {
|
|
763
|
+
const url = fileDataVariantToUrl(options.data, options.mediaType);
|
|
764
|
+
this.cache.set(hash, { url, mediaType: options.mediaType });
|
|
663
765
|
}
|
|
664
766
|
return {
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
modelId: this.modelId,
|
|
670
|
-
headers: responseHeaders
|
|
671
|
-
}
|
|
767
|
+
providerReference: this.cache.referenceFor(hash),
|
|
768
|
+
mediaType: options.mediaType,
|
|
769
|
+
filename: options.filename,
|
|
770
|
+
warnings: []
|
|
672
771
|
};
|
|
673
772
|
}
|
|
674
773
|
};
|
|
675
774
|
var PROVIDER_ID = "anyrouter";
|
|
676
775
|
function createAnyRouter(options = {}) {
|
|
677
776
|
const baseURL = withoutTrailingSlash(options.baseURL ?? DEFAULT_BASE_URL) ?? DEFAULT_BASE_URL;
|
|
777
|
+
const sharedFileCache = new FileReferenceCache();
|
|
678
778
|
function getHeaders() {
|
|
779
|
+
const attribution = options.app ? buildAppAttributionHeaders(options.app) : {};
|
|
780
|
+
if (options.keyless) {
|
|
781
|
+
return combineHeaders(attribution, options.headers);
|
|
782
|
+
}
|
|
679
783
|
const apiKey = loadApiKey({
|
|
680
784
|
apiKey: options.apiKey,
|
|
681
785
|
environmentVariableName: "ANYROUTER_API_KEY",
|
|
682
786
|
description: "AnyRouter"
|
|
683
787
|
});
|
|
684
|
-
return combineHeaders({ Authorization: `Bearer ${apiKey}
|
|
788
|
+
return combineHeaders({ Authorization: `Bearer ${apiKey}`, ...attribution }, options.headers);
|
|
685
789
|
}
|
|
686
790
|
function chatModel(modelId) {
|
|
687
791
|
return new AnyRouterChatLanguageModel(modelId, {
|
|
@@ -689,7 +793,8 @@ function createAnyRouter(options = {}) {
|
|
|
689
793
|
baseURL,
|
|
690
794
|
headers: getHeaders,
|
|
691
795
|
fetch: options.fetch,
|
|
692
|
-
settings: options
|
|
796
|
+
settings: options,
|
|
797
|
+
fileCache: sharedFileCache
|
|
693
798
|
});
|
|
694
799
|
}
|
|
695
800
|
function embeddingModel(modelId) {
|
|
@@ -700,37 +805,30 @@ function createAnyRouter(options = {}) {
|
|
|
700
805
|
fetch: options.fetch
|
|
701
806
|
});
|
|
702
807
|
}
|
|
703
|
-
function imageModel(modelId) {
|
|
704
|
-
return new AnyRouterImageModel(modelId, {
|
|
705
|
-
provider: PROVIDER_ID,
|
|
706
|
-
baseURL,
|
|
707
|
-
headers: getHeaders,
|
|
708
|
-
fetch: options.fetch
|
|
709
|
-
});
|
|
710
|
-
}
|
|
711
808
|
const provider = (modelId) => chatModel(modelId);
|
|
712
809
|
const providerObj = Object.assign(provider, {
|
|
713
|
-
//
|
|
714
|
-
specificationVersion: "
|
|
715
|
-
//
|
|
810
|
+
// ProviderV4 required fields
|
|
811
|
+
specificationVersion: "v4",
|
|
812
|
+
// ProviderV4 required methods
|
|
716
813
|
languageModel(modelId) {
|
|
717
814
|
return chatModel(modelId);
|
|
718
815
|
},
|
|
719
816
|
embeddingModel(modelId) {
|
|
720
817
|
return embeddingModel(modelId);
|
|
721
818
|
},
|
|
722
|
-
imageModel(modelId) {
|
|
723
|
-
return imageModel(modelId);
|
|
724
|
-
},
|
|
725
|
-
// ProviderV3 optional methods (deprecated textEmbeddingModel alias)
|
|
726
|
-
textEmbeddingModel(modelId) {
|
|
727
|
-
return embeddingModel(modelId);
|
|
728
|
-
},
|
|
729
819
|
// AnyRouter convenience alias
|
|
730
820
|
chat(modelId) {
|
|
731
821
|
return chatModel(modelId);
|
|
732
822
|
},
|
|
733
|
-
//
|
|
823
|
+
// ProviderV4 file-management interface
|
|
824
|
+
files() {
|
|
825
|
+
return new AnyRouterFiles(PROVIDER_ID, sharedFileCache);
|
|
826
|
+
},
|
|
827
|
+
// Unsupported optional model types — throw NoSuchModelError per ProviderV4 contract.
|
|
828
|
+
// Image/video generation is not an AnyRouter product (text + embeddings only).
|
|
829
|
+
imageModel(modelId) {
|
|
830
|
+
throw new NoSuchModelError({ modelId, modelType: "imageModel" });
|
|
831
|
+
},
|
|
734
832
|
transcriptionModel(modelId) {
|
|
735
833
|
throw new NoSuchModelError({ modelId, modelType: "transcriptionModel" });
|
|
736
834
|
},
|
|
@@ -754,4 +852,4 @@ function createAnyRouter(options = {}) {
|
|
|
754
852
|
}
|
|
755
853
|
var anyrouter = createAnyRouter();
|
|
756
854
|
|
|
757
|
-
export { AnyRouterChatLanguageModel, AnyRouterEmbeddingModel,
|
|
855
|
+
export { AnyRouterChatLanguageModel, AnyRouterEmbeddingModel, AnyRouterFiles, FileReferenceCache, PROVIDER_ATTRIBUTION, anyrouter, buildAppAttributionHeaders, createAnyRouter };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@anyr/ai-sdk-provider",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "The AnyRouter provider for the Vercel AI SDK — call every AnyRouter model through `createAnyRouter`.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"build": "tsup",
|
|
32
32
|
"dev": "tsup --watch",
|
|
33
33
|
"clean": "rm -rf dist",
|
|
34
|
-
"typecheck": "
|
|
34
|
+
"typecheck": "tsgo --noEmit",
|
|
35
35
|
"test": "vitest run",
|
|
36
36
|
"prepublishOnly": "tsup"
|
|
37
37
|
},
|
|
@@ -49,31 +49,35 @@
|
|
|
49
49
|
"homepage": "https://anyrouter.dev",
|
|
50
50
|
"repository": {
|
|
51
51
|
"type": "git",
|
|
52
|
-
"url": "git+https://github.com/
|
|
53
|
-
"directory": "packages/ai-sdk-provider"
|
|
52
|
+
"url": "git+https://github.com/AnyRouterHQ/anyrouter-ai-sdk-provider.git"
|
|
54
53
|
},
|
|
55
54
|
"bugs": {
|
|
56
|
-
"url": "https://github.com/
|
|
55
|
+
"url": "https://github.com/AnyRouterHQ/anyrouter-ai-sdk-provider/issues"
|
|
57
56
|
},
|
|
58
57
|
"author": "Duyet Le",
|
|
58
|
+
"packageManager": "pnpm@12.16.1",
|
|
59
59
|
"license": "MIT",
|
|
60
60
|
"publishConfig": {
|
|
61
61
|
"access": "public"
|
|
62
62
|
},
|
|
63
63
|
"dependencies": {
|
|
64
|
-
"@anyr/sdk": "^0.1.
|
|
64
|
+
"@anyr/sdk": "^0.1.3"
|
|
65
65
|
},
|
|
66
66
|
"peerDependencies": {
|
|
67
|
-
"@ai-sdk/provider": "^
|
|
68
|
-
"@ai-sdk/provider-utils": "^
|
|
69
|
-
"ai": "^
|
|
67
|
+
"@ai-sdk/provider": "^4.0.0",
|
|
68
|
+
"@ai-sdk/provider-utils": "^5.0.0",
|
|
69
|
+
"ai": "^7.0.0",
|
|
70
70
|
"zod": "^3.25.0 || ^4.0.0"
|
|
71
71
|
},
|
|
72
72
|
"devDependencies": {
|
|
73
|
-
"@ai-sdk/provider": "
|
|
74
|
-
"@ai-sdk/provider-utils": "
|
|
73
|
+
"@ai-sdk/provider": "4.0.2",
|
|
74
|
+
"@ai-sdk/provider-utils": "5.0.5",
|
|
75
75
|
"@types/node": "^24.0.0",
|
|
76
|
-
"
|
|
77
|
-
"
|
|
76
|
+
"@typescript/native-preview": "7.0.0-dev.20260707.2",
|
|
77
|
+
"ai": "7.0.15",
|
|
78
|
+
"tsup": "^8.5.1",
|
|
79
|
+
"typescript": "^6.0.3",
|
|
80
|
+
"vitest": "^5.1.0",
|
|
81
|
+
"zod": "4.6.7"
|
|
78
82
|
}
|
|
79
83
|
}
|