@funkai/models 0.1.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 (213) hide show
  1. package/.generated/entries.json +23 -0
  2. package/.generated/req.txt +1 -0
  3. package/.turbo/turbo-build.log +145 -0
  4. package/.turbo/turbo-typecheck.log +4 -0
  5. package/CHANGELOG.md +23 -0
  6. package/README.md +95 -0
  7. package/dist/alibaba-B6q4Ng1R.mjs +957 -0
  8. package/dist/alibaba-B6q4Ng1R.mjs.map +1 -0
  9. package/dist/amazon-bedrock-Cv9AHQBH.mjs +2070 -0
  10. package/dist/amazon-bedrock-Cv9AHQBH.mjs.map +1 -0
  11. package/dist/anthropic-yB7ST97_.mjs +651 -0
  12. package/dist/anthropic-yB7ST97_.mjs.map +1 -0
  13. package/dist/cerebras-COfl7XM-.mjs +95 -0
  14. package/dist/cerebras-COfl7XM-.mjs.map +1 -0
  15. package/dist/cohere-B7TgO0hT.mjs +271 -0
  16. package/dist/cohere-B7TgO0hT.mjs.map +1 -0
  17. package/dist/deepinfra-B0GxUwCG.mjs +636 -0
  18. package/dist/deepinfra-B0GxUwCG.mjs.map +1 -0
  19. package/dist/deepseek-D64ZEsvS.mjs +50 -0
  20. package/dist/deepseek-D64ZEsvS.mjs.map +1 -0
  21. package/dist/fireworks-ai-DJYvdAi_.mjs +304 -0
  22. package/dist/fireworks-ai-DJYvdAi_.mjs.map +1 -0
  23. package/dist/google-BypRl349.mjs +833 -0
  24. package/dist/google-BypRl349.mjs.map +1 -0
  25. package/dist/google-vertex-DbS-zTGD.mjs +730 -0
  26. package/dist/google-vertex-DbS-zTGD.mjs.map +1 -0
  27. package/dist/groq-ei_PerYi.mjs +381 -0
  28. package/dist/groq-ei_PerYi.mjs.map +1 -0
  29. package/dist/huggingface-DaM1EeLP.mjs +456 -0
  30. package/dist/huggingface-DaM1EeLP.mjs.map +1 -0
  31. package/dist/inception-CspEzqNV.mjs +101 -0
  32. package/dist/inception-CspEzqNV.mjs.map +1 -0
  33. package/dist/index.d.mts +30314 -0
  34. package/dist/index.d.mts.map +1 -0
  35. package/dist/index.mjs +271 -0
  36. package/dist/index.mjs.map +1 -0
  37. package/dist/llama-Cf3-koap.mjs +161 -0
  38. package/dist/llama-Cf3-koap.mjs.map +1 -0
  39. package/dist/mistral-BI9MdAO4.mjs +579 -0
  40. package/dist/mistral-BI9MdAO4.mjs.map +1 -0
  41. package/dist/nvidia-COHacuoa.mjs +1625 -0
  42. package/dist/nvidia-COHacuoa.mjs.map +1 -0
  43. package/dist/openai-C0nCfZUq.mjs +1023 -0
  44. package/dist/openai-C0nCfZUq.mjs.map +1 -0
  45. package/dist/openrouter-DSFzxKQb.mjs +4608 -0
  46. package/dist/openrouter-DSFzxKQb.mjs.map +1 -0
  47. package/dist/perplexity-zeZ2WlBU.mjs +96 -0
  48. package/dist/perplexity-zeZ2WlBU.mjs.map +1 -0
  49. package/dist/providers/alibaba.d.mts +1795 -0
  50. package/dist/providers/alibaba.d.mts.map +1 -0
  51. package/dist/providers/alibaba.mjs +39 -0
  52. package/dist/providers/alibaba.mjs.map +1 -0
  53. package/dist/providers/amazon-bedrock.d.mts +3713 -0
  54. package/dist/providers/amazon-bedrock.d.mts.map +1 -0
  55. package/dist/providers/amazon-bedrock.mjs +39 -0
  56. package/dist/providers/amazon-bedrock.mjs.map +1 -0
  57. package/dist/providers/anthropic.d.mts +1109 -0
  58. package/dist/providers/anthropic.d.mts.map +1 -0
  59. package/dist/providers/anthropic.mjs +39 -0
  60. package/dist/providers/anthropic.mjs.map +1 -0
  61. package/dist/providers/cerebras.d.mts +219 -0
  62. package/dist/providers/cerebras.d.mts.map +1 -0
  63. package/dist/providers/cerebras.mjs +39 -0
  64. package/dist/providers/cerebras.mjs.map +1 -0
  65. package/dist/providers/cohere.d.mts +555 -0
  66. package/dist/providers/cohere.d.mts.map +1 -0
  67. package/dist/providers/cohere.mjs +39 -0
  68. package/dist/providers/cohere.mjs.map +1 -0
  69. package/dist/providers/deepinfra.d.mts +1245 -0
  70. package/dist/providers/deepinfra.d.mts.map +1 -0
  71. package/dist/providers/deepinfra.mjs +39 -0
  72. package/dist/providers/deepinfra.mjs.map +1 -0
  73. package/dist/providers/deepseek.d.mts +139 -0
  74. package/dist/providers/deepseek.d.mts.map +1 -0
  75. package/dist/providers/deepseek.mjs +39 -0
  76. package/dist/providers/deepseek.mjs.map +1 -0
  77. package/dist/providers/fireworks-ai.d.mts +611 -0
  78. package/dist/providers/fireworks-ai.d.mts.map +1 -0
  79. package/dist/providers/fireworks-ai.mjs +39 -0
  80. package/dist/providers/fireworks-ai.mjs.map +1 -0
  81. package/dist/providers/google-vertex.d.mts +1227 -0
  82. package/dist/providers/google-vertex.d.mts.map +1 -0
  83. package/dist/providers/google-vertex.mjs +39 -0
  84. package/dist/providers/google-vertex.mjs.map +1 -0
  85. package/dist/providers/google.d.mts +1359 -0
  86. package/dist/providers/google.d.mts.map +1 -0
  87. package/dist/providers/google.mjs +39 -0
  88. package/dist/providers/google.mjs.map +1 -0
  89. package/dist/providers/groq.d.mts +765 -0
  90. package/dist/providers/groq.d.mts.map +1 -0
  91. package/dist/providers/groq.mjs +39 -0
  92. package/dist/providers/groq.mjs.map +1 -0
  93. package/dist/providers/huggingface.d.mts +901 -0
  94. package/dist/providers/huggingface.d.mts.map +1 -0
  95. package/dist/providers/huggingface.mjs +39 -0
  96. package/dist/providers/huggingface.mjs.map +1 -0
  97. package/dist/providers/inception.d.mts +231 -0
  98. package/dist/providers/inception.d.mts.map +1 -0
  99. package/dist/providers/inception.mjs +39 -0
  100. package/dist/providers/inception.mjs.map +1 -0
  101. package/dist/providers/llama.d.mts +345 -0
  102. package/dist/providers/llama.d.mts.map +1 -0
  103. package/dist/providers/llama.mjs +39 -0
  104. package/dist/providers/llama.mjs.map +1 -0
  105. package/dist/providers/mistral.d.mts +1143 -0
  106. package/dist/providers/mistral.d.mts.map +1 -0
  107. package/dist/providers/mistral.mjs +39 -0
  108. package/dist/providers/mistral.mjs.map +1 -0
  109. package/dist/providers/nvidia.d.mts +3117 -0
  110. package/dist/providers/nvidia.d.mts.map +1 -0
  111. package/dist/providers/nvidia.mjs +39 -0
  112. package/dist/providers/nvidia.mjs.map +1 -0
  113. package/dist/providers/openai.d.mts +1963 -0
  114. package/dist/providers/openai.d.mts.map +1 -0
  115. package/dist/providers/openai.mjs +39 -0
  116. package/dist/providers/openai.mjs.map +1 -0
  117. package/dist/providers/openrouter.d.mts +8531 -0
  118. package/dist/providers/openrouter.d.mts.map +1 -0
  119. package/dist/providers/openrouter.mjs +39 -0
  120. package/dist/providers/openrouter.mjs.map +1 -0
  121. package/dist/providers/perplexity.d.mts +221 -0
  122. package/dist/providers/perplexity.d.mts.map +1 -0
  123. package/dist/providers/perplexity.mjs +39 -0
  124. package/dist/providers/perplexity.mjs.map +1 -0
  125. package/dist/providers/togetherai.d.mts +767 -0
  126. package/dist/providers/togetherai.d.mts.map +1 -0
  127. package/dist/providers/togetherai.mjs +39 -0
  128. package/dist/providers/togetherai.mjs.map +1 -0
  129. package/dist/providers/xai.d.mts +1161 -0
  130. package/dist/providers/xai.d.mts.map +1 -0
  131. package/dist/providers/xai.mjs +39 -0
  132. package/dist/providers/xai.mjs.map +1 -0
  133. package/dist/togetherai-BvcxUfPE.mjs +382 -0
  134. package/dist/togetherai-BvcxUfPE.mjs.map +1 -0
  135. package/dist/types-DjdaZckF.d.mts +71 -0
  136. package/dist/types-DjdaZckF.d.mts.map +1 -0
  137. package/dist/xai-fSuAkQJo.mjs +587 -0
  138. package/dist/xai-fSuAkQJo.mjs.map +1 -0
  139. package/docs/catalog/filtering.md +102 -0
  140. package/docs/catalog/overview.md +168 -0
  141. package/docs/catalog/providers.md +73 -0
  142. package/docs/cost/overview.md +125 -0
  143. package/docs/guides/filter-models.md +113 -0
  144. package/docs/guides/setup-resolver.md +106 -0
  145. package/docs/guides/track-costs.md +133 -0
  146. package/docs/overview.md +139 -0
  147. package/docs/provider/configuration.md +100 -0
  148. package/docs/provider/openrouter.md +105 -0
  149. package/docs/provider/overview.md +131 -0
  150. package/docs/troubleshooting.md +100 -0
  151. package/package.json +142 -0
  152. package/providers.json +39 -0
  153. package/scripts/generate-models.ts +392 -0
  154. package/src/catalog/index.test.ts +124 -0
  155. package/src/catalog/index.ts +65 -0
  156. package/src/catalog/providers/alibaba.ts +468 -0
  157. package/src/catalog/providers/amazon-bedrock.ts +941 -0
  158. package/src/catalog/providers/anthropic.ts +270 -0
  159. package/src/catalog/providers/cerebras.ts +61 -0
  160. package/src/catalog/providers/cohere.ts +149 -0
  161. package/src/catalog/providers/deepinfra.ts +325 -0
  162. package/src/catalog/providers/deepseek.ts +39 -0
  163. package/src/catalog/providers/fireworks-ai.ts +160 -0
  164. package/src/catalog/providers/google-vertex.ts +314 -0
  165. package/src/catalog/providers/google.ts +347 -0
  166. package/src/catalog/providers/groq.ts +204 -0
  167. package/src/catalog/providers/huggingface.ts +237 -0
  168. package/src/catalog/providers/inception.ts +61 -0
  169. package/src/catalog/providers/index.ts +59 -0
  170. package/src/catalog/providers/llama.ts +94 -0
  171. package/src/catalog/providers/mistral.ts +303 -0
  172. package/src/catalog/providers/nvidia.ts +820 -0
  173. package/src/catalog/providers/openai.ts +501 -0
  174. package/src/catalog/providers/openrouter.ts +2201 -0
  175. package/src/catalog/providers/perplexity.ts +61 -0
  176. package/src/catalog/providers/togetherai.ts +204 -0
  177. package/src/catalog/providers/xai.ts +292 -0
  178. package/src/catalog/types.ts +86 -0
  179. package/src/cost/calculate.test.ts +157 -0
  180. package/src/cost/calculate.ts +43 -0
  181. package/src/cost/index.ts +2 -0
  182. package/src/cost/types.ts +25 -0
  183. package/src/index.ts +25 -0
  184. package/src/provider/index.ts +9 -0
  185. package/src/provider/openrouter.test.ts +125 -0
  186. package/src/provider/openrouter.ts +110 -0
  187. package/src/provider/resolver.test.ts +138 -0
  188. package/src/provider/resolver.ts +125 -0
  189. package/src/provider/types.ts +39 -0
  190. package/src/providers/alibaba.ts +65 -0
  191. package/src/providers/amazon-bedrock.ts +67 -0
  192. package/src/providers/anthropic.ts +65 -0
  193. package/src/providers/cerebras.ts +65 -0
  194. package/src/providers/cohere.ts +65 -0
  195. package/src/providers/deepinfra.ts +65 -0
  196. package/src/providers/deepseek.ts +65 -0
  197. package/src/providers/fireworks-ai.ts +65 -0
  198. package/src/providers/google-vertex.ts +67 -0
  199. package/src/providers/google.ts +65 -0
  200. package/src/providers/groq.ts +65 -0
  201. package/src/providers/huggingface.ts +67 -0
  202. package/src/providers/inception.ts +65 -0
  203. package/src/providers/llama.ts +65 -0
  204. package/src/providers/mistral.ts +65 -0
  205. package/src/providers/nvidia.ts +65 -0
  206. package/src/providers/openai.ts +65 -0
  207. package/src/providers/openrouter.ts +67 -0
  208. package/src/providers/perplexity.ts +67 -0
  209. package/src/providers/togetherai.ts +65 -0
  210. package/src/providers/xai.ts +65 -0
  211. package/tsconfig.json +25 -0
  212. package/tsdown.config.ts +23 -0
  213. package/vitest.config.ts +29 -0
@@ -0,0 +1,133 @@
1
+ # Track Costs
2
+
3
+ Calculate and accumulate token costs from model invocations using `calculateCost()`.
4
+
5
+ ## Prerequisites
6
+
7
+ - `@funkai/models` installed
8
+ - Token usage data from model invocations (e.g. from `@funkai/agents` execution traces)
9
+
10
+ ## Steps
11
+
12
+ ### 1. Import Cost Functions
13
+
14
+ ```ts
15
+ import { calculateCost, model } from "@funkai/models";
16
+ import type { TokenUsage } from "@funkai/models";
17
+ ```
18
+
19
+ ### 2. Get Model Pricing
20
+
21
+ Look up the model definition to access its pricing:
22
+
23
+ ```ts
24
+ const m = model("openai/gpt-4.1");
25
+ if (!m) {
26
+ throw new Error("Model not found in catalog");
27
+ }
28
+ ```
29
+
30
+ ### 3. Calculate Cost for a Single Invocation
31
+
32
+ ```ts
33
+ const usage: TokenUsage = {
34
+ inputTokens: 1500,
35
+ outputTokens: 800,
36
+ totalTokens: 2300,
37
+ cacheReadTokens: 500,
38
+ cacheWriteTokens: 0,
39
+ reasoningTokens: 0,
40
+ };
41
+
42
+ const cost = calculateCost(usage, m.pricing);
43
+
44
+ console.log(`Input: $${cost.input.toFixed(6)}`);
45
+ console.log(`Output: $${cost.output.toFixed(6)}`);
46
+ console.log(`Total: $${cost.total.toFixed(6)}`);
47
+ ```
48
+
49
+ ### 4. Accumulate Costs Across Multiple Calls
50
+
51
+ ```ts
52
+ const totalCost = runs.reduce((sum, run) => {
53
+ const runModel = model(run.modelId);
54
+ if (!runModel) return sum;
55
+ return sum + calculateCost(run.usage, runModel.pricing).total;
56
+ }, 0);
57
+
58
+ console.log(`Session total: $${totalCost.toFixed(6)}`);
59
+ ```
60
+
61
+ ### 5. Compare Model Costs
62
+
63
+ Estimate the cost of a workload across different models:
64
+
65
+ ```ts
66
+ const usage: TokenUsage = {
67
+ inputTokens: 10_000,
68
+ outputTokens: 2_000,
69
+ totalTokens: 12_000,
70
+ cacheReadTokens: 0,
71
+ cacheWriteTokens: 0,
72
+ reasoningTokens: 0,
73
+ };
74
+
75
+ const candidates = ["openai/gpt-4.1", "anthropic/claude-sonnet-4"] as const;
76
+
77
+ for (const id of candidates) {
78
+ const m = model(id);
79
+ if (!m) continue;
80
+ const cost = calculateCost(usage, m.pricing);
81
+ console.log(`${id}: $${cost.total.toFixed(6)}`);
82
+ }
83
+ ```
84
+
85
+ ## Verification
86
+
87
+ Verify cost calculation with known values:
88
+
89
+ ```ts
90
+ const usage: TokenUsage = {
91
+ inputTokens: 1_000_000,
92
+ outputTokens: 0,
93
+ totalTokens: 1_000_000,
94
+ cacheReadTokens: 0,
95
+ cacheWriteTokens: 0,
96
+ reasoningTokens: 0,
97
+ };
98
+
99
+ const m = model("openai/gpt-4.1");
100
+ if (m) {
101
+ const cost = calculateCost(usage, m.pricing);
102
+ console.log(`1M input tokens: $${cost.total.toFixed(4)}`);
103
+ }
104
+ ```
105
+
106
+ ## Troubleshooting
107
+
108
+ ### Cost is 0 for all fields
109
+
110
+ **Issue:** Token counts are all zero or the pricing rates are zero.
111
+
112
+ **Fix:** Verify the `TokenUsage` object has non-zero values and the model exists in the catalog:
113
+
114
+ ```ts
115
+ console.log(usage);
116
+ console.log(m.pricing);
117
+ ```
118
+
119
+ ### Cache costs are always 0
120
+
121
+ **Issue:** The model's pricing does not include `cacheRead` or `cacheWrite` rates.
122
+
123
+ **Fix:** Not all models support prompt caching. Check whether the model's pricing includes cache fields:
124
+
125
+ ```ts
126
+ console.log(m.pricing.cacheRead);
127
+ console.log(m.pricing.cacheWrite);
128
+ ```
129
+
130
+ ## References
131
+
132
+ - [Cost Calculation](../cost/overview.md)
133
+ - [Model Catalog](../catalog/overview.md)
@@ -0,0 +1,139 @@
1
+ # Models
2
+
3
+ `@funkai/models` provides a generated model catalog, configurable provider resolution, and token cost calculations for the funkai AI SDK.
4
+
5
+ ## Architecture
6
+
7
+ ```mermaid
8
+ %%{init: {
9
+ 'theme': 'base',
10
+ 'themeVariables': {
11
+ 'primaryColor': '#313244',
12
+ 'primaryTextColor': '#cdd6f4',
13
+ 'primaryBorderColor': '#6c7086',
14
+ 'lineColor': '#89b4fa',
15
+ 'secondaryColor': '#45475a',
16
+ 'tertiaryColor': '#1e1e2e',
17
+ 'background': '#1e1e2e',
18
+ 'mainBkg': '#313244',
19
+ 'clusterBkg': '#1e1e2e',
20
+ 'clusterBorder': '#45475a'
21
+ },
22
+ 'flowchart': { 'curve': 'basis', 'padding': 15 }
23
+ }}%%
24
+
25
+ flowchart LR
26
+ ModelId["Model ID"]:::external
27
+
28
+ subgraph catalog [" "]
29
+ direction TB
30
+ MODELS["MODELS catalog"]:::core
31
+ lookup["model() / models()"]:::core
32
+ filter["Filter predicates"]:::core
33
+ end
34
+
35
+ subgraph resolver [" "]
36
+ direction TB
37
+ createResolver["createModelResolver()"]:::core
38
+ providers["Provider map"]:::gateway
39
+ fallback["Fallback (OpenRouter)"]:::gateway
40
+ end
41
+
42
+ subgraph cost [" "]
43
+ direction TB
44
+ calcCost["calculateCost()"]:::agent
45
+ usage["TokenUsage"]:::agent
46
+ pricing["ModelPricing"]:::agent
47
+ end
48
+
49
+ ModelId --> lookup
50
+ MODELS --> lookup
51
+ lookup --> filter
52
+ ModelId --> createResolver
53
+ createResolver --> providers
54
+ createResolver --> fallback
55
+ providers --> LanguageModel["LanguageModel"]:::external
56
+ fallback --> LanguageModel
57
+ usage --> calcCost
58
+ pricing --> calcCost
59
+ calcCost --> UsageCost["UsageCost"]:::external
60
+
61
+ classDef external fill:#313244,stroke:#f5c2e7,stroke-width:2px,color:#cdd6f4
62
+ classDef core fill:#313244,stroke:#89b4fa,stroke-width:2px,color:#cdd6f4
63
+ classDef gateway fill:#313244,stroke:#fab387,stroke-width:2px,color:#cdd6f4
64
+ classDef agent fill:#313244,stroke:#a6e3a1,stroke-width:2px,color:#cdd6f4
65
+
66
+ style catalog fill:#181825,stroke:#89b4fa,stroke-width:2px
67
+ style resolver fill:#181825,stroke:#fab387,stroke-width:2px
68
+ style cost fill:#181825,stroke:#a6e3a1,stroke-width:2px
69
+ ```
70
+
71
+ The package has three domains:
72
+
73
+ | Domain | Purpose | Key Exports |
74
+ | ------------ | -------------------------------------------- | ------------------------------------- |
75
+ | **Catalog** | Generated model metadata from models.dev | `model()`, `models()`, `MODELS` |
76
+ | **Provider** | Resolve model IDs to AI SDK `LanguageModel`s | `createModelResolver()`, `openrouter` |
77
+ | **Cost** | Calculate USD costs from token usage | `calculateCost()` |
78
+
79
+ ## Key Concepts
80
+
81
+ ### Model Definitions
82
+
83
+ Every model in the catalog is a `ModelDefinition` with pricing, capabilities, modalities, and context window metadata. The catalog is auto-generated from [models.dev](https://models.dev) and updated via `pnpm --filter=@funkai/models generate:models`.
84
+
85
+ ### Provider Resolution
86
+
87
+ `createModelResolver()` maps model ID prefixes (e.g. `"openai"` from `"openai/gpt-4.1"`) to AI SDK provider factories. Unmapped prefixes fall through to an optional fallback (typically OpenRouter).
88
+
89
+ ### Cost Calculation
90
+
91
+ `calculateCost()` multiplies token counts by per-token pricing rates. Pricing is stored per-token in the catalog (converted from per-million at generation time), so no runtime conversion is needed.
92
+
93
+ ## Usage
94
+
95
+ ### Look Up a Model
96
+
97
+ ```ts
98
+ const m = model("openai/gpt-4.1");
99
+ if (m) {
100
+ console.log(m.name, m.contextWindow, m.capabilities.reasoning);
101
+ }
102
+ ```
103
+
104
+ ### Filter Models
105
+
106
+ ```ts
107
+ const reasoning = models((m) => m.capabilities.reasoning);
108
+ const multimodal = models((m) => m.modalities.input.includes("image"));
109
+ ```
110
+
111
+ ### Resolve a Model
112
+
113
+ ```ts
114
+ const resolve = createModelResolver({
115
+ fallback: openrouter,
116
+ });
117
+ const lm = resolve("openai/gpt-4.1");
118
+ ```
119
+
120
+ ### Calculate Cost
121
+
122
+ ```ts
123
+ const cost = calculateCost(usage, m.pricing);
124
+ console.log(`Total: $${cost.total.toFixed(6)}`);
125
+ ```
126
+
127
+ ## References
128
+
129
+ - [Model Catalog](catalog/overview.md)
130
+ - [Filtering](catalog/filtering.md)
131
+ - [Providers](catalog/providers.md)
132
+ - [Provider Resolution](provider/overview.md)
133
+ - [Configuration](provider/configuration.md)
134
+ - [OpenRouter](provider/openrouter.md)
135
+ - [Cost Calculation](cost/overview.md)
136
+ - [Setup Resolver Guide](guides/setup-resolver.md)
137
+ - [Filter Models Guide](guides/filter-models.md)
138
+ - [Track Costs Guide](guides/track-costs.md)
139
+ - [Troubleshooting](troubleshooting.md)
@@ -0,0 +1,100 @@
1
+ # Provider Configuration
2
+
3
+ Configuration options for `createModelResolver()` and how to set up provider mappings.
4
+
5
+ ## Key Concepts
6
+
7
+ ### ModelResolverConfig
8
+
9
+ | Option | Type | Default | Description |
10
+ | ----------- | ------------------------------------ | ----------- | ----------------------------------------- |
11
+ | `providers` | `ProviderMap` | `{}` | Direct AI SDK provider mappings by prefix |
12
+ | `fallback` | `(modelId: string) => LanguageModel` | `undefined` | Fallback factory for unmapped prefixes |
13
+
14
+ Both fields are optional. A resolver with no configuration throws on every call.
15
+
16
+ ### ProviderMap
17
+
18
+ `ProviderMap` is `Readonly<Record<string, ProviderFactory>>`. Keys are provider prefixes that match the portion before `/` in a model ID.
19
+
20
+ ```ts
21
+ const providers: ProviderMap = {
22
+ openai: createOpenAI({ apiKey: process.env.OPENAI_API_KEY }),
23
+ anthropic: createAnthropic({ apiKey: process.env.ANTHROPIC_API_KEY }),
24
+ };
25
+ ```
26
+
27
+ ### ProviderFactory
28
+
29
+ `ProviderFactory` is `(modelName: string) => LanguageModel`. AI SDK provider constructors (`createOpenAI`, `createAnthropic`, etc.) return compatible factory functions.
30
+
31
+ ## Configuration Patterns
32
+
33
+ ### Direct Providers Only
34
+
35
+ Map each provider explicitly. Unmapped prefixes throw an error:
36
+
37
+ ```ts
38
+ const resolve = createModelResolver({
39
+ providers: {
40
+ openai: createOpenAI({ apiKey: process.env.OPENAI_API_KEY }),
41
+ anthropic: createAnthropic({ apiKey: process.env.ANTHROPIC_API_KEY }),
42
+ google: createGoogleGenerativeAI({ apiKey: process.env.GOOGLE_API_KEY }),
43
+ },
44
+ });
45
+ ```
46
+
47
+ ### Direct Providers with OpenRouter Fallback
48
+
49
+ Map preferred providers directly. Unmapped prefixes route through OpenRouter:
50
+
51
+ ```ts
52
+ const resolve = createModelResolver({
53
+ providers: {
54
+ openai: createOpenAI({ apiKey: process.env.OPENAI_API_KEY }),
55
+ },
56
+ fallback: openrouter,
57
+ });
58
+ ```
59
+
60
+ ### OpenRouter-Only
61
+
62
+ Route all models through OpenRouter:
63
+
64
+ ```ts
65
+ const resolve = createModelResolver({
66
+ fallback: openrouter,
67
+ });
68
+ ```
69
+
70
+ ### Custom Fallback
71
+
72
+ Use any function as a fallback:
73
+
74
+ ```ts
75
+ const resolve = createModelResolver({
76
+ providers: {
77
+ openai: createOpenAI({ apiKey: process.env.OPENAI_API_KEY }),
78
+ },
79
+ fallback: (modelId: string) => {
80
+ const provider = createOpenRouter({ apiKey: process.env.OPENROUTER_API_KEY });
81
+ return provider(modelId);
82
+ },
83
+ });
84
+ ```
85
+
86
+ ## Error Handling
87
+
88
+ `createModelResolver()` throws in these cases:
89
+
90
+ | Condition | Error Message |
91
+ | ---------------------------- | ------------------------------------------------------------------------------------------- |
92
+ | Empty model ID | `Cannot resolve model: model ID is empty` |
93
+ | No prefix, no fallback | `Cannot resolve model "<id>": no provider prefix and no fallback configured` |
94
+ | Unmapped prefix, no fallback | `Cannot resolve model "<id>": no provider mapped for "<prefix>" and no fallback configured` |
95
+
96
+ ## References
97
+
98
+ - [Provider Resolution](overview.md)
99
+ - [OpenRouter](openrouter.md)
100
+ - [Setup Resolver Guide](../guides/setup-resolver.md)
@@ -0,0 +1,105 @@
1
+ # OpenRouter Integration
2
+
3
+ OpenRouter acts as a model aggregator, routing requests to the underlying provider. `@funkai/models` provides two exports for OpenRouter integration: `openrouter` (cached singleton) and `createOpenRouter` (factory).
4
+
5
+ ## Key Concepts
6
+
7
+ ### API Key Resolution
8
+
9
+ Both `openrouter` and `createOpenRouter` resolve the API key in this order:
10
+
11
+ 1. Explicit `apiKey` in options (for `createOpenRouter`)
12
+ 2. `OPENROUTER_API_KEY` environment variable
13
+
14
+ If neither is set, an error is thrown at call time.
15
+
16
+ ### Cached Provider
17
+
18
+ The `openrouter` export is a cached resolver. The underlying provider instance is created once and reused across calls. If `OPENROUTER_API_KEY` changes at runtime, the cache invalidates and a new provider is created.
19
+
20
+ ```ts
21
+ const lm = openrouter("openai/gpt-4.1");
22
+ ```
23
+
24
+ ### Provider Factory
25
+
26
+ `createOpenRouter` creates a new `OpenRouterProvider` instance. Use this when you need multiple providers with different configurations:
27
+
28
+ ```ts
29
+ const provider = createOpenRouter({ apiKey: "sk-..." });
30
+ const lm = provider("openai/gpt-4.1");
31
+ ```
32
+
33
+ ## Usage
34
+
35
+ ### As a Fallback
36
+
37
+ The most common pattern is using `openrouter` as the fallback for `createModelResolver()`:
38
+
39
+ ```ts
40
+ const resolve = createModelResolver({
41
+ providers: {
42
+ openai: createOpenAI({ apiKey: process.env.OPENAI_API_KEY }),
43
+ },
44
+ fallback: openrouter,
45
+ });
46
+ ```
47
+
48
+ Models with an `"openai"` prefix route directly. All other prefixes route through OpenRouter.
49
+
50
+ ### As the Only Provider
51
+
52
+ ```ts
53
+ const resolve = createModelResolver({
54
+ fallback: openrouter,
55
+ });
56
+
57
+ const lm = resolve("anthropic/claude-sonnet-4");
58
+ ```
59
+
60
+ ### Direct Usage
61
+
62
+ Use `openrouter` directly without a resolver:
63
+
64
+ ```ts
65
+ const lm = openrouter("openai/gpt-4.1");
66
+ ```
67
+
68
+ ### Custom Instance
69
+
70
+ ```ts
71
+ const provider = createOpenRouter({
72
+ apiKey: process.env.OPENROUTER_API_KEY,
73
+ });
74
+
75
+ const lm = provider("mistral/mistral-large-latest");
76
+ ```
77
+
78
+ ## Configuration
79
+
80
+ `createOpenRouter` accepts all options from `@openrouter/ai-sdk-provider`:
81
+
82
+ | Option | Type | Default | Description |
83
+ | -------- | -------- | -------------------------------- | ------------------ |
84
+ | `apiKey` | `string` | `process.env.OPENROUTER_API_KEY` | OpenRouter API key |
85
+
86
+ Additional options are forwarded directly to the underlying `@openrouter/ai-sdk-provider`.
87
+
88
+ ## Environment Variables
89
+
90
+ | Variable | Required | Description |
91
+ | -------------------- | -------- | ------------------ |
92
+ | `OPENROUTER_API_KEY` | No\* | OpenRouter API key |
93
+
94
+ \*Required when `apiKey` is not provided to `createOpenRouter(...)`.
95
+
96
+ ## Resources
97
+
98
+ - [OpenRouter Documentation](https://openrouter.ai/docs)
99
+ - [@openrouter/ai-sdk-provider](https://www.npmjs.com/package/@openrouter/ai-sdk-provider)
100
+
101
+ ## References
102
+
103
+ - [Provider Resolution](overview.md)
104
+ - [Configuration](configuration.md)
105
+ - [Setup Resolver Guide](../guides/setup-resolver.md)
@@ -0,0 +1,131 @@
1
+ # Provider Resolution
2
+
3
+ Provider resolution maps model ID strings to AI SDK `LanguageModel` instances. `createModelResolver()` extracts the provider prefix from a model ID and dispatches to the appropriate provider factory.
4
+
5
+ ## Architecture
6
+
7
+ ```mermaid
8
+ %%{init: {
9
+ 'theme': 'base',
10
+ 'themeVariables': {
11
+ 'primaryColor': '#313244',
12
+ 'primaryTextColor': '#cdd6f4',
13
+ 'primaryBorderColor': '#6c7086',
14
+ 'lineColor': '#89b4fa',
15
+ 'secondaryColor': '#45475a',
16
+ 'tertiaryColor': '#1e1e2e',
17
+ 'actorBkg': '#313244',
18
+ 'actorBorder': '#89b4fa',
19
+ 'actorTextColor': '#cdd6f4',
20
+ 'signalColor': '#cdd6f4',
21
+ 'signalTextColor': '#cdd6f4'
22
+ }
23
+ }}%%
24
+ sequenceDiagram
25
+ participant C as Caller
26
+ participant R as ModelResolver
27
+ participant P as ProviderFactory
28
+ participant F as Fallback
29
+
30
+ C->>R: resolve("openai/gpt-4.1")
31
+ R->>R: Extract prefix "openai"
32
+
33
+ alt Provider mapped
34
+ R->>P: factory("gpt-4.1")
35
+ P-->>R: LanguageModel
36
+ else No match, fallback configured
37
+ R->>F: fallback("openai/gpt-4.1")
38
+ F-->>R: LanguageModel
39
+ else No match, no fallback
40
+ R-->>C: Error thrown
41
+ end
42
+
43
+ R-->>C: LanguageModel
44
+ ```
45
+
46
+ ## Key Concepts
47
+
48
+ ### Resolution Algorithm
49
+
50
+ When `resolve("openai/gpt-4.1")` is called:
51
+
52
+ 1. The model ID is validated (non-empty)
53
+ 2. The prefix before the first `/` is extracted (`"openai"`)
54
+ 3. If a provider factory is mapped for that prefix, it receives the model portion (`"gpt-4.1"`)
55
+ 4. If no provider matches, the fallback receives the full ID (if configured)
56
+ 5. If no fallback exists, an error is thrown
57
+
58
+ ### Model IDs Without a Prefix
59
+
60
+ Model IDs without a `/` (e.g. `"gpt-4.1"`) skip provider lookup entirely and go directly to the fallback. If no fallback is configured, an error is thrown.
61
+
62
+ ### ProviderFactory
63
+
64
+ A `ProviderFactory` is a function that takes a model name string and returns a `LanguageModel`. AI SDK provider constructors like `createOpenAI()` return compatible factories:
65
+
66
+ ```ts
67
+ import { createOpenAI } from "@ai-sdk/openai";
68
+
69
+ const factory: ProviderFactory = createOpenAI({ apiKey: "..." });
70
+ const lm = factory("gpt-4.1");
71
+ ```
72
+
73
+ ### ProviderMap
74
+
75
+ A `ProviderMap` is a readonly record mapping prefix strings to `ProviderFactory` functions:
76
+
77
+ ```ts
78
+ const providers: ProviderMap = {
79
+ openai: createOpenAI({ apiKey: "..." }),
80
+ anthropic: createAnthropic({ apiKey: "..." }),
81
+ };
82
+ ```
83
+
84
+ ## Usage
85
+
86
+ ### Basic Resolver
87
+
88
+ ```ts
89
+ const resolve = createModelResolver({
90
+ providers: {
91
+ openai: createOpenAI({ apiKey: process.env.OPENAI_API_KEY }),
92
+ },
93
+ });
94
+
95
+ const lm = resolve("openai/gpt-4.1");
96
+ ```
97
+
98
+ ### Resolver with Fallback
99
+
100
+ ```ts
101
+ const resolve = createModelResolver({
102
+ providers: {
103
+ openai: createOpenAI({ apiKey: process.env.OPENAI_API_KEY }),
104
+ },
105
+ fallback: openrouter,
106
+ });
107
+
108
+ const lm1 = resolve("openai/gpt-4.1");
109
+ const lm2 = resolve("anthropic/claude-sonnet-4");
110
+ ```
111
+
112
+ `lm1` routes through the direct OpenAI provider. `lm2` has no mapped provider for `"anthropic"`, so it falls through to the OpenRouter fallback.
113
+
114
+ ### Fallback-Only Resolver
115
+
116
+ ```ts
117
+ const resolve = createModelResolver({
118
+ fallback: openrouter,
119
+ });
120
+
121
+ const lm = resolve("openai/gpt-4.1");
122
+ ```
123
+
124
+ All models route through OpenRouter regardless of prefix.
125
+
126
+ ## References
127
+
128
+ - [Configuration](configuration.md)
129
+ - [OpenRouter](openrouter.md)
130
+ - [Model Catalog](../catalog/overview.md)
131
+ - [Setup Resolver Guide](../guides/setup-resolver.md)
@@ -0,0 +1,100 @@
1
+ # Models Troubleshooting
2
+
3
+ Common issues and fixes for `@funkai/models`.
4
+
5
+ ## Cannot resolve model: model ID is empty
6
+
7
+ The model ID passed to the resolver is an empty string or whitespace.
8
+
9
+ **Fix:** Ensure the model ID is a non-empty string:
10
+
11
+ ```ts
12
+ const lm = resolve("openai/gpt-4.1");
13
+ ```
14
+
15
+ ## Cannot resolve model: no provider prefix and no fallback configured
16
+
17
+ A model ID without a `/` (e.g. `"gpt-4.1"`) was passed to a resolver with no fallback.
18
+
19
+ **Fix:** Either use the full `"provider/model"` format or configure a fallback:
20
+
21
+ ```ts
22
+ const resolve = createModelResolver({
23
+ fallback: openrouter,
24
+ });
25
+ ```
26
+
27
+ ## Cannot resolve model: no provider mapped for "x" and no fallback configured
28
+
29
+ The model ID prefix does not match any key in the `providers` map and no `fallback` is configured.
30
+
31
+ **Fix:** Add the provider to the `providers` map or add a fallback:
32
+
33
+ ```ts
34
+ const resolve = createModelResolver({
35
+ providers: {
36
+ openai: createOpenAI({ apiKey: process.env.OPENAI_API_KEY }),
37
+ },
38
+ fallback: openrouter,
39
+ });
40
+ ```
41
+
42
+ ## OPENROUTER_API_KEY environment variable is required
43
+
44
+ `openrouter` or `createOpenRouter()` was called without an API key, and `OPENROUTER_API_KEY` is not set in the environment.
45
+
46
+ **Fix:** Set the environment variable:
47
+
48
+ ```bash
49
+ export OPENROUTER_API_KEY=sk-or-...
50
+ ```
51
+
52
+ Or pass the key explicitly:
53
+
54
+ ```ts
55
+ const provider = createOpenRouter({ apiKey: "sk-or-..." });
56
+ ```
57
+
58
+ ## model() returns null
59
+
60
+ The model ID is not in the generated catalog. This can happen with new or custom models.
61
+
62
+ **Fix:** Check the model ID is correct. Regenerate the catalog to include newly released models:
63
+
64
+ ```bash
65
+ pnpm --filter=@funkai/models generate:models
66
+ ```
67
+
68
+ ## models() returns empty array
69
+
70
+ No models match the filter predicate.
71
+
72
+ **Fix:** Check available values before filtering:
73
+
74
+ ```ts
75
+ const providers = [...new Set(models().map((m) => m.provider))];
76
+ console.log(providers);
77
+ ```
78
+
79
+ ## Module not found: @funkai/models/provider
80
+
81
+ The subpath import does not match an available export.
82
+
83
+ **Fix:** Check the `exports` field in `package.json`. Valid subpath imports use the provider slug (e.g. `@funkai/models/openai`, not `@funkai/models/provider/openai`).
84
+
85
+ ## Type error: ModelId is not assignable
86
+
87
+ `ModelId` uses `LiteralUnion` which accepts both known catalog IDs and arbitrary strings. If you see type errors, ensure you are importing `ModelId` from `@funkai/models`:
88
+
89
+ ```ts
90
+ import type { ModelId } from "@funkai/models";
91
+
92
+ const id: ModelId = "openai/gpt-4.1";
93
+ ```
94
+
95
+ ## References
96
+
97
+ - [Model Catalog](catalog/overview.md)
98
+ - [Provider Resolution](provider/overview.md)
99
+ - [Cost Calculation](cost/overview.md)
100
+ - [Setup Resolver Guide](guides/setup-resolver.md)