@earendil-works/pi-ai 1.1.0 → 1.2.0

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.
Files changed (52) hide show
  1. package/dist/api/anthropic-messages.d.ts.map +1 -1
  2. package/dist/api/anthropic-messages.js +63 -22
  3. package/dist/api/anthropic-messages.js.map +1 -1
  4. package/dist/api/mistral-conversations.d.ts.map +1 -1
  5. package/dist/api/mistral-conversations.js +29 -13
  6. package/dist/api/mistral-conversations.js.map +1 -1
  7. package/dist/api/openai-codex-responses.d.ts.map +1 -1
  8. package/dist/api/openai-codex-responses.js +2 -1
  9. package/dist/api/openai-codex-responses.js.map +1 -1
  10. package/dist/models.d.ts +5 -0
  11. package/dist/models.d.ts.map +1 -1
  12. package/dist/models.js +1 -0
  13. package/dist/models.js.map +1 -1
  14. package/dist/providers/compat-schema.d.ts +338 -0
  15. package/dist/providers/compat-schema.d.ts.map +1 -0
  16. package/dist/providers/compat-schema.js +375 -0
  17. package/dist/providers/compat-schema.js.map +1 -0
  18. package/dist/providers/data/.manifest.json +1 -1
  19. package/dist/providers/data/amazon-bedrock.json +1 -1
  20. package/dist/providers/data/anthropic.json +1 -1
  21. package/dist/providers/data/baseten.json +1 -1
  22. package/dist/providers/data/cloudflare-ai-gateway.json +1 -1
  23. package/dist/providers/data/cloudflare-workers-ai.json +1 -1
  24. package/dist/providers/data/github-copilot.json +1 -1
  25. package/dist/providers/data/google-vertex.json +1 -1
  26. package/dist/providers/data/huggingface.json +1 -1
  27. package/dist/providers/data/meta.json +1 -1
  28. package/dist/providers/data/mistral.json +1 -1
  29. package/dist/providers/data/opencode-go.json +1 -1
  30. package/dist/providers/data/opencode.json +1 -1
  31. package/dist/providers/data/openrouter.json +1 -1
  32. package/dist/providers/data/radius.json +1 -1
  33. package/dist/providers/data/vercel-ai-gateway.json +1 -1
  34. package/dist/providers/model-schema.d.ts +91 -0
  35. package/dist/providers/model-schema.d.ts.map +1 -0
  36. package/dist/providers/model-schema.js +55 -0
  37. package/dist/providers/model-schema.js.map +1 -0
  38. package/dist/providers/openrouter-api.d.ts +40 -0
  39. package/dist/providers/openrouter-api.d.ts.map +1 -0
  40. package/dist/providers/openrouter-api.js +41 -0
  41. package/dist/providers/openrouter-api.js.map +1 -0
  42. package/dist/providers/openrouter.d.ts +11 -1
  43. package/dist/providers/openrouter.d.ts.map +1 -1
  44. package/dist/providers/openrouter.js +52 -1
  45. package/dist/providers/openrouter.js.map +1 -1
  46. package/dist/types.d.ts +5 -303
  47. package/dist/types.d.ts.map +1 -1
  48. package/dist/types.js.map +1 -1
  49. package/dist/utils/retry.d.ts.map +1 -1
  50. package/dist/utils/retry.js +3 -2
  51. package/dist/utils/retry.js.map +1 -1
  52. package/package.json +2 -2
@@ -0,0 +1,91 @@
1
+ import { type Static, Type } from "typebox";
2
+ export declare const THINKING_LEVELS: readonly ["minimal", "low", "medium", "high", "xhigh", "max"];
3
+ export declare const MODEL_THINKING_LEVELS: readonly ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
4
+ export declare const MODEL_INPUT_MODALITIES: readonly ["text", "image"];
5
+ export declare const CACHE_RETENTIONS: readonly ["none", "short", "long"];
6
+ export declare const MODEL_PROMPT_CACHE_RETENTIONS: readonly ["short", "long"];
7
+ export declare const ThinkingLevelSchema: Type.TEnum<["minimal", "low", "medium", "high", "xhigh", "max"]>;
8
+ export declare const ModelThinkingLevelSchema: Type.TEnum<["off", "minimal", "low", "medium", "high", "xhigh", "max"]>;
9
+ export declare const ModelInputModalitySchema: Type.TEnum<["text", "image"]>;
10
+ export declare const CacheRetentionSchema: Type.TEnum<["none", "short", "long"]>;
11
+ export declare const ThinkingLevelMapSchema: Type.TObject<{
12
+ high: Type.TOptional<Type.TUnion<[Type.TString, Type.TNull]>>;
13
+ low: Type.TOptional<Type.TUnion<[Type.TString, Type.TNull]>>;
14
+ max: Type.TOptional<Type.TUnion<[Type.TString, Type.TNull]>>;
15
+ medium: Type.TOptional<Type.TUnion<[Type.TString, Type.TNull]>>;
16
+ minimal: Type.TOptional<Type.TUnion<[Type.TString, Type.TNull]>>;
17
+ off: Type.TOptional<Type.TUnion<[Type.TString, Type.TNull]>>;
18
+ xhigh: Type.TOptional<Type.TUnion<[Type.TString, Type.TNull]>>;
19
+ }>;
20
+ export declare const ModelPromptCacheSchema: Type.TObject<{
21
+ long: Type.TOptional<Type.TNumber>;
22
+ short: Type.TOptional<Type.TNumber>;
23
+ }>;
24
+ export declare const ModelCostRatesSchema: Type.TObject<{
25
+ input: Type.TNumber;
26
+ output: Type.TNumber;
27
+ cacheRead: Type.TNumber;
28
+ cacheWrite: Type.TNumber;
29
+ }>;
30
+ export declare const ModelCostTierSchema: Type.TObject<{
31
+ input: Type.TNumber;
32
+ output: Type.TNumber;
33
+ cacheRead: Type.TNumber;
34
+ cacheWrite: Type.TNumber;
35
+ inputTokensAbove: Type.TNumber;
36
+ }>;
37
+ export declare const ModelCostSchema: Type.TObject<{
38
+ input: Type.TNumber;
39
+ output: Type.TNumber;
40
+ cacheRead: Type.TNumber;
41
+ cacheWrite: Type.TNumber;
42
+ tiers: Type.TOptional<Type.TArray<Type.TObject<{
43
+ input: Type.TNumber;
44
+ output: Type.TNumber;
45
+ cacheRead: Type.TNumber;
46
+ cacheWrite: Type.TNumber;
47
+ inputTokensAbove: Type.TNumber;
48
+ }>>>;
49
+ }>;
50
+ export declare const ModelImageResizeOptionsSchema: Type.TObject<{
51
+ maxWidth: Type.TOptional<Type.TInteger>;
52
+ maxHeight: Type.TOptional<Type.TInteger>;
53
+ maxBytes: Type.TOptional<Type.TInteger>;
54
+ jpegQuality: Type.TOptional<Type.TInteger>;
55
+ }>;
56
+ export declare const ModelImageInputLimitsSchema: Type.TObject<{
57
+ resize: Type.TOptional<Type.TObject<{
58
+ maxWidth: Type.TOptional<Type.TInteger>;
59
+ maxHeight: Type.TOptional<Type.TInteger>;
60
+ maxBytes: Type.TOptional<Type.TInteger>;
61
+ jpegQuality: Type.TOptional<Type.TInteger>;
62
+ }>>;
63
+ maxPerMessage: Type.TOptional<Type.TInteger>;
64
+ maxPerRequest: Type.TOptional<Type.TInteger>;
65
+ }>;
66
+ export declare const ModelInputLimitsSchema: Type.TObject<{
67
+ maxRequestBytes: Type.TOptional<Type.TInteger>;
68
+ images: Type.TOptional<Type.TObject<{
69
+ resize: Type.TOptional<Type.TObject<{
70
+ maxWidth: Type.TOptional<Type.TInteger>;
71
+ maxHeight: Type.TOptional<Type.TInteger>;
72
+ maxBytes: Type.TOptional<Type.TInteger>;
73
+ jpegQuality: Type.TOptional<Type.TInteger>;
74
+ }>>;
75
+ maxPerMessage: Type.TOptional<Type.TInteger>;
76
+ maxPerRequest: Type.TOptional<Type.TInteger>;
77
+ }>>;
78
+ }>;
79
+ export type ThinkingLevel = Static<typeof ThinkingLevelSchema>;
80
+ export type ModelThinkingLevel = Static<typeof ModelThinkingLevelSchema>;
81
+ export type ThinkingLevelMap = Static<typeof ThinkingLevelMapSchema>;
82
+ export type ModelInputModality = Static<typeof ModelInputModalitySchema>;
83
+ export type CacheRetention = Static<typeof CacheRetentionSchema>;
84
+ export type ModelPromptCache = Static<typeof ModelPromptCacheSchema>;
85
+ export type ModelCostRates = Static<typeof ModelCostRatesSchema>;
86
+ export type ModelCostTier = Static<typeof ModelCostTierSchema>;
87
+ export type ModelCost = Static<typeof ModelCostSchema>;
88
+ export type ModelImageResizeOptions = Static<typeof ModelImageResizeOptionsSchema>;
89
+ export type ModelImageInputLimits = Static<typeof ModelImageInputLimitsSchema>;
90
+ export type ModelInputLimits = Static<typeof ModelInputLimitsSchema>;
91
+ //# sourceMappingURL=model-schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-schema.d.ts","sourceRoot":"","sources":["../../src/providers/model-schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAuB,IAAI,EAAE,MAAM,SAAS,CAAC;AAEjE,eAAO,MAAM,eAAe,YAAI,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAU,CAAC;AAC7F,eAAO,MAAM,qBAAqB,sEAAuC,CAAC;AAC1E,eAAO,MAAM,sBAAsB,YAAI,MAAM,EAAE,OAAO,CAAU,CAAC;AACjE,eAAO,MAAM,gBAAgB,YAAI,MAAM,EAAE,OAAO,EAAE,MAAM,CAAU,CAAC;AACnE,eAAO,MAAM,6BAA6B,YAAI,OAAO,EAAE,MAAM,CAAU,CAAC;AAExE,eAAO,MAAM,mBAAmB,kEAA6B,CAAC;AAC9D,eAAO,MAAM,wBAAwB,yEAAmC,CAAC;AACzE,eAAO,MAAM,wBAAwB,+BAAoC,CAAC;AAC1E,eAAO,MAAM,oBAAoB,uCAA8B,CAAC;AAGhE,eAAO,MAAM,sBAAsB;;;;;;;;EAAmF,CAAC;AAEvH,eAAO,MAAM,sBAAsB;;;EAMlC,CAAC;AASF,eAAO,MAAM,oBAAoB;;;;;EAAwC,CAAC;AAC1E,eAAO,MAAM,mBAAmB;;;;;;EAK9B,CAAC;AACH,eAAO,MAAM,eAAe;;;;;;;;;;;;EAO1B,CAAC;AAgBH,eAAO,MAAM,6BAA6B;;;;;EAA4B,CAAC;AAEvE,eAAO,MAAM,2BAA2B;;;;;;;;;EAYtC,CAAC;AAEH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;EAKjC,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAC/D,MAAM,MAAM,kBAAkB,GAAG,MAAM,CAAC,OAAO,wBAAwB,CAAC,CAAC;AACzE,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,OAAO,sBAAsB,CAAC,CAAC;AACrE,MAAM,MAAM,kBAAkB,GAAG,MAAM,CAAC,OAAO,wBAAwB,CAAC,CAAC;AACzE,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,OAAO,oBAAoB,CAAC,CAAC;AACjE,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,OAAO,sBAAsB,CAAC,CAAC;AACrE,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,OAAO,oBAAoB,CAAC,CAAC;AACjE,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAC/D,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,eAAe,CAAC,CAAC;AACvD,MAAM,MAAM,uBAAuB,GAAG,MAAM,CAAC,OAAO,6BAA6B,CAAC,CAAC;AACnF,MAAM,MAAM,qBAAqB,GAAG,MAAM,CAAC,OAAO,2BAA2B,CAAC,CAAC;AAC/E,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,OAAO,sBAAsB,CAAC,CAAC"}
@@ -0,0 +1,55 @@
1
+ import { Type } from "typebox";
2
+ export const THINKING_LEVELS = ["minimal", "low", "medium", "high", "xhigh", "max"];
3
+ export const MODEL_THINKING_LEVELS = ["off", ...THINKING_LEVELS];
4
+ export const MODEL_INPUT_MODALITIES = ["text", "image"];
5
+ export const CACHE_RETENTIONS = ["none", "short", "long"];
6
+ export const MODEL_PROMPT_CACHE_RETENTIONS = ["short", "long"];
7
+ export const ThinkingLevelSchema = Type.Enum(THINKING_LEVELS);
8
+ export const ModelThinkingLevelSchema = Type.Enum(MODEL_THINKING_LEVELS);
9
+ export const ModelInputModalitySchema = Type.Enum(MODEL_INPUT_MODALITIES);
10
+ export const CacheRetentionSchema = Type.Enum(CACHE_RETENTIONS);
11
+ const ThinkingLevelMapValueSchema = Type.Union([Type.String(), Type.Null()]);
12
+ export const ThinkingLevelMapSchema = Type.Partial(Type.Record(ModelThinkingLevelSchema, ThinkingLevelMapValueSchema));
13
+ export const ModelPromptCacheSchema = Type.Partial(Type.Record(Type.Enum(MODEL_PROMPT_CACHE_RETENTIONS), Type.Number({ exclusiveMinimum: 0 })), {
14
+ description: "Best-effort prompt cache lifetime in seconds for each retention tier. A missing tier means the lifetime is unknown, so Pi does not warm it.",
15
+ });
16
+ const ModelCostRatesProperties = {
17
+ input: Type.Number({ description: "Input cost in USD per million tokens." }),
18
+ output: Type.Number({ description: "Output cost in USD per million tokens." }),
19
+ cacheRead: Type.Number({ description: "Cache-read cost in USD per million tokens." }),
20
+ cacheWrite: Type.Number({ description: "Cache-write cost in USD per million tokens." }),
21
+ };
22
+ export const ModelCostRatesSchema = Type.Object(ModelCostRatesProperties);
23
+ export const ModelCostTierSchema = Type.Object({
24
+ inputTokensAbove: Type.Number({
25
+ description: "Use this tier when total request input exceeds this token count.",
26
+ }),
27
+ ...ModelCostRatesProperties,
28
+ });
29
+ export const ModelCostSchema = Type.Object({
30
+ ...ModelCostRatesProperties,
31
+ tiers: Type.Optional(Type.Array(ModelCostTierSchema, {
32
+ description: "Request-wide pricing tiers. The highest matching input threshold applies to the full request.",
33
+ })),
34
+ });
35
+ function modelImageResizeOptions(options) {
36
+ return Type.Object({
37
+ maxWidth: Type.Optional(Type.Integer({ minimum: 1 })),
38
+ maxHeight: Type.Optional(Type.Integer({ minimum: 1 })),
39
+ maxBytes: Type.Optional(Type.Integer({ minimum: 1, description: "Maximum base64-encoded payload size in bytes." })),
40
+ jpegQuality: Type.Optional(Type.Integer({ minimum: 1, maximum: 100 })),
41
+ }, options);
42
+ }
43
+ export const ModelImageResizeOptionsSchema = modelImageResizeOptions();
44
+ export const ModelImageInputLimitsSchema = Type.Object({
45
+ resize: Type.Optional(modelImageResizeOptions({
46
+ description: "Cache-safe resize profile applied before a new image enters conversation history.",
47
+ })),
48
+ maxPerMessage: Type.Optional(Type.Integer({ minimum: 1, description: "Maximum images accepted in one provider message." })),
49
+ maxPerRequest: Type.Optional(Type.Integer({ minimum: 1, description: "Maximum images accepted across one provider request." })),
50
+ });
51
+ export const ModelInputLimitsSchema = Type.Object({
52
+ maxRequestBytes: Type.Optional(Type.Integer({ minimum: 1, description: "Maximum serialized provider request size in bytes." })),
53
+ images: Type.Optional(ModelImageInputLimitsSchema),
54
+ });
55
+ //# sourceMappingURL=model-schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-schema.js","sourceRoot":"","sources":["../../src/providers/model-schema.ts"],"names":[],"mappings":"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","sourcesContent":["import { type Static, type TSchemaOptions, Type } from \"typebox\";\n\nexport const THINKING_LEVELS = [\"minimal\", \"low\", \"medium\", \"high\", \"xhigh\", \"max\"] as const;\nexport const MODEL_THINKING_LEVELS = [\"off\", ...THINKING_LEVELS] as const;\nexport const MODEL_INPUT_MODALITIES = [\"text\", \"image\"] as const;\nexport const CACHE_RETENTIONS = [\"none\", \"short\", \"long\"] as const;\nexport const MODEL_PROMPT_CACHE_RETENTIONS = [\"short\", \"long\"] as const;\n\nexport const ThinkingLevelSchema = Type.Enum(THINKING_LEVELS);\nexport const ModelThinkingLevelSchema = Type.Enum(MODEL_THINKING_LEVELS);\nexport const ModelInputModalitySchema = Type.Enum(MODEL_INPUT_MODALITIES);\nexport const CacheRetentionSchema = Type.Enum(CACHE_RETENTIONS);\n\nconst ThinkingLevelMapValueSchema = Type.Union([Type.String(), Type.Null()]);\nexport const ThinkingLevelMapSchema = Type.Partial(Type.Record(ModelThinkingLevelSchema, ThinkingLevelMapValueSchema));\n\nexport const ModelPromptCacheSchema = Type.Partial(\n\tType.Record(Type.Enum(MODEL_PROMPT_CACHE_RETENTIONS), Type.Number({ exclusiveMinimum: 0 })),\n\t{\n\t\tdescription:\n\t\t\t\"Best-effort prompt cache lifetime in seconds for each retention tier. A missing tier means the lifetime is unknown, so Pi does not warm it.\",\n\t},\n);\n\nconst ModelCostRatesProperties = {\n\tinput: Type.Number({ description: \"Input cost in USD per million tokens.\" }),\n\toutput: Type.Number({ description: \"Output cost in USD per million tokens.\" }),\n\tcacheRead: Type.Number({ description: \"Cache-read cost in USD per million tokens.\" }),\n\tcacheWrite: Type.Number({ description: \"Cache-write cost in USD per million tokens.\" }),\n};\n\nexport const ModelCostRatesSchema = Type.Object(ModelCostRatesProperties);\nexport const ModelCostTierSchema = Type.Object({\n\tinputTokensAbove: Type.Number({\n\t\tdescription: \"Use this tier when total request input exceeds this token count.\",\n\t}),\n\t...ModelCostRatesProperties,\n});\nexport const ModelCostSchema = Type.Object({\n\t...ModelCostRatesProperties,\n\ttiers: Type.Optional(\n\t\tType.Array(ModelCostTierSchema, {\n\t\t\tdescription: \"Request-wide pricing tiers. The highest matching input threshold applies to the full request.\",\n\t\t}),\n\t),\n});\n\nfunction modelImageResizeOptions(options?: TSchemaOptions) {\n\treturn Type.Object(\n\t\t{\n\t\t\tmaxWidth: Type.Optional(Type.Integer({ minimum: 1 })),\n\t\t\tmaxHeight: Type.Optional(Type.Integer({ minimum: 1 })),\n\t\t\tmaxBytes: Type.Optional(\n\t\t\t\tType.Integer({ minimum: 1, description: \"Maximum base64-encoded payload size in bytes.\" }),\n\t\t\t),\n\t\t\tjpegQuality: Type.Optional(Type.Integer({ minimum: 1, maximum: 100 })),\n\t\t},\n\t\toptions,\n\t);\n}\n\nexport const ModelImageResizeOptionsSchema = modelImageResizeOptions();\n\nexport const ModelImageInputLimitsSchema = Type.Object({\n\tresize: Type.Optional(\n\t\tmodelImageResizeOptions({\n\t\t\tdescription: \"Cache-safe resize profile applied before a new image enters conversation history.\",\n\t\t}),\n\t),\n\tmaxPerMessage: Type.Optional(\n\t\tType.Integer({ minimum: 1, description: \"Maximum images accepted in one provider message.\" }),\n\t),\n\tmaxPerRequest: Type.Optional(\n\t\tType.Integer({ minimum: 1, description: \"Maximum images accepted across one provider request.\" }),\n\t),\n});\n\nexport const ModelInputLimitsSchema = Type.Object({\n\tmaxRequestBytes: Type.Optional(\n\t\tType.Integer({ minimum: 1, description: \"Maximum serialized provider request size in bytes.\" }),\n\t),\n\timages: Type.Optional(ModelImageInputLimitsSchema),\n});\n\nexport type ThinkingLevel = Static<typeof ThinkingLevelSchema>;\nexport type ModelThinkingLevel = Static<typeof ModelThinkingLevelSchema>;\nexport type ThinkingLevelMap = Static<typeof ThinkingLevelMapSchema>;\nexport type ModelInputModality = Static<typeof ModelInputModalitySchema>;\nexport type CacheRetention = Static<typeof CacheRetentionSchema>;\nexport type ModelPromptCache = Static<typeof ModelPromptCacheSchema>;\nexport type ModelCostRates = Static<typeof ModelCostRatesSchema>;\nexport type ModelCostTier = Static<typeof ModelCostTierSchema>;\nexport type ModelCost = Static<typeof ModelCostSchema>;\nexport type ModelImageResizeOptions = Static<typeof ModelImageResizeOptionsSchema>;\nexport type ModelImageInputLimits = Static<typeof ModelImageInputLimitsSchema>;\nexport type ModelInputLimits = Static<typeof ModelInputLimitsSchema>;\n"]}
@@ -0,0 +1,40 @@
1
+ import type { ModelCost } from "../types.ts";
2
+ /** OpenRouter's per-token prices, as decimal strings. */
3
+ export interface OpenRouterPricing {
4
+ prompt?: string;
5
+ completion?: string;
6
+ input_cache_read?: string;
7
+ input_cache_write?: string;
8
+ overrides?: OpenRouterPricingOverride[];
9
+ }
10
+ /**
11
+ * A conditional price. `min_prompt_tokens` selects prompt-length pricing; `utc_*` fields select
12
+ * time-of-day or weekday pricing. Missing rates keep the base price.
13
+ */
14
+ export interface OpenRouterPricingOverride {
15
+ min_prompt_tokens?: number;
16
+ utc_start?: number;
17
+ utc_end?: number;
18
+ utc_days?: string[];
19
+ prompt?: string;
20
+ completion?: string;
21
+ input_cache_read?: string;
22
+ input_cache_write?: string;
23
+ }
24
+ /** The fields pi reads from a model in OpenRouter's `GET /models` and `GET /models/user`. */
25
+ export interface OpenRouterModel {
26
+ id: string;
27
+ context_length?: number | null;
28
+ top_provider?: {
29
+ context_length?: number | null;
30
+ max_completion_tokens?: number | null;
31
+ };
32
+ pricing?: OpenRouterPricing;
33
+ }
34
+ /**
35
+ * Convert OpenRouter's $/token prices to pi's $/1M tokens. Missing, invalid and negative base
36
+ * prices are 0. Prompt-length overrides become request-wide tiers; a rate a tier does not list
37
+ * keeps the base price. Time-of-day overrides are skipped because ModelCost cannot express them.
38
+ */
39
+ export declare function openRouterCost(pricing: OpenRouterPricing | undefined): ModelCost;
40
+ //# sourceMappingURL=openrouter-api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openrouter-api.d.ts","sourceRoot":"","sources":["../../src/providers/openrouter-api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAiB,MAAM,aAAa,CAAC;AAE5D,yDAAyD;AACzD,MAAM,WAAW,iBAAiB;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,SAAS,CAAC,EAAE,yBAAyB,EAAE,CAAC;CACxC;AAED;;;GAGG;AACH,MAAM,WAAW,yBAAyB;IACzC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,6FAA6F;AAC7F,MAAM,WAAW,eAAe;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,YAAY,CAAC,EAAE;QAAE,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,qBAAqB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IACzF,OAAO,CAAC,EAAE,iBAAiB,CAAC;CAC5B;AAYD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,iBAAiB,GAAG,SAAS,GAAG,SAAS,CA2BhF"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Convert one $/token price to $/1M tokens. 0 means free. Missing, invalid and negative prices use
3
+ * the fallback: OpenRouter reports -1 for router models such as `openrouter/auto`, whose price
4
+ * depends on the model they pick.
5
+ */
6
+ function perMillion(value, fallback) {
7
+ const perToken = Number.parseFloat(value ?? "");
8
+ return perToken >= 0 ? Number((perToken * 1_000_000).toFixed(6)) : fallback;
9
+ }
10
+ /**
11
+ * Convert OpenRouter's $/token prices to pi's $/1M tokens. Missing, invalid and negative base
12
+ * prices are 0. Prompt-length overrides become request-wide tiers; a rate a tier does not list
13
+ * keeps the base price. Time-of-day overrides are skipped because ModelCost cannot express them.
14
+ */
15
+ export function openRouterCost(pricing) {
16
+ const base = {
17
+ input: perMillion(pricing?.prompt, 0),
18
+ output: perMillion(pricing?.completion, 0),
19
+ cacheRead: perMillion(pricing?.input_cache_read, 0),
20
+ cacheWrite: perMillion(pricing?.input_cache_write, 0),
21
+ };
22
+ const tiers = (pricing?.overrides ?? []).flatMap((override) => {
23
+ if (override.min_prompt_tokens === undefined ||
24
+ override.utc_start !== undefined ||
25
+ override.utc_end !== undefined ||
26
+ override.utc_days !== undefined) {
27
+ return [];
28
+ }
29
+ return [
30
+ {
31
+ inputTokensAbove: override.min_prompt_tokens,
32
+ input: perMillion(override.prompt, base.input),
33
+ output: perMillion(override.completion, base.output),
34
+ cacheRead: perMillion(override.input_cache_read, base.cacheRead),
35
+ cacheWrite: perMillion(override.input_cache_write, base.cacheWrite),
36
+ },
37
+ ];
38
+ });
39
+ return tiers.length > 0 ? { ...base, tiers } : base;
40
+ }
41
+ //# sourceMappingURL=openrouter-api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openrouter-api.js","sourceRoot":"","sources":["../../src/providers/openrouter-api.ts"],"names":[],"mappings":"AAkCA;;;;GAIG;AACH,SAAS,UAAU,CAAC,KAAyB,EAAE,QAAgB;IAC9D,MAAM,QAAQ,GAAG,MAAM,CAAC,UAAU,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;IAChD,OAAO,QAAQ,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,QAAQ,GAAG,SAAS,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;AAC7E,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,OAAsC;IACpE,MAAM,IAAI,GAAG;QACZ,KAAK,EAAE,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;QACrC,MAAM,EAAE,UAAU,CAAC,OAAO,EAAE,UAAU,EAAE,CAAC,CAAC;QAC1C,SAAS,EAAE,UAAU,CAAC,OAAO,EAAE,gBAAgB,EAAE,CAAC,CAAC;QACnD,UAAU,EAAE,UAAU,CAAC,OAAO,EAAE,iBAAiB,EAAE,CAAC,CAAC;KACrD,CAAC;IACF,MAAM,KAAK,GAAG,CAAC,OAAO,EAAE,SAAS,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAmB,EAAE;QAC9E,IACC,QAAQ,CAAC,iBAAiB,KAAK,SAAS;YACxC,QAAQ,CAAC,SAAS,KAAK,SAAS;YAChC,QAAQ,CAAC,OAAO,KAAK,SAAS;YAC9B,QAAQ,CAAC,QAAQ,KAAK,SAAS,EAC9B,CAAC;YACF,OAAO,EAAE,CAAC;QACX,CAAC;QACD,OAAO;YACN;gBACC,gBAAgB,EAAE,QAAQ,CAAC,iBAAiB;gBAC5C,KAAK,EAAE,UAAU,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC;gBAC9C,MAAM,EAAE,UAAU,CAAC,QAAQ,CAAC,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC;gBACpD,SAAS,EAAE,UAAU,CAAC,QAAQ,CAAC,gBAAgB,EAAE,IAAI,CAAC,SAAS,CAAC;gBAChE,UAAU,EAAE,UAAU,CAAC,QAAQ,CAAC,iBAAiB,EAAE,IAAI,CAAC,UAAU,CAAC;aACnE;SACD,CAAC;IACH,CAAC,CAAC,CAAC;IACH,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AACrD,CAAC","sourcesContent":["import type { ModelCost, ModelCostTier } from \"../types.ts\";\n\n/** OpenRouter's per-token prices, as decimal strings. */\nexport interface OpenRouterPricing {\n\tprompt?: string;\n\tcompletion?: string;\n\tinput_cache_read?: string;\n\tinput_cache_write?: string;\n\toverrides?: OpenRouterPricingOverride[];\n}\n\n/**\n * A conditional price. `min_prompt_tokens` selects prompt-length pricing; `utc_*` fields select\n * time-of-day or weekday pricing. Missing rates keep the base price.\n */\nexport interface OpenRouterPricingOverride {\n\tmin_prompt_tokens?: number;\n\tutc_start?: number;\n\tutc_end?: number;\n\tutc_days?: string[];\n\tprompt?: string;\n\tcompletion?: string;\n\tinput_cache_read?: string;\n\tinput_cache_write?: string;\n}\n\n/** The fields pi reads from a model in OpenRouter's `GET /models` and `GET /models/user`. */\nexport interface OpenRouterModel {\n\tid: string;\n\tcontext_length?: number | null;\n\ttop_provider?: { context_length?: number | null; max_completion_tokens?: number | null };\n\tpricing?: OpenRouterPricing;\n}\n\n/**\n * Convert one $/token price to $/1M tokens. 0 means free. Missing, invalid and negative prices use\n * the fallback: OpenRouter reports -1 for router models such as `openrouter/auto`, whose price\n * depends on the model they pick.\n */\nfunction perMillion(value: string | undefined, fallback: number): number {\n\tconst perToken = Number.parseFloat(value ?? \"\");\n\treturn perToken >= 0 ? Number((perToken * 1_000_000).toFixed(6)) : fallback;\n}\n\n/**\n * Convert OpenRouter's $/token prices to pi's $/1M tokens. Missing, invalid and negative base\n * prices are 0. Prompt-length overrides become request-wide tiers; a rate a tier does not list\n * keeps the base price. Time-of-day overrides are skipped because ModelCost cannot express them.\n */\nexport function openRouterCost(pricing: OpenRouterPricing | undefined): ModelCost {\n\tconst base = {\n\t\tinput: perMillion(pricing?.prompt, 0),\n\t\toutput: perMillion(pricing?.completion, 0),\n\t\tcacheRead: perMillion(pricing?.input_cache_read, 0),\n\t\tcacheWrite: perMillion(pricing?.input_cache_write, 0),\n\t};\n\tconst tiers = (pricing?.overrides ?? []).flatMap((override): ModelCostTier[] => {\n\t\tif (\n\t\t\toverride.min_prompt_tokens === undefined ||\n\t\t\toverride.utc_start !== undefined ||\n\t\t\toverride.utc_end !== undefined ||\n\t\t\toverride.utc_days !== undefined\n\t\t) {\n\t\t\treturn [];\n\t\t}\n\t\treturn [\n\t\t\t{\n\t\t\t\tinputTokensAbove: override.min_prompt_tokens,\n\t\t\t\tinput: perMillion(override.prompt, base.input),\n\t\t\t\toutput: perMillion(override.completion, base.output),\n\t\t\t\tcacheRead: perMillion(override.input_cache_read, base.cacheRead),\n\t\t\t\tcacheWrite: perMillion(override.input_cache_write, base.cacheWrite),\n\t\t\t},\n\t\t];\n\t});\n\treturn tiers.length > 0 ? { ...base, tiers } : base;\n}\n"]}
@@ -1,3 +1,13 @@
1
- import { type Provider } from "../models.ts";
1
+ import { type Provider, type RefreshModelsContext } from "../models.ts";
2
+ import type { AnyModel } from "../types.ts";
3
+ /**
4
+ * Restrict an OpenRouter catalog to the chat models the key may use, with the key's limits and
5
+ * prices from `GET /models/user`. Image and classifier models are returned unchanged. A missing
6
+ * limit keeps the catalog value. The request uses the provider's configured base URL, so regional
7
+ * endpoints such as `https://us.openrouter.ai/api/v1` apply their region's filtering. When the
8
+ * endpoint rejects the key (401/403) or does not exist (404), for example with a proxy configured
9
+ * in models.json, the catalog is returned unchanged.
10
+ */
11
+ export declare function personalizeOpenRouterCatalog(catalog: readonly AnyModel[], context: RefreshModelsContext): Promise<readonly AnyModel[]>;
2
12
  export declare function openrouterProvider(): Provider<"anthropic-messages" | "openai-completions">;
3
13
  //# sourceMappingURL=openrouter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"openrouter.d.ts","sourceRoot":"","sources":["../../src/providers/openrouter.ts"],"names":[],"mappings":"AAMA,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,cAAc,CAAC;AAG7D,wBAAgB,kBAAkB,IAAI,QAAQ,CAAC,oBAAoB,GAAG,oBAAoB,CAAC,CA0B1F"}
1
+ {"version":3,"file":"openrouter.d.ts","sourceRoot":"","sources":["../../src/providers/openrouter.ts"],"names":[],"mappings":"AAMA,OAAO,EAAkB,KAAK,QAAQ,EAAE,KAAK,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACxF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAQ5C;;;;;;;GAOG;AACH,wBAAsB,4BAA4B,CACjD,OAAO,EAAE,SAAS,QAAQ,EAAE,EAC5B,OAAO,EAAE,oBAAoB,GAC3B,OAAO,CAAC,SAAS,QAAQ,EAAE,CAAC,CAiC9B;AAED,wBAAgB,kBAAkB,IAAI,QAAQ,CAAC,oBAAoB,GAAG,oBAAoB,CAAC,CA0B1F"}
@@ -5,12 +5,63 @@ import { typesafeSystemOneApi } from "../api/typesafe-system-one.lazy.js";
5
5
  import { envApiKeyAuth, lazyOAuth } from "../auth/helpers.js";
6
6
  import { loadOpenRouterOAuth } from "../auth/oauth/load.js";
7
7
  import { createProvider } from "../models.js";
8
+ import { isModelType } from "../utils/model-operations.js";
8
9
  import { OPENROUTER_CLASSIFIER_MODELS, OPENROUTER_IMAGE_MODELS, OPENROUTER_MODELS } from "./openrouter.models.js";
10
+ import { openRouterCost } from "./openrouter-api.js";
11
+ const OPENROUTER_BASE_URL = "https://openrouter.ai/api/v1";
12
+ const OPENROUTER_USER_MODELS_TIMEOUT_MS = 15_000;
13
+ /**
14
+ * Restrict an OpenRouter catalog to the chat models the key may use, with the key's limits and
15
+ * prices from `GET /models/user`. Image and classifier models are returned unchanged. A missing
16
+ * limit keeps the catalog value. The request uses the provider's configured base URL, so regional
17
+ * endpoints such as `https://us.openrouter.ai/api/v1` apply their region's filtering. When the
18
+ * endpoint rejects the key (401/403) or does not exist (404), for example with a proxy configured
19
+ * in models.json, the catalog is returned unchanged.
20
+ */
21
+ export async function personalizeOpenRouterCatalog(catalog, context) {
22
+ const key = context.credential?.type === "oauth" ? context.credential.access : context.credential?.key;
23
+ if (!key)
24
+ return catalog;
25
+ const response = await fetch(`${(context.baseUrl ?? OPENROUTER_BASE_URL).replace(/\/+$/, "")}/models/user`, {
26
+ headers: { accept: "application/json", authorization: `Bearer ${key}` },
27
+ signal: AbortSignal.any([context.signal, AbortSignal.timeout(OPENROUTER_USER_MODELS_TIMEOUT_MS)]),
28
+ });
29
+ if (response.status === 401 || response.status === 403 || response.status === 404)
30
+ return catalog;
31
+ if (!response.ok)
32
+ throw new Error(`OpenRouter model list request failed: ${response.status}`);
33
+ const body = await response.json();
34
+ if (typeof body !== "object" || body === null || !("data" in body) || !Array.isArray(body.data)) {
35
+ throw new Error("Invalid OpenRouter model list response");
36
+ }
37
+ const allowed = new Map();
38
+ for (const entry of body.data) {
39
+ if (typeof entry === "object" && entry !== null && "id" in entry && typeof entry.id === "string") {
40
+ allowed.set(entry.id, entry);
41
+ }
42
+ }
43
+ return catalog.flatMap((model) => {
44
+ if (!isModelType(model, "chat"))
45
+ return [model];
46
+ const entry = allowed.get(model.id);
47
+ if (!entry)
48
+ return [];
49
+ return [
50
+ {
51
+ ...model,
52
+ // `||`: OpenRouter reports a missing limit as absent, null or 0, like the generator's fallback.
53
+ contextWindow: entry.top_provider?.context_length || entry.context_length || model.contextWindow,
54
+ maxTokens: entry.top_provider?.max_completion_tokens || model.maxTokens,
55
+ cost: entry.pricing ? openRouterCost(entry.pricing) : model.cost,
56
+ },
57
+ ];
58
+ });
59
+ }
9
60
  export function openrouterProvider() {
10
61
  return createProvider({
11
62
  id: "openrouter",
12
63
  name: "OpenRouter",
13
- baseUrl: "https://openrouter.ai/api/v1",
64
+ baseUrl: OPENROUTER_BASE_URL,
14
65
  auth: {
15
66
  apiKey: envApiKeyAuth("OpenRouter API key", ["OPENROUTER_API_KEY"]),
16
67
  oauth: lazyOAuth({
@@ -1 +1 @@
1
- {"version":3,"file":"openrouter.js","sourceRoot":"","sources":["../../src/providers/openrouter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AACzE,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AACzE,OAAO,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AACvE,OAAO,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AAC1E,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAiB,MAAM,cAAc,CAAC;AAC7D,OAAO,EAAE,4BAA4B,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAElH,MAAM,UAAU,kBAAkB;IACjC,OAAO,cAAc,CAA8C;QAClE,EAAE,EAAE,YAAY;QAChB,IAAI,EAAE,YAAY;QAClB,OAAO,EAAE,8BAA8B;QACvC,IAAI,EAAE;YACL,MAAM,EAAE,aAAa,CAAC,oBAAoB,EAAE,CAAC,oBAAoB,CAAC,CAAC;YACnE,KAAK,EAAE,SAAS,CAAC;gBAChB,IAAI,EAAE,kBAAkB;gBACxB,UAAU,EAAE,yBAAyB;gBACrC,IAAI,EAAE,mBAAmB;aACzB,CAAC;SACF;QACD,MAAM,EAAE;YACP,GAAG,MAAM,CAAC,MAAM,CAAC,iBAAiB,CAAC;YACnC,GAAG,MAAM,CAAC,MAAM,CAAC,uBAAuB,CAAC;YACzC,GAAG,MAAM,CAAC,MAAM,CAAC,4BAA4B,CAAC;SAC9C;QACD,GAAG,EAAE;YACJ,oBAAoB,EAAE,oBAAoB,EAAE;YAC5C,oBAAoB,EAAE,oBAAoB,EAAE;SAC5C;QACD,MAAM,EAAE,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,EAAE;QACtD,yEAAyE;QACzE,WAAW,EAAE,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,EAAE;KAC9D,CAAC,CAAC;AACJ,CAAC","sourcesContent":["import { anthropicMessagesApi } from \"../api/anthropic-messages.lazy.ts\";\nimport { openAICompletionsApi } from \"../api/openai-completions.lazy.ts\";\nimport { openrouterImagesApi } from \"../api/openrouter-images.lazy.ts\";\nimport { typesafeSystemOneApi } from \"../api/typesafe-system-one.lazy.ts\";\nimport { envApiKeyAuth, lazyOAuth } from \"../auth/helpers.ts\";\nimport { loadOpenRouterOAuth } from \"../auth/oauth/load.ts\";\nimport { createProvider, type Provider } from \"../models.ts\";\nimport { OPENROUTER_CLASSIFIER_MODELS, OPENROUTER_IMAGE_MODELS, OPENROUTER_MODELS } from \"./openrouter.models.ts\";\n\nexport function openrouterProvider(): Provider<\"anthropic-messages\" | \"openai-completions\"> {\n\treturn createProvider<\"anthropic-messages\" | \"openai-completions\">({\n\t\tid: \"openrouter\",\n\t\tname: \"OpenRouter\",\n\t\tbaseUrl: \"https://openrouter.ai/api/v1\",\n\t\tauth: {\n\t\t\tapiKey: envApiKeyAuth(\"OpenRouter API key\", [\"OPENROUTER_API_KEY\"]),\n\t\t\toauth: lazyOAuth({\n\t\t\t\tname: \"OpenRouter OAuth\",\n\t\t\t\tloginLabel: \"Sign in with OpenRouter\",\n\t\t\t\tload: loadOpenRouterOAuth,\n\t\t\t}),\n\t\t},\n\t\tmodels: [\n\t\t\t...Object.values(OPENROUTER_MODELS),\n\t\t\t...Object.values(OPENROUTER_IMAGE_MODELS),\n\t\t\t...Object.values(OPENROUTER_CLASSIFIER_MODELS),\n\t\t],\n\t\tapi: {\n\t\t\t\"anthropic-messages\": anthropicMessagesApi(),\n\t\t\t\"openai-completions\": openAICompletionsApi(),\n\t\t},\n\t\timages: { \"openrouter-images\": openrouterImagesApi() },\n\t\t// OpenRouter serves TypeSafe's System One protocol at /api/v1/systemone.\n\t\tclassifiers: { \"typesafe-system-one\": typesafeSystemOneApi() },\n\t});\n}\n"]}
1
+ {"version":3,"file":"openrouter.js","sourceRoot":"","sources":["../../src/providers/openrouter.ts"],"names":[],"mappings":"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","sourcesContent":["import { anthropicMessagesApi } from \"../api/anthropic-messages.lazy.ts\";\nimport { openAICompletionsApi } from \"../api/openai-completions.lazy.ts\";\nimport { openrouterImagesApi } from \"../api/openrouter-images.lazy.ts\";\nimport { typesafeSystemOneApi } from \"../api/typesafe-system-one.lazy.ts\";\nimport { envApiKeyAuth, lazyOAuth } from \"../auth/helpers.ts\";\nimport { loadOpenRouterOAuth } from \"../auth/oauth/load.ts\";\nimport { createProvider, type Provider, type RefreshModelsContext } from \"../models.ts\";\nimport type { AnyModel } from \"../types.ts\";\nimport { isModelType } from \"../utils/model-operations.ts\";\nimport { OPENROUTER_CLASSIFIER_MODELS, OPENROUTER_IMAGE_MODELS, OPENROUTER_MODELS } from \"./openrouter.models.ts\";\nimport { type OpenRouterModel, openRouterCost } from \"./openrouter-api.ts\";\n\nconst OPENROUTER_BASE_URL = \"https://openrouter.ai/api/v1\";\nconst OPENROUTER_USER_MODELS_TIMEOUT_MS = 15_000;\n\n/**\n * Restrict an OpenRouter catalog to the chat models the key may use, with the key's limits and\n * prices from `GET /models/user`. Image and classifier models are returned unchanged. A missing\n * limit keeps the catalog value. The request uses the provider's configured base URL, so regional\n * endpoints such as `https://us.openrouter.ai/api/v1` apply their region's filtering. When the\n * endpoint rejects the key (401/403) or does not exist (404), for example with a proxy configured\n * in models.json, the catalog is returned unchanged.\n */\nexport async function personalizeOpenRouterCatalog(\n\tcatalog: readonly AnyModel[],\n\tcontext: RefreshModelsContext,\n): Promise<readonly AnyModel[]> {\n\tconst key = context.credential?.type === \"oauth\" ? context.credential.access : context.credential?.key;\n\tif (!key) return catalog;\n\tconst response = await fetch(`${(context.baseUrl ?? OPENROUTER_BASE_URL).replace(/\\/+$/, \"\")}/models/user`, {\n\t\theaders: { accept: \"application/json\", authorization: `Bearer ${key}` },\n\t\tsignal: AbortSignal.any([context.signal, AbortSignal.timeout(OPENROUTER_USER_MODELS_TIMEOUT_MS)]),\n\t});\n\tif (response.status === 401 || response.status === 403 || response.status === 404) return catalog;\n\tif (!response.ok) throw new Error(`OpenRouter model list request failed: ${response.status}`);\n\tconst body: unknown = await response.json();\n\tif (typeof body !== \"object\" || body === null || !(\"data\" in body) || !Array.isArray(body.data)) {\n\t\tthrow new Error(\"Invalid OpenRouter model list response\");\n\t}\n\tconst allowed = new Map<string, OpenRouterModel>();\n\tfor (const entry of body.data as unknown[]) {\n\t\tif (typeof entry === \"object\" && entry !== null && \"id\" in entry && typeof entry.id === \"string\") {\n\t\t\tallowed.set(entry.id, entry as OpenRouterModel);\n\t\t}\n\t}\n\treturn catalog.flatMap((model): AnyModel[] => {\n\t\tif (!isModelType(model, \"chat\")) return [model];\n\t\tconst entry = allowed.get(model.id);\n\t\tif (!entry) return [];\n\t\treturn [\n\t\t\t{\n\t\t\t\t...model,\n\t\t\t\t// `||`: OpenRouter reports a missing limit as absent, null or 0, like the generator's fallback.\n\t\t\t\tcontextWindow: entry.top_provider?.context_length || entry.context_length || model.contextWindow,\n\t\t\t\tmaxTokens: entry.top_provider?.max_completion_tokens || model.maxTokens,\n\t\t\t\tcost: entry.pricing ? openRouterCost(entry.pricing) : model.cost,\n\t\t\t},\n\t\t];\n\t});\n}\n\nexport function openrouterProvider(): Provider<\"anthropic-messages\" | \"openai-completions\"> {\n\treturn createProvider<\"anthropic-messages\" | \"openai-completions\">({\n\t\tid: \"openrouter\",\n\t\tname: \"OpenRouter\",\n\t\tbaseUrl: OPENROUTER_BASE_URL,\n\t\tauth: {\n\t\t\tapiKey: envApiKeyAuth(\"OpenRouter API key\", [\"OPENROUTER_API_KEY\"]),\n\t\t\toauth: lazyOAuth({\n\t\t\t\tname: \"OpenRouter OAuth\",\n\t\t\t\tloginLabel: \"Sign in with OpenRouter\",\n\t\t\t\tload: loadOpenRouterOAuth,\n\t\t\t}),\n\t\t},\n\t\tmodels: [\n\t\t\t...Object.values(OPENROUTER_MODELS),\n\t\t\t...Object.values(OPENROUTER_IMAGE_MODELS),\n\t\t\t...Object.values(OPENROUTER_CLASSIFIER_MODELS),\n\t\t],\n\t\tapi: {\n\t\t\t\"anthropic-messages\": anthropicMessagesApi(),\n\t\t\t\"openai-completions\": openAICompletionsApi(),\n\t\t},\n\t\timages: { \"openrouter-images\": openrouterImagesApi() },\n\t\t// OpenRouter serves TypeSafe's System One protocol at /api/v1/systemone.\n\t\tclassifiers: { \"typesafe-system-one\": typesafeSystemOneApi() },\n\t});\n}\n"]}