@anyr/ai-sdk-provider 0.1.6 → 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 CHANGED
@@ -3,8 +3,8 @@
3
3
  The [AnyRouter](https://anyrouter.dev) provider for the [Vercel AI SDK](https://ai-sdk.dev).
4
4
 
5
5
  Call every model on the AnyRouter gateway through the standard AI SDK
6
- interface — `generateText`, `streamText`, `generateObject`, `embedMany`,
7
- `generateImage` — with one key and one provider.
6
+ interface — `generateText`, `streamText`, `generateObject`, `embedMany` —
7
+ with one key and one provider.
8
8
 
9
9
  ## Install
10
10
 
@@ -147,23 +147,6 @@ const { embeddings } = await embedMany({
147
147
  console.log(embeddings[0]) // [0.123, -0.456, ...]
148
148
  ```
149
149
 
150
- ### Image generation
151
-
152
- ```ts
153
- import { createAnyRouter } from "@anyr/ai-sdk-provider"
154
- import { generateImage } from "ai"
155
-
156
- const anyrouter = createAnyRouter()
157
-
158
- const { image } = await generateImage({
159
- model: anyrouter.imageModel("openai/dall-e-3"),
160
- prompt: "A serene mountain lake at dawn, oil painting.",
161
- size: "1024x1024",
162
- })
163
-
164
- // image.base64 — base64-encoded PNG or URL depending on the model
165
- ```
166
-
167
150
  ### Provider options (routing, session, extra body)
168
151
 
169
152
  ```ts
@@ -227,7 +210,6 @@ const anyrouter = createAnyRouter({
227
210
  | `anyrouter.chat(modelId)` | `LanguageModelV3` | Chat / instruction language model |
228
211
  | `anyrouter.languageModel(modelId)` | `LanguageModelV3` | Alias for `.chat()` (ProviderV3 interface) |
229
212
  | `anyrouter.embeddingModel(modelId)` | `EmbeddingModelV3` | Text embedding model |
230
- | `anyrouter.imageModel(modelId)` | `ImageModelV3` | Image generation model |
231
213
  | `anyrouter.client` | `AnyRouter` | Raw `@anyr/sdk` client (escape hatch) |
232
214
 
233
215
  ## Default export
package/dist/index.d.cts CHANGED
@@ -1,5 +1,43 @@
1
- import { SharedV4ProviderReference, LanguageModelV4, LanguageModelV4CallOptions, LanguageModelV4GenerateResult, LanguageModelV4StreamResult, EmbeddingModelV4, EmbeddingModelV4CallOptions, EmbeddingModelV4Result, FilesV4, FilesV4UploadFileCallOptions, FilesV4UploadFileResult, ImageModelV4, ImageModelV4CallOptions, ImageModelV4Result, ProviderV4 } from '@ai-sdk/provider';
2
- import { AppAttribution, AnyRouter } from '@anyr/sdk';
1
+ import { SharedV4ProviderReference, LanguageModelV4, LanguageModelV4CallOptions, LanguageModelV4GenerateResult, LanguageModelV4StreamResult, EmbeddingModelV4, EmbeddingModelV4CallOptions, EmbeddingModelV4Result, FilesV4, FilesV4UploadFileCallOptions, FilesV4UploadFileResult, ProviderV4 } from '@ai-sdk/provider';
2
+ import { AnyRouter } from '@anyr/sdk';
3
+
4
+ /**
5
+ * Identity of the client making the request. Mirrors the `X-AnyRouter-*`
6
+ * headers the gateway reads in `inference-telemetry.ts`.
7
+ */
8
+ interface AppAttribution {
9
+ /**
10
+ * Stable machine identifier, becomes `generations.app_source`. Use a slug
11
+ * (`anyr-cli`, `anyr-agent`), not a display name.
12
+ */
13
+ source?: string;
14
+ /** Human-readable name, becomes `generations.app_title`. */
15
+ title?: string;
16
+ /** Client version, becomes `generations.app_version`. */
17
+ version?: string;
18
+ /**
19
+ * Curation categories (e.g. `cli-agent`). The gateway allowlists these and
20
+ * keeps at most two per request; unknown values are dropped server-side.
21
+ */
22
+ categories?: string[];
23
+ /**
24
+ * Public URL identifying the app, sent as `HTTP-Referer`. Only meaningful for
25
+ * apps that want to appear in the public app rankings.
26
+ */
27
+ url?: string;
28
+ }
29
+ /** This provider's own identity, used when the caller supplies no `app`. */
30
+ declare const PROVIDER_ATTRIBUTION: AppAttribution;
31
+ /**
32
+ * Translate an {@link AppAttribution} into wire headers. Blank/whitespace-only
33
+ * values are omitted rather than sent empty, so a partially-filled identity
34
+ * never overwrites a good value with `""`.
35
+ *
36
+ * Returns `{}` when `app` is undefined, so callers that pass no `app` send no
37
+ * attribution headers by default. This matches the provider contract in
38
+ * `types.ts`: browser callers are attributed by Referer instead.
39
+ */
40
+ declare function buildAppAttributionHeaders(app: AppAttribution | undefined): Record<string, string>;
3
41
 
4
42
  /**
5
43
  * The resolved representation of a cached file — a URL string ready to drop
@@ -107,13 +145,6 @@ interface AnyRouterProviderSettings {
107
145
  * Per-call `providerOptions.anyrouter.cacheTtl` takes precedence.
108
146
  */
109
147
  cacheTtl?: number;
110
- /**
111
- * Custom cache key for Cloudflare AI Gateway response caching, so requests
112
- * with different content can share a cached response. Sent as
113
- * `cf-aig-cache-key`. Per-call `providerOptions.anyrouter.cacheKey` takes
114
- * precedence.
115
- */
116
- cacheKey?: string;
117
148
  /**
118
149
  * Skip Cloudflare AI Gateway response caching for every request, even if a
119
150
  * default cache policy would otherwise apply. Sent as
@@ -201,26 +232,6 @@ declare class AnyRouterFiles implements FilesV4 {
201
232
  uploadFile(options: FilesV4UploadFileCallOptions): Promise<FilesV4UploadFileResult>;
202
233
  }
203
234
 
204
- interface AnyRouterImageConfig {
205
- provider: string;
206
- baseURL: string;
207
- headers: () => Record<string, string | undefined>;
208
- fetch?: typeof globalThis.fetch;
209
- }
210
- declare class AnyRouterImageModel implements ImageModelV4 {
211
- readonly specificationVersion: "v4";
212
- readonly provider: string;
213
- readonly modelId: string;
214
- /**
215
- * Most image generation APIs support only 1 image per call via the
216
- * standard `/images/generations` endpoint.
217
- */
218
- readonly maxImagesPerCall = 1;
219
- private readonly config;
220
- constructor(modelId: string, config: AnyRouterImageConfig);
221
- doGenerate(options: ImageModelV4CallOptions): Promise<ImageModelV4Result>;
222
- }
223
-
224
235
  interface AnyRouterProvider extends ProviderV4 {
225
236
  /** Create a chat language model by calling the provider directly. */
226
237
  (modelId: string): LanguageModelV4;
@@ -285,4 +296,4 @@ declare function createAnyRouter(options?: AnyRouterProviderSettings): AnyRouter
285
296
  */
286
297
  declare const anyrouter: AnyRouterProvider;
287
298
 
288
- export { type AnyRouterChatConfig, AnyRouterChatLanguageModel, type AnyRouterEmbeddingConfig, AnyRouterEmbeddingModel, AnyRouterFiles, type AnyRouterImageConfig, AnyRouterImageModel, type AnyRouterProvider, type AnyRouterProviderSettings, FileReferenceCache, type ResolvedFileEntry, anyrouter, createAnyRouter };
299
+ export { type AnyRouterChatConfig, AnyRouterChatLanguageModel, type AnyRouterEmbeddingConfig, AnyRouterEmbeddingModel, AnyRouterFiles, type AnyRouterProvider, type AnyRouterProviderSettings, type AppAttribution, FileReferenceCache, PROVIDER_ATTRIBUTION, type ResolvedFileEntry, anyrouter, buildAppAttributionHeaders, createAnyRouter };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,43 @@
1
- import { SharedV4ProviderReference, LanguageModelV4, LanguageModelV4CallOptions, LanguageModelV4GenerateResult, LanguageModelV4StreamResult, EmbeddingModelV4, EmbeddingModelV4CallOptions, EmbeddingModelV4Result, FilesV4, FilesV4UploadFileCallOptions, FilesV4UploadFileResult, ImageModelV4, ImageModelV4CallOptions, ImageModelV4Result, ProviderV4 } from '@ai-sdk/provider';
2
- import { AppAttribution, AnyRouter } from '@anyr/sdk';
1
+ import { SharedV4ProviderReference, LanguageModelV4, LanguageModelV4CallOptions, LanguageModelV4GenerateResult, LanguageModelV4StreamResult, EmbeddingModelV4, EmbeddingModelV4CallOptions, EmbeddingModelV4Result, FilesV4, FilesV4UploadFileCallOptions, FilesV4UploadFileResult, ProviderV4 } from '@ai-sdk/provider';
2
+ import { AnyRouter } from '@anyr/sdk';
3
+
4
+ /**
5
+ * Identity of the client making the request. Mirrors the `X-AnyRouter-*`
6
+ * headers the gateway reads in `inference-telemetry.ts`.
7
+ */
8
+ interface AppAttribution {
9
+ /**
10
+ * Stable machine identifier, becomes `generations.app_source`. Use a slug
11
+ * (`anyr-cli`, `anyr-agent`), not a display name.
12
+ */
13
+ source?: string;
14
+ /** Human-readable name, becomes `generations.app_title`. */
15
+ title?: string;
16
+ /** Client version, becomes `generations.app_version`. */
17
+ version?: string;
18
+ /**
19
+ * Curation categories (e.g. `cli-agent`). The gateway allowlists these and
20
+ * keeps at most two per request; unknown values are dropped server-side.
21
+ */
22
+ categories?: string[];
23
+ /**
24
+ * Public URL identifying the app, sent as `HTTP-Referer`. Only meaningful for
25
+ * apps that want to appear in the public app rankings.
26
+ */
27
+ url?: string;
28
+ }
29
+ /** This provider's own identity, used when the caller supplies no `app`. */
30
+ declare const PROVIDER_ATTRIBUTION: AppAttribution;
31
+ /**
32
+ * Translate an {@link AppAttribution} into wire headers. Blank/whitespace-only
33
+ * values are omitted rather than sent empty, so a partially-filled identity
34
+ * never overwrites a good value with `""`.
35
+ *
36
+ * Returns `{}` when `app` is undefined, so callers that pass no `app` send no
37
+ * attribution headers by default. This matches the provider contract in
38
+ * `types.ts`: browser callers are attributed by Referer instead.
39
+ */
40
+ declare function buildAppAttributionHeaders(app: AppAttribution | undefined): Record<string, string>;
3
41
 
4
42
  /**
5
43
  * The resolved representation of a cached file — a URL string ready to drop
@@ -107,13 +145,6 @@ interface AnyRouterProviderSettings {
107
145
  * Per-call `providerOptions.anyrouter.cacheTtl` takes precedence.
108
146
  */
109
147
  cacheTtl?: number;
110
- /**
111
- * Custom cache key for Cloudflare AI Gateway response caching, so requests
112
- * with different content can share a cached response. Sent as
113
- * `cf-aig-cache-key`. Per-call `providerOptions.anyrouter.cacheKey` takes
114
- * precedence.
115
- */
116
- cacheKey?: string;
117
148
  /**
118
149
  * Skip Cloudflare AI Gateway response caching for every request, even if a
119
150
  * default cache policy would otherwise apply. Sent as
@@ -201,26 +232,6 @@ declare class AnyRouterFiles implements FilesV4 {
201
232
  uploadFile(options: FilesV4UploadFileCallOptions): Promise<FilesV4UploadFileResult>;
202
233
  }
203
234
 
204
- interface AnyRouterImageConfig {
205
- provider: string;
206
- baseURL: string;
207
- headers: () => Record<string, string | undefined>;
208
- fetch?: typeof globalThis.fetch;
209
- }
210
- declare class AnyRouterImageModel implements ImageModelV4 {
211
- readonly specificationVersion: "v4";
212
- readonly provider: string;
213
- readonly modelId: string;
214
- /**
215
- * Most image generation APIs support only 1 image per call via the
216
- * standard `/images/generations` endpoint.
217
- */
218
- readonly maxImagesPerCall = 1;
219
- private readonly config;
220
- constructor(modelId: string, config: AnyRouterImageConfig);
221
- doGenerate(options: ImageModelV4CallOptions): Promise<ImageModelV4Result>;
222
- }
223
-
224
235
  interface AnyRouterProvider extends ProviderV4 {
225
236
  /** Create a chat language model by calling the provider directly. */
226
237
  (modelId: string): LanguageModelV4;
@@ -285,4 +296,4 @@ declare function createAnyRouter(options?: AnyRouterProviderSettings): AnyRouter
285
296
  */
286
297
  declare const anyrouter: AnyRouterProvider;
287
298
 
288
- export { type AnyRouterChatConfig, AnyRouterChatLanguageModel, type AnyRouterEmbeddingConfig, AnyRouterEmbeddingModel, AnyRouterFiles, type AnyRouterImageConfig, AnyRouterImageModel, type AnyRouterProvider, type AnyRouterProviderSettings, FileReferenceCache, type ResolvedFileEntry, anyrouter, createAnyRouter };
299
+ export { type AnyRouterChatConfig, AnyRouterChatLanguageModel, type AnyRouterEmbeddingConfig, AnyRouterEmbeddingModel, AnyRouterFiles, type AnyRouterProvider, type AnyRouterProviderSettings, type AppAttribution, FileReferenceCache, PROVIDER_ATTRIBUTION, type ResolvedFileEntry, anyrouter, buildAppAttributionHeaders, createAnyRouter };
package/dist/index.js CHANGED
@@ -1,11 +1,34 @@
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/anyrouter-chat-language-model.ts
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
+ }
9
32
  function convertToAnyRouterChatMessages(prompt, fileCache) {
10
33
  const messages = [];
11
34
  for (const message of prompt) {
@@ -166,8 +189,6 @@ var anyRouterProviderOptionsSchema = zod.z.object({
166
189
  extraBody: zod.z.record(zod.z.string(), zod.z.unknown()).optional(),
167
190
  /** Cache this response for N seconds, forwarded as the `cf-aig-cache-ttl` header. */
168
191
  cacheTtl: zod.z.number().optional(),
169
- /** Custom cache key, forwarded as the `cf-aig-cache-key` header. */
170
- cacheKey: zod.z.string().optional(),
171
192
  /** Skip response caching for this call, forwarded as `cf-aig-skip-cache: true`. */
172
193
  skipCache: zod.z.boolean().optional()
173
194
  });
@@ -203,7 +224,7 @@ var toolCallDeltaSchema = zod.z.object({
203
224
  var messageSchema = zod.z.object({
204
225
  content: zod.z.string().nullish(),
205
226
  reasoning_content: zod.z.string().nullish(),
206
- tool_calls: zod.z.array(toolCallSchema).optional()
227
+ tool_calls: zod.z.array(toolCallSchema).nullish()
207
228
  });
208
229
  var choiceSchema = zod.z.object({
209
230
  message: messageSchema.optional(),
@@ -213,12 +234,12 @@ var generateResponseSchema = zod.z.object({
213
234
  id: zod.z.string().optional(),
214
235
  model: zod.z.string().optional(),
215
236
  choices: zod.z.array(choiceSchema).optional(),
216
- usage: completionUsageSchema.optional()
237
+ usage: completionUsageSchema.nullish()
217
238
  });
218
239
  var deltaSchema = zod.z.object({
219
240
  content: zod.z.string().nullish(),
220
241
  reasoning_content: zod.z.string().nullish(),
221
- tool_calls: zod.z.array(toolCallDeltaSchema).optional()
242
+ tool_calls: zod.z.array(toolCallDeltaSchema).nullish()
222
243
  });
223
244
  var streamChoiceSchema = zod.z.object({
224
245
  delta: deltaSchema.optional(),
@@ -228,7 +249,10 @@ var streamChunkSchema = zod.z.object({
228
249
  id: zod.z.string().optional(),
229
250
  model: zod.z.string().optional(),
230
251
  choices: zod.z.array(streamChoiceSchema).optional(),
231
- usage: completionUsageSchema.optional()
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
232
256
  });
233
257
  function mapFinishReason(raw) {
234
258
  switch (raw) {
@@ -242,6 +266,8 @@ function mapFinishReason(raw) {
242
266
  return { unified: "tool-calls", raw };
243
267
  case "content_filter":
244
268
  return { unified: "content-filter", raw };
269
+ case "error":
270
+ return { unified: "error", raw };
245
271
  default:
246
272
  return { unified: "other", raw: raw ?? void 0 };
247
273
  }
@@ -318,6 +344,30 @@ function emitToolCallDeltas(toolCalls, acc, controller) {
318
344
  }
319
345
  }
320
346
  }
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
+ }
321
371
  function emitFinish(choice, usage, state, acc, controller) {
322
372
  closeReasoning(state, controller);
323
373
  if (state.activeTextId !== null) {
@@ -422,11 +472,9 @@ function buildRequestBody(modelId, options, settings, stream, perCallOptions, fi
422
472
  }
423
473
  function buildCacheHeaders(settings, perCallOptions) {
424
474
  const cacheTtl = perCallOptions.cacheTtl ?? settings.cacheTtl;
425
- const cacheKey = perCallOptions.cacheKey ?? settings.cacheKey;
426
475
  const skipCache = perCallOptions.skipCache ?? settings.skipCache;
427
476
  const headers = {};
428
477
  if (cacheTtl !== void 0) headers["cf-aig-cache-ttl"] = String(cacheTtl);
429
- if (cacheKey !== void 0) headers["cf-aig-cache-key"] = cacheKey;
430
478
  if (skipCache === true) headers["cf-aig-skip-cache"] = "true";
431
479
  return headers;
432
480
  }
@@ -441,7 +489,6 @@ var AnyRouterChatLanguageModel = class {
441
489
  this.provider = config.provider;
442
490
  this.config = config;
443
491
  }
444
- // ─── doGenerate ─────────────────────────────────────────────────────────
445
492
  async doGenerate(options) {
446
493
  const perCallOptions = await providerUtils.parseProviderOptions({
447
494
  provider: "anyrouter",
@@ -507,7 +554,6 @@ var AnyRouterChatLanguageModel = class {
507
554
  }
508
555
  };
509
556
  }
510
- // ─── doStream ───────────────────────────────────────────────────────────
511
557
  async doStream(options) {
512
558
  const perCallOptions = await providerUtils.parseProviderOptions({
513
559
  provider: "anyrouter",
@@ -522,8 +568,9 @@ var AnyRouterChatLanguageModel = class {
522
568
  perCallOptions,
523
569
  this.config.fileCache
524
570
  );
571
+ const url = `${this.config.baseURL}/chat/completions`;
525
572
  const { value: stream, responseHeaders } = await providerUtils.postJsonToApi({
526
- url: `${this.config.baseURL}/chat/completions`,
573
+ url,
527
574
  headers: providerUtils.combineHeaders(
528
575
  this.config.headers(),
529
576
  buildCacheHeaders(this.config.settings, perCallOptions),
@@ -558,21 +605,15 @@ var AnyRouterChatLanguageModel = class {
558
605
  return;
559
606
  }
560
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
+ }
561
613
  const choice = chunk.choices?.[0];
562
614
  if (chunk.usage) streamUsage = chunk.usage;
563
615
  emitResponseMetadata(chunk, state, selfModelId, controller);
564
- const delta = choice?.delta;
565
- if (delta) {
566
- if (typeof delta.reasoning_content === "string" && delta.reasoning_content.length > 0) {
567
- emitReasoningDelta(delta.reasoning_content, state, controller);
568
- }
569
- if (typeof delta.content === "string" && delta.content.length > 0) {
570
- emitTextDelta(delta.content, state, controller);
571
- }
572
- if (delta.tool_calls) {
573
- emitToolCallDeltas(delta.tool_calls, toolCallAccumulator, controller);
574
- }
575
- }
616
+ if (choice?.delta) emitDelta(choice.delta, state, toolCallAccumulator, controller);
576
617
  if (choice?.finish_reason) {
577
618
  finishChoice = choice;
578
619
  }
@@ -732,101 +773,12 @@ var AnyRouterFiles = class {
732
773
  };
733
774
  }
734
775
  };
735
- var anyRouterErrorSchema3 = zod.z.object({
736
- error: zod.z.object({
737
- code: zod.z.string().optional(),
738
- message: zod.z.string().optional()
739
- }).optional()
740
- });
741
- var imageGenerationResponseSchema = zod.z.object({
742
- created: zod.z.number().optional(),
743
- data: zod.z.array(
744
- zod.z.object({
745
- url: zod.z.string().optional(),
746
- b64_json: zod.z.string().optional(),
747
- revised_prompt: zod.z.string().optional()
748
- })
749
- )
750
- });
751
- var AnyRouterImageModel = class {
752
- specificationVersion = "v4";
753
- provider;
754
- modelId;
755
- /**
756
- * Most image generation APIs support only 1 image per call via the
757
- * standard `/images/generations` endpoint.
758
- */
759
- maxImagesPerCall = 1;
760
- config;
761
- constructor(modelId, config) {
762
- this.modelId = modelId;
763
- this.provider = config.provider;
764
- this.config = config;
765
- }
766
- async doGenerate(options) {
767
- if (options.mask) {
768
- throw new provider.UnsupportedFunctionalityError({
769
- functionality: "mask (inpainting) for AnyRouter image generation"
770
- });
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
- }
777
- const warnings = [];
778
- if (options.n > 1) {
779
- warnings.push({
780
- type: "unsupported",
781
- feature: `n=${options.n} (AnyRouter image endpoint supports n=1 per call)`
782
- });
783
- }
784
- const body = {
785
- model: this.modelId,
786
- prompt: options.prompt ?? "",
787
- n: Math.min(options.n, 1),
788
- ...options.size ? { size: options.size } : {},
789
- ...options.seed !== void 0 ? { seed: options.seed } : {},
790
- response_format: "b64_json"
791
- };
792
- const { value: response, responseHeaders } = await providerUtils.postJsonToApi({
793
- url: `${this.config.baseURL}/images/generations`,
794
- headers: providerUtils.combineHeaders(this.config.headers(), options.headers),
795
- body,
796
- failedResponseHandler: providerUtils.createJsonErrorResponseHandler({
797
- errorSchema: anyRouterErrorSchema3,
798
- errorToMessage: (data) => data.error?.message ?? "AnyRouter images API error",
799
- isRetryable: (res) => res.status >= 500
800
- }),
801
- successfulResponseHandler: providerUtils.createJsonResponseHandler(imageGenerationResponseSchema),
802
- abortSignal: options.abortSignal,
803
- fetch: this.config.fetch
804
- });
805
- const images = [];
806
- for (const item of response.data) {
807
- if (item.b64_json) {
808
- images.push(item.b64_json);
809
- } else if (item.url) {
810
- images.push(item.url);
811
- }
812
- }
813
- return {
814
- images,
815
- warnings,
816
- response: {
817
- timestamp: /* @__PURE__ */ new Date(),
818
- modelId: this.modelId,
819
- headers: responseHeaders
820
- }
821
- };
822
- }
823
- };
824
776
  var PROVIDER_ID = "anyrouter";
825
777
  function createAnyRouter(options = {}) {
826
778
  const baseURL = providerUtils.withoutTrailingSlash(options.baseURL ?? sdk.DEFAULT_BASE_URL) ?? sdk.DEFAULT_BASE_URL;
827
779
  const sharedFileCache = new FileReferenceCache();
828
780
  function getHeaders() {
829
- const attribution = options.app ? sdk.buildAppAttributionHeaders(options.app) : {};
781
+ const attribution = options.app ? buildAppAttributionHeaders(options.app) : {};
830
782
  if (options.keyless) {
831
783
  return providerUtils.combineHeaders(attribution, options.headers);
832
784
  }
@@ -855,14 +807,6 @@ function createAnyRouter(options = {}) {
855
807
  fetch: options.fetch
856
808
  });
857
809
  }
858
- function imageModel(modelId) {
859
- return new AnyRouterImageModel(modelId, {
860
- provider: PROVIDER_ID,
861
- baseURL,
862
- headers: getHeaders,
863
- fetch: options.fetch
864
- });
865
- }
866
810
  const provider$1 = (modelId) => chatModel(modelId);
867
811
  const providerObj = Object.assign(provider$1, {
868
812
  // ProviderV4 required fields
@@ -874,9 +818,6 @@ function createAnyRouter(options = {}) {
874
818
  embeddingModel(modelId) {
875
819
  return embeddingModel(modelId);
876
820
  },
877
- imageModel(modelId) {
878
- return imageModel(modelId);
879
- },
880
821
  // AnyRouter convenience alias
881
822
  chat(modelId) {
882
823
  return chatModel(modelId);
@@ -885,7 +826,11 @@ function createAnyRouter(options = {}) {
885
826
  files() {
886
827
  return new AnyRouterFiles(PROVIDER_ID, sharedFileCache);
887
828
  },
888
- // Unsupported optional model types — throw NoSuchModelError per ProviderV4 contract
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
+ },
889
834
  transcriptionModel(modelId) {
890
835
  throw new provider.NoSuchModelError({ modelId, modelType: "transcriptionModel" });
891
836
  },
@@ -901,7 +846,6 @@ function createAnyRouter(options = {}) {
901
846
  apiKey: options.apiKey,
902
847
  baseURL: options.baseURL,
903
848
  headers: options.headers,
904
- app: options.app,
905
849
  fetch: options.fetch
906
850
  });
907
851
  }
@@ -913,7 +857,8 @@ var anyrouter = createAnyRouter();
913
857
  exports.AnyRouterChatLanguageModel = AnyRouterChatLanguageModel;
914
858
  exports.AnyRouterEmbeddingModel = AnyRouterEmbeddingModel;
915
859
  exports.AnyRouterFiles = AnyRouterFiles;
916
- exports.AnyRouterImageModel = AnyRouterImageModel;
917
860
  exports.FileReferenceCache = FileReferenceCache;
861
+ exports.PROVIDER_ATTRIBUTION = PROVIDER_ATTRIBUTION;
918
862
  exports.anyrouter = anyrouter;
863
+ exports.buildAppAttributionHeaders = buildAppAttributionHeaders;
919
864
  exports.createAnyRouter = createAnyRouter;
package/dist/index.mjs CHANGED
@@ -1,9 +1,32 @@
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
- import { DEFAULT_BASE_URL, AnyRouter, buildAppAttributionHeaders } from '@anyr/sdk';
4
+ import { DEFAULT_BASE_URL, AnyRouter } from '@anyr/sdk';
5
5
 
6
- // src/anyrouter-chat-language-model.ts
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
+ }
7
30
  function convertToAnyRouterChatMessages(prompt, fileCache) {
8
31
  const messages = [];
9
32
  for (const message of prompt) {
@@ -164,8 +187,6 @@ var anyRouterProviderOptionsSchema = z.object({
164
187
  extraBody: z.record(z.string(), z.unknown()).optional(),
165
188
  /** Cache this response for N seconds, forwarded as the `cf-aig-cache-ttl` header. */
166
189
  cacheTtl: z.number().optional(),
167
- /** Custom cache key, forwarded as the `cf-aig-cache-key` header. */
168
- cacheKey: z.string().optional(),
169
190
  /** Skip response caching for this call, forwarded as `cf-aig-skip-cache: true`. */
170
191
  skipCache: z.boolean().optional()
171
192
  });
@@ -201,7 +222,7 @@ var toolCallDeltaSchema = z.object({
201
222
  var messageSchema = z.object({
202
223
  content: z.string().nullish(),
203
224
  reasoning_content: z.string().nullish(),
204
- tool_calls: z.array(toolCallSchema).optional()
225
+ tool_calls: z.array(toolCallSchema).nullish()
205
226
  });
206
227
  var choiceSchema = z.object({
207
228
  message: messageSchema.optional(),
@@ -211,12 +232,12 @@ var generateResponseSchema = z.object({
211
232
  id: z.string().optional(),
212
233
  model: z.string().optional(),
213
234
  choices: z.array(choiceSchema).optional(),
214
- usage: completionUsageSchema.optional()
235
+ usage: completionUsageSchema.nullish()
215
236
  });
216
237
  var deltaSchema = z.object({
217
238
  content: z.string().nullish(),
218
239
  reasoning_content: z.string().nullish(),
219
- tool_calls: z.array(toolCallDeltaSchema).optional()
240
+ tool_calls: z.array(toolCallDeltaSchema).nullish()
220
241
  });
221
242
  var streamChoiceSchema = z.object({
222
243
  delta: deltaSchema.optional(),
@@ -226,7 +247,10 @@ var streamChunkSchema = z.object({
226
247
  id: z.string().optional(),
227
248
  model: z.string().optional(),
228
249
  choices: z.array(streamChoiceSchema).optional(),
229
- usage: completionUsageSchema.optional()
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
230
254
  });
231
255
  function mapFinishReason(raw) {
232
256
  switch (raw) {
@@ -240,6 +264,8 @@ function mapFinishReason(raw) {
240
264
  return { unified: "tool-calls", raw };
241
265
  case "content_filter":
242
266
  return { unified: "content-filter", raw };
267
+ case "error":
268
+ return { unified: "error", raw };
243
269
  default:
244
270
  return { unified: "other", raw: raw ?? void 0 };
245
271
  }
@@ -316,6 +342,30 @@ function emitToolCallDeltas(toolCalls, acc, controller) {
316
342
  }
317
343
  }
318
344
  }
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
+ }
319
369
  function emitFinish(choice, usage, state, acc, controller) {
320
370
  closeReasoning(state, controller);
321
371
  if (state.activeTextId !== null) {
@@ -420,11 +470,9 @@ function buildRequestBody(modelId, options, settings, stream, perCallOptions, fi
420
470
  }
421
471
  function buildCacheHeaders(settings, perCallOptions) {
422
472
  const cacheTtl = perCallOptions.cacheTtl ?? settings.cacheTtl;
423
- const cacheKey = perCallOptions.cacheKey ?? settings.cacheKey;
424
473
  const skipCache = perCallOptions.skipCache ?? settings.skipCache;
425
474
  const headers = {};
426
475
  if (cacheTtl !== void 0) headers["cf-aig-cache-ttl"] = String(cacheTtl);
427
- if (cacheKey !== void 0) headers["cf-aig-cache-key"] = cacheKey;
428
476
  if (skipCache === true) headers["cf-aig-skip-cache"] = "true";
429
477
  return headers;
430
478
  }
@@ -439,7 +487,6 @@ var AnyRouterChatLanguageModel = class {
439
487
  this.provider = config.provider;
440
488
  this.config = config;
441
489
  }
442
- // ─── doGenerate ─────────────────────────────────────────────────────────
443
490
  async doGenerate(options) {
444
491
  const perCallOptions = await parseProviderOptions({
445
492
  provider: "anyrouter",
@@ -505,7 +552,6 @@ var AnyRouterChatLanguageModel = class {
505
552
  }
506
553
  };
507
554
  }
508
- // ─── doStream ───────────────────────────────────────────────────────────
509
555
  async doStream(options) {
510
556
  const perCallOptions = await parseProviderOptions({
511
557
  provider: "anyrouter",
@@ -520,8 +566,9 @@ var AnyRouterChatLanguageModel = class {
520
566
  perCallOptions,
521
567
  this.config.fileCache
522
568
  );
569
+ const url = `${this.config.baseURL}/chat/completions`;
523
570
  const { value: stream, responseHeaders } = await postJsonToApi({
524
- url: `${this.config.baseURL}/chat/completions`,
571
+ url,
525
572
  headers: combineHeaders(
526
573
  this.config.headers(),
527
574
  buildCacheHeaders(this.config.settings, perCallOptions),
@@ -556,21 +603,15 @@ var AnyRouterChatLanguageModel = class {
556
603
  return;
557
604
  }
558
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
+ }
559
611
  const choice = chunk.choices?.[0];
560
612
  if (chunk.usage) streamUsage = chunk.usage;
561
613
  emitResponseMetadata(chunk, state, selfModelId, controller);
562
- const delta = choice?.delta;
563
- if (delta) {
564
- if (typeof delta.reasoning_content === "string" && delta.reasoning_content.length > 0) {
565
- emitReasoningDelta(delta.reasoning_content, state, controller);
566
- }
567
- if (typeof delta.content === "string" && delta.content.length > 0) {
568
- emitTextDelta(delta.content, state, controller);
569
- }
570
- if (delta.tool_calls) {
571
- emitToolCallDeltas(delta.tool_calls, toolCallAccumulator, controller);
572
- }
573
- }
614
+ if (choice?.delta) emitDelta(choice.delta, state, toolCallAccumulator, controller);
574
615
  if (choice?.finish_reason) {
575
616
  finishChoice = choice;
576
617
  }
@@ -730,95 +771,6 @@ var AnyRouterFiles = class {
730
771
  };
731
772
  }
732
773
  };
733
- var anyRouterErrorSchema3 = z.object({
734
- error: z.object({
735
- code: z.string().optional(),
736
- message: z.string().optional()
737
- }).optional()
738
- });
739
- var imageGenerationResponseSchema = z.object({
740
- created: z.number().optional(),
741
- data: z.array(
742
- z.object({
743
- url: z.string().optional(),
744
- b64_json: z.string().optional(),
745
- revised_prompt: z.string().optional()
746
- })
747
- )
748
- });
749
- var AnyRouterImageModel = class {
750
- specificationVersion = "v4";
751
- provider;
752
- modelId;
753
- /**
754
- * Most image generation APIs support only 1 image per call via the
755
- * standard `/images/generations` endpoint.
756
- */
757
- maxImagesPerCall = 1;
758
- config;
759
- constructor(modelId, config) {
760
- this.modelId = modelId;
761
- this.provider = config.provider;
762
- this.config = config;
763
- }
764
- async doGenerate(options) {
765
- if (options.mask) {
766
- throw new UnsupportedFunctionalityError({
767
- functionality: "mask (inpainting) for AnyRouter image generation"
768
- });
769
- }
770
- if (options.files && options.files.length > 0) {
771
- throw new UnsupportedFunctionalityError({
772
- functionality: "image editing (files) for AnyRouter image generation"
773
- });
774
- }
775
- const warnings = [];
776
- if (options.n > 1) {
777
- warnings.push({
778
- type: "unsupported",
779
- feature: `n=${options.n} (AnyRouter image endpoint supports n=1 per call)`
780
- });
781
- }
782
- const body = {
783
- model: this.modelId,
784
- prompt: options.prompt ?? "",
785
- n: Math.min(options.n, 1),
786
- ...options.size ? { size: options.size } : {},
787
- ...options.seed !== void 0 ? { seed: options.seed } : {},
788
- response_format: "b64_json"
789
- };
790
- const { value: response, responseHeaders } = await postJsonToApi({
791
- url: `${this.config.baseURL}/images/generations`,
792
- headers: combineHeaders(this.config.headers(), options.headers),
793
- body,
794
- failedResponseHandler: createJsonErrorResponseHandler({
795
- errorSchema: anyRouterErrorSchema3,
796
- errorToMessage: (data) => data.error?.message ?? "AnyRouter images API error",
797
- isRetryable: (res) => res.status >= 500
798
- }),
799
- successfulResponseHandler: createJsonResponseHandler(imageGenerationResponseSchema),
800
- abortSignal: options.abortSignal,
801
- fetch: this.config.fetch
802
- });
803
- const images = [];
804
- for (const item of response.data) {
805
- if (item.b64_json) {
806
- images.push(item.b64_json);
807
- } else if (item.url) {
808
- images.push(item.url);
809
- }
810
- }
811
- return {
812
- images,
813
- warnings,
814
- response: {
815
- timestamp: /* @__PURE__ */ new Date(),
816
- modelId: this.modelId,
817
- headers: responseHeaders
818
- }
819
- };
820
- }
821
- };
822
774
  var PROVIDER_ID = "anyrouter";
823
775
  function createAnyRouter(options = {}) {
824
776
  const baseURL = withoutTrailingSlash(options.baseURL ?? DEFAULT_BASE_URL) ?? DEFAULT_BASE_URL;
@@ -853,14 +805,6 @@ function createAnyRouter(options = {}) {
853
805
  fetch: options.fetch
854
806
  });
855
807
  }
856
- function imageModel(modelId) {
857
- return new AnyRouterImageModel(modelId, {
858
- provider: PROVIDER_ID,
859
- baseURL,
860
- headers: getHeaders,
861
- fetch: options.fetch
862
- });
863
- }
864
808
  const provider = (modelId) => chatModel(modelId);
865
809
  const providerObj = Object.assign(provider, {
866
810
  // ProviderV4 required fields
@@ -872,9 +816,6 @@ function createAnyRouter(options = {}) {
872
816
  embeddingModel(modelId) {
873
817
  return embeddingModel(modelId);
874
818
  },
875
- imageModel(modelId) {
876
- return imageModel(modelId);
877
- },
878
819
  // AnyRouter convenience alias
879
820
  chat(modelId) {
880
821
  return chatModel(modelId);
@@ -883,7 +824,11 @@ function createAnyRouter(options = {}) {
883
824
  files() {
884
825
  return new AnyRouterFiles(PROVIDER_ID, sharedFileCache);
885
826
  },
886
- // Unsupported optional model types — throw NoSuchModelError per ProviderV4 contract
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
+ },
887
832
  transcriptionModel(modelId) {
888
833
  throw new NoSuchModelError({ modelId, modelType: "transcriptionModel" });
889
834
  },
@@ -899,7 +844,6 @@ function createAnyRouter(options = {}) {
899
844
  apiKey: options.apiKey,
900
845
  baseURL: options.baseURL,
901
846
  headers: options.headers,
902
- app: options.app,
903
847
  fetch: options.fetch
904
848
  });
905
849
  }
@@ -908,4 +852,4 @@ function createAnyRouter(options = {}) {
908
852
  }
909
853
  var anyrouter = createAnyRouter();
910
854
 
911
- export { AnyRouterChatLanguageModel, AnyRouterEmbeddingModel, AnyRouterFiles, AnyRouterImageModel, FileReferenceCache, anyrouter, createAnyRouter };
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.6",
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": "tsc --noEmit",
34
+ "typecheck": "tsgo --noEmit",
35
35
  "test": "vitest run",
36
36
  "prepublishOnly": "tsup"
37
37
  },
@@ -49,13 +49,13 @@
49
49
  "homepage": "https://anyrouter.dev",
50
50
  "repository": {
51
51
  "type": "git",
52
- "url": "git+https://github.com/duyet/anyrouter.git",
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/duyet/anyrouter/issues"
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"
@@ -70,10 +70,14 @@
70
70
  "zod": "^3.25.0 || ^4.0.0"
71
71
  },
72
72
  "devDependencies": {
73
- "@ai-sdk/provider": "^4.0.0",
74
- "@ai-sdk/provider-utils": "^5.0.0",
73
+ "@ai-sdk/provider": "4.0.2",
74
+ "@ai-sdk/provider-utils": "5.0.5",
75
75
  "@types/node": "^24.0.0",
76
- "ai": "^7.0.0",
77
- "zod": "^4.4.3"
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
  }