@anyr/ai-sdk-provider 0.1.3 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ var provider = require('@ai-sdk/provider');
6
6
  var sdk = require('@anyr/sdk');
7
7
 
8
8
  // src/anyrouter-chat-language-model.ts
9
- function convertToAnyRouterChatMessages(prompt) {
9
+ function convertToAnyRouterChatMessages(prompt, fileCache) {
10
10
  const messages = [];
11
11
  for (const message of prompt) {
12
12
  switch (message.role) {
@@ -14,7 +14,7 @@ function convertToAnyRouterChatMessages(prompt) {
14
14
  messages.push({ role: "system", content: message.content });
15
15
  break;
16
16
  case "user":
17
- messages.push(convertUserMessage(message));
17
+ messages.push(convertUserMessage(message, fileCache));
18
18
  break;
19
19
  case "assistant":
20
20
  messages.push(convertAssistantMessage(message));
@@ -34,7 +34,7 @@ function convertToAnyRouterChatMessages(prompt) {
34
34
  }
35
35
  return messages;
36
36
  }
37
- function convertUserMessage(message) {
37
+ function convertUserMessage(message, fileCache) {
38
38
  if (message.content.length === 1 && message.content[0].type === "text") {
39
39
  return { role: "user", content: message.content[0].text };
40
40
  }
@@ -45,7 +45,7 @@ function convertUserMessage(message) {
45
45
  } else if (part.type === "file") {
46
46
  const filePart = part;
47
47
  const mediaType = filePart.mediaType;
48
- const url = toDataUrlOrString(filePart.data, mediaType);
48
+ const url = fileDataToString(filePart, mediaType, fileCache);
49
49
  if (mediaType.startsWith("image/")) {
50
50
  parts.push({ type: "image_url", image_url: { url } });
51
51
  } else {
@@ -125,13 +125,28 @@ function toolResultOutputToString(output) {
125
125
  return JSON.stringify(output);
126
126
  }
127
127
  }
128
- function toDataUrlOrString(data, mediaType) {
129
- if (data instanceof URL) return data.toString();
130
- if (typeof data === "string") {
131
- if (data.startsWith("http") || data.startsWith("data:")) return data;
132
- return `data:${mediaType};base64,${data}`;
128
+ function fileDataToString(filePart, mediaType, fileCache) {
129
+ const d = filePart.data;
130
+ switch (d.type) {
131
+ case "data": {
132
+ if (typeof d.data === "string") {
133
+ if (d.data.startsWith("data:")) return d.data;
134
+ return `data:${mediaType};base64,${d.data}`;
135
+ }
136
+ return `data:${mediaType};base64,${uint8ArrayToBase64(d.data)}`;
137
+ }
138
+ case "url":
139
+ return d.url.toString();
140
+ case "text":
141
+ return `data:${mediaType};base64,${btoa(d.text)}`;
142
+ case "reference": {
143
+ const entry = fileCache?.lookup(d.reference);
144
+ if (entry) return entry.url;
145
+ throw new provider.UnsupportedFunctionalityError({
146
+ functionality: "Unresolved provider file reference \u2014 call provider.files().uploadFile() first to register the file in the session cache"
147
+ });
148
+ }
133
149
  }
134
- return `data:${mediaType};base64,${uint8ArrayToBase64(data)}`;
135
150
  }
136
151
  function uint8ArrayToBase64(bytes) {
137
152
  let binary = "";
@@ -148,7 +163,13 @@ var anyRouterProviderOptionsSchema = zod.z.object({
148
163
  /** Session ID forwarded as `session_id` in the request body. */
149
164
  sessionId: zod.z.string().optional(),
150
165
  /** Extra body fields merged in last (highest precedence). */
151
- extraBody: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
166
+ extraBody: zod.z.record(zod.z.string(), zod.z.unknown()).optional(),
167
+ /** Cache this response for N seconds, forwarded as the `cf-aig-cache-ttl` header. */
168
+ cacheTtl: zod.z.number().optional(),
169
+ /** Custom cache key, forwarded as the `cf-aig-cache-key` header. */
170
+ cacheKey: zod.z.string().optional(),
171
+ /** Skip response caching for this call, forwarded as `cf-aig-skip-cache: true`. */
172
+ skipCache: zod.z.boolean().optional()
152
173
  });
153
174
  var anyRouterErrorSchema = zod.z.object({
154
175
  error: zod.z.object({
@@ -240,6 +261,11 @@ function buildUsage(raw) {
240
261
  }
241
262
  };
242
263
  }
264
+ function mapReasoningEffort(reasoning) {
265
+ if (!reasoning || reasoning === "provider-default") return void 0;
266
+ if (reasoning === "xhigh") return "high";
267
+ return reasoning;
268
+ }
243
269
  function emitResponseMetadata(chunk, state, fallbackModelId, controller) {
244
270
  if (state.metadataEmitted || !(chunk.id || chunk.model)) return;
245
271
  state.metadataEmitted = true;
@@ -249,7 +275,21 @@ function emitResponseMetadata(chunk, state, fallbackModelId, controller) {
249
275
  modelId: chunk.model ?? fallbackModelId
250
276
  });
251
277
  }
278
+ function closeReasoning(state, controller) {
279
+ if (state.activeReasoningId !== null) {
280
+ controller.enqueue({ type: "reasoning-end", id: state.activeReasoningId });
281
+ state.activeReasoningId = null;
282
+ }
283
+ }
284
+ function emitReasoningDelta(content, state, controller) {
285
+ if (state.activeReasoningId === null) {
286
+ state.activeReasoningId = providerUtils.generateId();
287
+ controller.enqueue({ type: "reasoning-start", id: state.activeReasoningId });
288
+ }
289
+ controller.enqueue({ type: "reasoning-delta", id: state.activeReasoningId, delta: content });
290
+ }
252
291
  function emitTextDelta(content, state, controller) {
292
+ closeReasoning(state, controller);
253
293
  if (state.activeTextId === null) {
254
294
  state.activeTextId = providerUtils.generateId();
255
295
  controller.enqueue({ type: "text-start", id: state.activeTextId });
@@ -278,7 +318,8 @@ function emitToolCallDeltas(toolCalls, acc, controller) {
278
318
  }
279
319
  }
280
320
  }
281
- function emitFinish(choice, chunk, state, acc, controller) {
321
+ function emitFinish(choice, usage, state, acc, controller) {
322
+ closeReasoning(state, controller);
282
323
  if (state.activeTextId !== null) {
283
324
  controller.enqueue({ type: "text-end", id: state.activeTextId });
284
325
  state.activeTextId = null;
@@ -296,8 +337,8 @@ function emitFinish(choice, chunk, state, acc, controller) {
296
337
  }
297
338
  controller.enqueue({
298
339
  type: "finish",
299
- usage: buildUsage(chunk.usage),
300
- finishReason: mapFinishReason(choice.finish_reason)
340
+ usage: buildUsage(usage),
341
+ finishReason: mapFinishReason(choice?.finish_reason)
301
342
  });
302
343
  }
303
344
  function mapToolChoice(toolChoice) {
@@ -345,14 +386,15 @@ function buildTools(tools) {
345
386
  }));
346
387
  return fns.length > 0 ? fns : void 0;
347
388
  }
348
- function buildRequestBody(modelId, options, settings, stream, perCallOptions) {
349
- const messages = convertToAnyRouterChatMessages(options.prompt);
389
+ function buildRequestBody(modelId, options, settings, stream, perCallOptions, fileCache) {
390
+ const messages = convertToAnyRouterChatMessages(options.prompt, fileCache);
350
391
  const responseFormat = buildResponseFormat(options.responseFormat);
351
392
  const tools = buildTools(options.tools);
352
393
  const toolChoice = mapToolChoice(options.toolChoice);
353
394
  const provider = perCallOptions.provider ?? settings.provider;
354
395
  const sessionId = perCallOptions.sessionId ?? settings.sessionId;
355
396
  const extraBody = { ...settings.extraBody, ...perCallOptions.extraBody };
397
+ const reasoningEffort = mapReasoningEffort(options.reasoning);
356
398
  const body = {
357
399
  model: modelId,
358
400
  messages,
@@ -370,6 +412,7 @@ function buildRequestBody(modelId, options, settings, stream, perCallOptions) {
370
412
  tool_choice: toolChoice,
371
413
  provider: provider ?? void 0,
372
414
  session_id: sessionId ?? void 0,
415
+ reasoning_effort: reasoningEffort,
373
416
  ...extraBody
374
417
  };
375
418
  for (const key of Object.keys(body)) {
@@ -377,8 +420,18 @@ function buildRequestBody(modelId, options, settings, stream, perCallOptions) {
377
420
  }
378
421
  return body;
379
422
  }
423
+ function buildCacheHeaders(settings, perCallOptions) {
424
+ const cacheTtl = perCallOptions.cacheTtl ?? settings.cacheTtl;
425
+ const cacheKey = perCallOptions.cacheKey ?? settings.cacheKey;
426
+ const skipCache = perCallOptions.skipCache ?? settings.skipCache;
427
+ const headers = {};
428
+ if (cacheTtl !== void 0) headers["cf-aig-cache-ttl"] = String(cacheTtl);
429
+ if (cacheKey !== void 0) headers["cf-aig-cache-key"] = cacheKey;
430
+ if (skipCache === true) headers["cf-aig-skip-cache"] = "true";
431
+ return headers;
432
+ }
380
433
  var AnyRouterChatLanguageModel = class {
381
- specificationVersion = "v3";
434
+ specificationVersion = "v4";
382
435
  provider;
383
436
  modelId;
384
437
  supportedUrls = {};
@@ -400,11 +453,16 @@ var AnyRouterChatLanguageModel = class {
400
453
  options,
401
454
  this.config.settings,
402
455
  false,
403
- perCallOptions
456
+ perCallOptions,
457
+ this.config.fileCache
404
458
  );
405
459
  const { value: response, responseHeaders } = await providerUtils.postJsonToApi({
406
460
  url: `${this.config.baseURL}/chat/completions`,
407
- headers: providerUtils.combineHeaders(this.config.headers(), options.headers),
461
+ headers: providerUtils.combineHeaders(
462
+ this.config.headers(),
463
+ buildCacheHeaders(this.config.settings, perCallOptions),
464
+ options.headers
465
+ ),
408
466
  body,
409
467
  failedResponseHandler: providerUtils.createJsonErrorResponseHandler({
410
468
  errorSchema: anyRouterErrorSchema,
@@ -418,6 +476,9 @@ var AnyRouterChatLanguageModel = class {
418
476
  const choice = response.choices?.[0];
419
477
  const message = choice?.message;
420
478
  const content = [];
479
+ if (message?.reasoning_content) {
480
+ content.push({ type: "reasoning", text: message.reasoning_content });
481
+ }
421
482
  if (message?.content) {
422
483
  content.push({ type: "text", text: message.content });
423
484
  }
@@ -453,10 +514,21 @@ var AnyRouterChatLanguageModel = class {
453
514
  providerOptions: options.providerOptions,
454
515
  schema: anyRouterProviderOptionsSchema
455
516
  }) ?? {};
456
- const body = buildRequestBody(this.modelId, options, this.config.settings, true, perCallOptions);
517
+ const body = buildRequestBody(
518
+ this.modelId,
519
+ options,
520
+ this.config.settings,
521
+ true,
522
+ perCallOptions,
523
+ this.config.fileCache
524
+ );
457
525
  const { value: stream, responseHeaders } = await providerUtils.postJsonToApi({
458
526
  url: `${this.config.baseURL}/chat/completions`,
459
- headers: providerUtils.combineHeaders(this.config.headers(), options.headers),
527
+ headers: providerUtils.combineHeaders(
528
+ this.config.headers(),
529
+ buildCacheHeaders(this.config.settings, perCallOptions),
530
+ options.headers
531
+ ),
460
532
  body,
461
533
  failedResponseHandler: providerUtils.createJsonErrorResponseHandler({
462
534
  errorSchema: anyRouterErrorSchema,
@@ -468,8 +540,14 @@ var AnyRouterChatLanguageModel = class {
468
540
  fetch: this.config.fetch
469
541
  });
470
542
  const toolCallAccumulator = {};
471
- const state = { activeTextId: null, metadataEmitted: false };
543
+ const state = {
544
+ activeTextId: null,
545
+ activeReasoningId: null,
546
+ metadataEmitted: false
547
+ };
472
548
  const selfModelId = this.modelId;
549
+ let streamUsage;
550
+ let finishChoice;
473
551
  const transformStream = new TransformStream({
474
552
  start(controller) {
475
553
  controller.enqueue({ type: "stream-start", warnings: [] });
@@ -481,9 +559,13 @@ var AnyRouterChatLanguageModel = class {
481
559
  }
482
560
  const chunk = chunkResult.value;
483
561
  const choice = chunk.choices?.[0];
562
+ if (chunk.usage) streamUsage = chunk.usage;
484
563
  emitResponseMetadata(chunk, state, selfModelId, controller);
485
564
  const delta = choice?.delta;
486
565
  if (delta) {
566
+ if (typeof delta.reasoning_content === "string" && delta.reasoning_content.length > 0) {
567
+ emitReasoningDelta(delta.reasoning_content, state, controller);
568
+ }
487
569
  if (typeof delta.content === "string" && delta.content.length > 0) {
488
570
  emitTextDelta(delta.content, state, controller);
489
571
  }
@@ -492,13 +574,11 @@ var AnyRouterChatLanguageModel = class {
492
574
  }
493
575
  }
494
576
  if (choice?.finish_reason) {
495
- emitFinish(choice, chunk, state, toolCallAccumulator, controller);
577
+ finishChoice = choice;
496
578
  }
497
579
  },
498
580
  flush(controller) {
499
- if (state.activeTextId !== null) {
500
- controller.enqueue({ type: "text-end", id: state.activeTextId });
501
- }
581
+ emitFinish(finishChoice, streamUsage, state, toolCallAccumulator, controller);
502
582
  }
503
583
  });
504
584
  return {
@@ -526,7 +606,7 @@ var embeddingResponseSchema = zod.z.object({
526
606
  }).optional()
527
607
  });
528
608
  var AnyRouterEmbeddingModel = class {
529
- specificationVersion = "v3";
609
+ specificationVersion = "v4";
530
610
  provider;
531
611
  modelId;
532
612
  /** No hard per-call limit imposed by the provider layer. */
@@ -564,6 +644,94 @@ var AnyRouterEmbeddingModel = class {
564
644
  };
565
645
  }
566
646
  };
647
+
648
+ // src/file-reference-cache.ts
649
+ var PROVIDER_KEY = "anyrouter";
650
+ var FileReferenceCache = class {
651
+ store = /* @__PURE__ */ new Map();
652
+ /**
653
+ * Look up a cached entry via a `SharedV4ProviderReference`.
654
+ * Returns `undefined` when the reference was not created by this cache.
655
+ */
656
+ lookup(reference) {
657
+ const hash = reference[PROVIDER_KEY];
658
+ return hash ? this.store.get(hash) : void 0;
659
+ }
660
+ /** Add an entry and return the corresponding provider reference. */
661
+ set(hash, entry) {
662
+ this.store.set(hash, entry);
663
+ return { [PROVIDER_KEY]: hash };
664
+ }
665
+ /** Returns true when the content hash is already cached. */
666
+ has(hash) {
667
+ return this.store.has(hash);
668
+ }
669
+ /**
670
+ * Build a `SharedV4ProviderReference` for the given hash without writing
671
+ * to the cache. Use this to construct the return value of `uploadFile`
672
+ * after confirming the hash is already stored.
673
+ */
674
+ referenceFor(hash) {
675
+ return { [PROVIDER_KEY]: hash };
676
+ }
677
+ };
678
+ async function hashFileData(data) {
679
+ let bytes;
680
+ if (data.type === "text") {
681
+ bytes = new TextEncoder().encode(data.text);
682
+ } else if (typeof data.data === "string") {
683
+ bytes = new TextEncoder().encode(data.data);
684
+ } else {
685
+ bytes = data.data;
686
+ }
687
+ const hashBuffer = await crypto.subtle.digest("SHA-256", Uint8Array.from(bytes));
688
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
689
+ return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
690
+ }
691
+ function fileDataVariantToUrl(data, mediaType) {
692
+ if (data.type === "text") {
693
+ return `data:${mediaType};base64,${btoa(data.text)}`;
694
+ }
695
+ if (typeof data.data === "string") {
696
+ if (data.data.startsWith("data:")) return data.data;
697
+ return `data:${mediaType};base64,${data.data}`;
698
+ }
699
+ let binary = "";
700
+ for (let i = 0; i < data.data.length; i++) {
701
+ binary += String.fromCharCode(data.data[i]);
702
+ }
703
+ return `data:${mediaType};base64,${btoa(binary)}`;
704
+ }
705
+
706
+ // src/anyrouter-files.ts
707
+ var AnyRouterFiles = class {
708
+ specificationVersion = "v4";
709
+ provider;
710
+ cache;
711
+ constructor(provider, cache) {
712
+ this.provider = provider;
713
+ this.cache = cache;
714
+ }
715
+ /**
716
+ * Register a file in the in-session cache and return a provider reference.
717
+ *
718
+ * Calling this multiple times with the same file content is idempotent —
719
+ * the hash check prevents redundant re-encoding.
720
+ */
721
+ async uploadFile(options) {
722
+ const hash = await hashFileData(options.data);
723
+ if (!this.cache.has(hash)) {
724
+ const url = fileDataVariantToUrl(options.data, options.mediaType);
725
+ this.cache.set(hash, { url, mediaType: options.mediaType });
726
+ }
727
+ return {
728
+ providerReference: this.cache.referenceFor(hash),
729
+ mediaType: options.mediaType,
730
+ filename: options.filename,
731
+ warnings: []
732
+ };
733
+ }
734
+ };
567
735
  var anyRouterErrorSchema3 = zod.z.object({
568
736
  error: zod.z.object({
569
737
  code: zod.z.string().optional(),
@@ -581,7 +749,7 @@ var imageGenerationResponseSchema = zod.z.object({
581
749
  )
582
750
  });
583
751
  var AnyRouterImageModel = class {
584
- specificationVersion = "v3";
752
+ specificationVersion = "v4";
585
753
  provider;
586
754
  modelId;
587
755
  /**
@@ -601,6 +769,11 @@ var AnyRouterImageModel = class {
601
769
  functionality: "mask (inpainting) for AnyRouter image generation"
602
770
  });
603
771
  }
772
+ if (options.files && options.files.length > 0) {
773
+ throw new provider.UnsupportedFunctionalityError({
774
+ functionality: "image editing (files) for AnyRouter image generation"
775
+ });
776
+ }
604
777
  const warnings = [];
605
778
  if (options.n > 1) {
606
779
  warnings.push({
@@ -651,13 +824,18 @@ var AnyRouterImageModel = class {
651
824
  var PROVIDER_ID = "anyrouter";
652
825
  function createAnyRouter(options = {}) {
653
826
  const baseURL = providerUtils.withoutTrailingSlash(options.baseURL ?? sdk.DEFAULT_BASE_URL) ?? sdk.DEFAULT_BASE_URL;
827
+ const sharedFileCache = new FileReferenceCache();
654
828
  function getHeaders() {
829
+ const attribution = options.app ? sdk.buildAppAttributionHeaders(options.app) : {};
830
+ if (options.keyless) {
831
+ return providerUtils.combineHeaders(attribution, options.headers);
832
+ }
655
833
  const apiKey = providerUtils.loadApiKey({
656
834
  apiKey: options.apiKey,
657
835
  environmentVariableName: "ANYROUTER_API_KEY",
658
836
  description: "AnyRouter"
659
837
  });
660
- return providerUtils.combineHeaders({ Authorization: `Bearer ${apiKey}` }, options.headers);
838
+ return providerUtils.combineHeaders({ Authorization: `Bearer ${apiKey}`, ...attribution }, options.headers);
661
839
  }
662
840
  function chatModel(modelId) {
663
841
  return new AnyRouterChatLanguageModel(modelId, {
@@ -665,7 +843,8 @@ function createAnyRouter(options = {}) {
665
843
  baseURL,
666
844
  headers: getHeaders,
667
845
  fetch: options.fetch,
668
- settings: options
846
+ settings: options,
847
+ fileCache: sharedFileCache
669
848
  });
670
849
  }
671
850
  function embeddingModel(modelId) {
@@ -686,9 +865,9 @@ function createAnyRouter(options = {}) {
686
865
  }
687
866
  const provider$1 = (modelId) => chatModel(modelId);
688
867
  const providerObj = Object.assign(provider$1, {
689
- // ProviderV3 required fields
690
- specificationVersion: "v3",
691
- // ProviderV3 required methods
868
+ // ProviderV4 required fields
869
+ specificationVersion: "v4",
870
+ // ProviderV4 required methods
692
871
  languageModel(modelId) {
693
872
  return chatModel(modelId);
694
873
  },
@@ -698,15 +877,15 @@ function createAnyRouter(options = {}) {
698
877
  imageModel(modelId) {
699
878
  return imageModel(modelId);
700
879
  },
701
- // ProviderV3 optional methods (deprecated textEmbeddingModel alias)
702
- textEmbeddingModel(modelId) {
703
- return embeddingModel(modelId);
704
- },
705
880
  // AnyRouter convenience alias
706
881
  chat(modelId) {
707
882
  return chatModel(modelId);
708
883
  },
709
- // Unsupported model types — throw NoSuchModelError per ProviderV3 contract
884
+ // ProviderV4 file-management interface
885
+ files() {
886
+ return new AnyRouterFiles(PROVIDER_ID, sharedFileCache);
887
+ },
888
+ // Unsupported optional model types — throw NoSuchModelError per ProviderV4 contract
710
889
  transcriptionModel(modelId) {
711
890
  throw new provider.NoSuchModelError({ modelId, modelType: "transcriptionModel" });
712
891
  },
@@ -722,6 +901,7 @@ function createAnyRouter(options = {}) {
722
901
  apiKey: options.apiKey,
723
902
  baseURL: options.baseURL,
724
903
  headers: options.headers,
904
+ app: options.app,
725
905
  fetch: options.fetch
726
906
  });
727
907
  }
@@ -732,6 +912,8 @@ var anyrouter = createAnyRouter();
732
912
 
733
913
  exports.AnyRouterChatLanguageModel = AnyRouterChatLanguageModel;
734
914
  exports.AnyRouterEmbeddingModel = AnyRouterEmbeddingModel;
915
+ exports.AnyRouterFiles = AnyRouterFiles;
735
916
  exports.AnyRouterImageModel = AnyRouterImageModel;
917
+ exports.FileReferenceCache = FileReferenceCache;
736
918
  exports.anyrouter = anyrouter;
737
919
  exports.createAnyRouter = createAnyRouter;