@anyr/ai-sdk-provider 0.1.4 → 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;
@@ -292,7 +318,7 @@ function emitToolCallDeltas(toolCalls, acc, controller) {
292
318
  }
293
319
  }
294
320
  }
295
- function emitFinish(choice, chunk, state, acc, controller) {
321
+ function emitFinish(choice, usage, state, acc, controller) {
296
322
  closeReasoning(state, controller);
297
323
  if (state.activeTextId !== null) {
298
324
  controller.enqueue({ type: "text-end", id: state.activeTextId });
@@ -311,8 +337,8 @@ function emitFinish(choice, chunk, state, acc, controller) {
311
337
  }
312
338
  controller.enqueue({
313
339
  type: "finish",
314
- usage: buildUsage(chunk.usage),
315
- finishReason: mapFinishReason(choice.finish_reason)
340
+ usage: buildUsage(usage),
341
+ finishReason: mapFinishReason(choice?.finish_reason)
316
342
  });
317
343
  }
318
344
  function mapToolChoice(toolChoice) {
@@ -360,14 +386,15 @@ function buildTools(tools) {
360
386
  }));
361
387
  return fns.length > 0 ? fns : void 0;
362
388
  }
363
- function buildRequestBody(modelId, options, settings, stream, perCallOptions) {
364
- const messages = convertToAnyRouterChatMessages(options.prompt);
389
+ function buildRequestBody(modelId, options, settings, stream, perCallOptions, fileCache) {
390
+ const messages = convertToAnyRouterChatMessages(options.prompt, fileCache);
365
391
  const responseFormat = buildResponseFormat(options.responseFormat);
366
392
  const tools = buildTools(options.tools);
367
393
  const toolChoice = mapToolChoice(options.toolChoice);
368
394
  const provider = perCallOptions.provider ?? settings.provider;
369
395
  const sessionId = perCallOptions.sessionId ?? settings.sessionId;
370
396
  const extraBody = { ...settings.extraBody, ...perCallOptions.extraBody };
397
+ const reasoningEffort = mapReasoningEffort(options.reasoning);
371
398
  const body = {
372
399
  model: modelId,
373
400
  messages,
@@ -385,6 +412,7 @@ function buildRequestBody(modelId, options, settings, stream, perCallOptions) {
385
412
  tool_choice: toolChoice,
386
413
  provider: provider ?? void 0,
387
414
  session_id: sessionId ?? void 0,
415
+ reasoning_effort: reasoningEffort,
388
416
  ...extraBody
389
417
  };
390
418
  for (const key of Object.keys(body)) {
@@ -392,8 +420,18 @@ function buildRequestBody(modelId, options, settings, stream, perCallOptions) {
392
420
  }
393
421
  return body;
394
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
+ }
395
433
  var AnyRouterChatLanguageModel = class {
396
- specificationVersion = "v3";
434
+ specificationVersion = "v4";
397
435
  provider;
398
436
  modelId;
399
437
  supportedUrls = {};
@@ -415,11 +453,16 @@ var AnyRouterChatLanguageModel = class {
415
453
  options,
416
454
  this.config.settings,
417
455
  false,
418
- perCallOptions
456
+ perCallOptions,
457
+ this.config.fileCache
419
458
  );
420
459
  const { value: response, responseHeaders } = await providerUtils.postJsonToApi({
421
460
  url: `${this.config.baseURL}/chat/completions`,
422
- 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
+ ),
423
466
  body,
424
467
  failedResponseHandler: providerUtils.createJsonErrorResponseHandler({
425
468
  errorSchema: anyRouterErrorSchema,
@@ -471,10 +514,21 @@ var AnyRouterChatLanguageModel = class {
471
514
  providerOptions: options.providerOptions,
472
515
  schema: anyRouterProviderOptionsSchema
473
516
  }) ?? {};
474
- 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
+ );
475
525
  const { value: stream, responseHeaders } = await providerUtils.postJsonToApi({
476
526
  url: `${this.config.baseURL}/chat/completions`,
477
- 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
+ ),
478
532
  body,
479
533
  failedResponseHandler: providerUtils.createJsonErrorResponseHandler({
480
534
  errorSchema: anyRouterErrorSchema,
@@ -492,6 +546,8 @@ var AnyRouterChatLanguageModel = class {
492
546
  metadataEmitted: false
493
547
  };
494
548
  const selfModelId = this.modelId;
549
+ let streamUsage;
550
+ let finishChoice;
495
551
  const transformStream = new TransformStream({
496
552
  start(controller) {
497
553
  controller.enqueue({ type: "stream-start", warnings: [] });
@@ -503,6 +559,7 @@ var AnyRouterChatLanguageModel = class {
503
559
  }
504
560
  const chunk = chunkResult.value;
505
561
  const choice = chunk.choices?.[0];
562
+ if (chunk.usage) streamUsage = chunk.usage;
506
563
  emitResponseMetadata(chunk, state, selfModelId, controller);
507
564
  const delta = choice?.delta;
508
565
  if (delta) {
@@ -517,14 +574,11 @@ var AnyRouterChatLanguageModel = class {
517
574
  }
518
575
  }
519
576
  if (choice?.finish_reason) {
520
- emitFinish(choice, chunk, state, toolCallAccumulator, controller);
577
+ finishChoice = choice;
521
578
  }
522
579
  },
523
580
  flush(controller) {
524
- closeReasoning(state, controller);
525
- if (state.activeTextId !== null) {
526
- controller.enqueue({ type: "text-end", id: state.activeTextId });
527
- }
581
+ emitFinish(finishChoice, streamUsage, state, toolCallAccumulator, controller);
528
582
  }
529
583
  });
530
584
  return {
@@ -552,7 +606,7 @@ var embeddingResponseSchema = zod.z.object({
552
606
  }).optional()
553
607
  });
554
608
  var AnyRouterEmbeddingModel = class {
555
- specificationVersion = "v3";
609
+ specificationVersion = "v4";
556
610
  provider;
557
611
  modelId;
558
612
  /** No hard per-call limit imposed by the provider layer. */
@@ -590,6 +644,94 @@ var AnyRouterEmbeddingModel = class {
590
644
  };
591
645
  }
592
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
+ };
593
735
  var anyRouterErrorSchema3 = zod.z.object({
594
736
  error: zod.z.object({
595
737
  code: zod.z.string().optional(),
@@ -607,7 +749,7 @@ var imageGenerationResponseSchema = zod.z.object({
607
749
  )
608
750
  });
609
751
  var AnyRouterImageModel = class {
610
- specificationVersion = "v3";
752
+ specificationVersion = "v4";
611
753
  provider;
612
754
  modelId;
613
755
  /**
@@ -627,6 +769,11 @@ var AnyRouterImageModel = class {
627
769
  functionality: "mask (inpainting) for AnyRouter image generation"
628
770
  });
629
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
+ }
630
777
  const warnings = [];
631
778
  if (options.n > 1) {
632
779
  warnings.push({
@@ -677,13 +824,18 @@ var AnyRouterImageModel = class {
677
824
  var PROVIDER_ID = "anyrouter";
678
825
  function createAnyRouter(options = {}) {
679
826
  const baseURL = providerUtils.withoutTrailingSlash(options.baseURL ?? sdk.DEFAULT_BASE_URL) ?? sdk.DEFAULT_BASE_URL;
827
+ const sharedFileCache = new FileReferenceCache();
680
828
  function getHeaders() {
829
+ const attribution = options.app ? sdk.buildAppAttributionHeaders(options.app) : {};
830
+ if (options.keyless) {
831
+ return providerUtils.combineHeaders(attribution, options.headers);
832
+ }
681
833
  const apiKey = providerUtils.loadApiKey({
682
834
  apiKey: options.apiKey,
683
835
  environmentVariableName: "ANYROUTER_API_KEY",
684
836
  description: "AnyRouter"
685
837
  });
686
- return providerUtils.combineHeaders({ Authorization: `Bearer ${apiKey}` }, options.headers);
838
+ return providerUtils.combineHeaders({ Authorization: `Bearer ${apiKey}`, ...attribution }, options.headers);
687
839
  }
688
840
  function chatModel(modelId) {
689
841
  return new AnyRouterChatLanguageModel(modelId, {
@@ -691,7 +843,8 @@ function createAnyRouter(options = {}) {
691
843
  baseURL,
692
844
  headers: getHeaders,
693
845
  fetch: options.fetch,
694
- settings: options
846
+ settings: options,
847
+ fileCache: sharedFileCache
695
848
  });
696
849
  }
697
850
  function embeddingModel(modelId) {
@@ -712,9 +865,9 @@ function createAnyRouter(options = {}) {
712
865
  }
713
866
  const provider$1 = (modelId) => chatModel(modelId);
714
867
  const providerObj = Object.assign(provider$1, {
715
- // ProviderV3 required fields
716
- specificationVersion: "v3",
717
- // ProviderV3 required methods
868
+ // ProviderV4 required fields
869
+ specificationVersion: "v4",
870
+ // ProviderV4 required methods
718
871
  languageModel(modelId) {
719
872
  return chatModel(modelId);
720
873
  },
@@ -724,15 +877,15 @@ function createAnyRouter(options = {}) {
724
877
  imageModel(modelId) {
725
878
  return imageModel(modelId);
726
879
  },
727
- // ProviderV3 optional methods (deprecated textEmbeddingModel alias)
728
- textEmbeddingModel(modelId) {
729
- return embeddingModel(modelId);
730
- },
731
880
  // AnyRouter convenience alias
732
881
  chat(modelId) {
733
882
  return chatModel(modelId);
734
883
  },
735
- // 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
736
889
  transcriptionModel(modelId) {
737
890
  throw new provider.NoSuchModelError({ modelId, modelType: "transcriptionModel" });
738
891
  },
@@ -748,6 +901,7 @@ function createAnyRouter(options = {}) {
748
901
  apiKey: options.apiKey,
749
902
  baseURL: options.baseURL,
750
903
  headers: options.headers,
904
+ app: options.app,
751
905
  fetch: options.fetch
752
906
  });
753
907
  }
@@ -758,6 +912,8 @@ var anyrouter = createAnyRouter();
758
912
 
759
913
  exports.AnyRouterChatLanguageModel = AnyRouterChatLanguageModel;
760
914
  exports.AnyRouterEmbeddingModel = AnyRouterEmbeddingModel;
915
+ exports.AnyRouterFiles = AnyRouterFiles;
761
916
  exports.AnyRouterImageModel = AnyRouterImageModel;
917
+ exports.FileReferenceCache = FileReferenceCache;
762
918
  exports.anyrouter = anyrouter;
763
919
  exports.createAnyRouter = createAnyRouter;