@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.
- package/.generated/entries.json +23 -0
- package/.generated/req.txt +1 -0
- package/.turbo/turbo-build.log +145 -0
- package/.turbo/turbo-typecheck.log +4 -0
- package/CHANGELOG.md +23 -0
- package/README.md +95 -0
- package/dist/alibaba-B6q4Ng1R.mjs +957 -0
- package/dist/alibaba-B6q4Ng1R.mjs.map +1 -0
- package/dist/amazon-bedrock-Cv9AHQBH.mjs +2070 -0
- package/dist/amazon-bedrock-Cv9AHQBH.mjs.map +1 -0
- package/dist/anthropic-yB7ST97_.mjs +651 -0
- package/dist/anthropic-yB7ST97_.mjs.map +1 -0
- package/dist/cerebras-COfl7XM-.mjs +95 -0
- package/dist/cerebras-COfl7XM-.mjs.map +1 -0
- package/dist/cohere-B7TgO0hT.mjs +271 -0
- package/dist/cohere-B7TgO0hT.mjs.map +1 -0
- package/dist/deepinfra-B0GxUwCG.mjs +636 -0
- package/dist/deepinfra-B0GxUwCG.mjs.map +1 -0
- package/dist/deepseek-D64ZEsvS.mjs +50 -0
- package/dist/deepseek-D64ZEsvS.mjs.map +1 -0
- package/dist/fireworks-ai-DJYvdAi_.mjs +304 -0
- package/dist/fireworks-ai-DJYvdAi_.mjs.map +1 -0
- package/dist/google-BypRl349.mjs +833 -0
- package/dist/google-BypRl349.mjs.map +1 -0
- package/dist/google-vertex-DbS-zTGD.mjs +730 -0
- package/dist/google-vertex-DbS-zTGD.mjs.map +1 -0
- package/dist/groq-ei_PerYi.mjs +381 -0
- package/dist/groq-ei_PerYi.mjs.map +1 -0
- package/dist/huggingface-DaM1EeLP.mjs +456 -0
- package/dist/huggingface-DaM1EeLP.mjs.map +1 -0
- package/dist/inception-CspEzqNV.mjs +101 -0
- package/dist/inception-CspEzqNV.mjs.map +1 -0
- package/dist/index.d.mts +30314 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +271 -0
- package/dist/index.mjs.map +1 -0
- package/dist/llama-Cf3-koap.mjs +161 -0
- package/dist/llama-Cf3-koap.mjs.map +1 -0
- package/dist/mistral-BI9MdAO4.mjs +579 -0
- package/dist/mistral-BI9MdAO4.mjs.map +1 -0
- package/dist/nvidia-COHacuoa.mjs +1625 -0
- package/dist/nvidia-COHacuoa.mjs.map +1 -0
- package/dist/openai-C0nCfZUq.mjs +1023 -0
- package/dist/openai-C0nCfZUq.mjs.map +1 -0
- package/dist/openrouter-DSFzxKQb.mjs +4608 -0
- package/dist/openrouter-DSFzxKQb.mjs.map +1 -0
- package/dist/perplexity-zeZ2WlBU.mjs +96 -0
- package/dist/perplexity-zeZ2WlBU.mjs.map +1 -0
- package/dist/providers/alibaba.d.mts +1795 -0
- package/dist/providers/alibaba.d.mts.map +1 -0
- package/dist/providers/alibaba.mjs +39 -0
- package/dist/providers/alibaba.mjs.map +1 -0
- package/dist/providers/amazon-bedrock.d.mts +3713 -0
- package/dist/providers/amazon-bedrock.d.mts.map +1 -0
- package/dist/providers/amazon-bedrock.mjs +39 -0
- package/dist/providers/amazon-bedrock.mjs.map +1 -0
- package/dist/providers/anthropic.d.mts +1109 -0
- package/dist/providers/anthropic.d.mts.map +1 -0
- package/dist/providers/anthropic.mjs +39 -0
- package/dist/providers/anthropic.mjs.map +1 -0
- package/dist/providers/cerebras.d.mts +219 -0
- package/dist/providers/cerebras.d.mts.map +1 -0
- package/dist/providers/cerebras.mjs +39 -0
- package/dist/providers/cerebras.mjs.map +1 -0
- package/dist/providers/cohere.d.mts +555 -0
- package/dist/providers/cohere.d.mts.map +1 -0
- package/dist/providers/cohere.mjs +39 -0
- package/dist/providers/cohere.mjs.map +1 -0
- package/dist/providers/deepinfra.d.mts +1245 -0
- package/dist/providers/deepinfra.d.mts.map +1 -0
- package/dist/providers/deepinfra.mjs +39 -0
- package/dist/providers/deepinfra.mjs.map +1 -0
- package/dist/providers/deepseek.d.mts +139 -0
- package/dist/providers/deepseek.d.mts.map +1 -0
- package/dist/providers/deepseek.mjs +39 -0
- package/dist/providers/deepseek.mjs.map +1 -0
- package/dist/providers/fireworks-ai.d.mts +611 -0
- package/dist/providers/fireworks-ai.d.mts.map +1 -0
- package/dist/providers/fireworks-ai.mjs +39 -0
- package/dist/providers/fireworks-ai.mjs.map +1 -0
- package/dist/providers/google-vertex.d.mts +1227 -0
- package/dist/providers/google-vertex.d.mts.map +1 -0
- package/dist/providers/google-vertex.mjs +39 -0
- package/dist/providers/google-vertex.mjs.map +1 -0
- package/dist/providers/google.d.mts +1359 -0
- package/dist/providers/google.d.mts.map +1 -0
- package/dist/providers/google.mjs +39 -0
- package/dist/providers/google.mjs.map +1 -0
- package/dist/providers/groq.d.mts +765 -0
- package/dist/providers/groq.d.mts.map +1 -0
- package/dist/providers/groq.mjs +39 -0
- package/dist/providers/groq.mjs.map +1 -0
- package/dist/providers/huggingface.d.mts +901 -0
- package/dist/providers/huggingface.d.mts.map +1 -0
- package/dist/providers/huggingface.mjs +39 -0
- package/dist/providers/huggingface.mjs.map +1 -0
- package/dist/providers/inception.d.mts +231 -0
- package/dist/providers/inception.d.mts.map +1 -0
- package/dist/providers/inception.mjs +39 -0
- package/dist/providers/inception.mjs.map +1 -0
- package/dist/providers/llama.d.mts +345 -0
- package/dist/providers/llama.d.mts.map +1 -0
- package/dist/providers/llama.mjs +39 -0
- package/dist/providers/llama.mjs.map +1 -0
- package/dist/providers/mistral.d.mts +1143 -0
- package/dist/providers/mistral.d.mts.map +1 -0
- package/dist/providers/mistral.mjs +39 -0
- package/dist/providers/mistral.mjs.map +1 -0
- package/dist/providers/nvidia.d.mts +3117 -0
- package/dist/providers/nvidia.d.mts.map +1 -0
- package/dist/providers/nvidia.mjs +39 -0
- package/dist/providers/nvidia.mjs.map +1 -0
- package/dist/providers/openai.d.mts +1963 -0
- package/dist/providers/openai.d.mts.map +1 -0
- package/dist/providers/openai.mjs +39 -0
- package/dist/providers/openai.mjs.map +1 -0
- package/dist/providers/openrouter.d.mts +8531 -0
- package/dist/providers/openrouter.d.mts.map +1 -0
- package/dist/providers/openrouter.mjs +39 -0
- package/dist/providers/openrouter.mjs.map +1 -0
- package/dist/providers/perplexity.d.mts +221 -0
- package/dist/providers/perplexity.d.mts.map +1 -0
- package/dist/providers/perplexity.mjs +39 -0
- package/dist/providers/perplexity.mjs.map +1 -0
- package/dist/providers/togetherai.d.mts +767 -0
- package/dist/providers/togetherai.d.mts.map +1 -0
- package/dist/providers/togetherai.mjs +39 -0
- package/dist/providers/togetherai.mjs.map +1 -0
- package/dist/providers/xai.d.mts +1161 -0
- package/dist/providers/xai.d.mts.map +1 -0
- package/dist/providers/xai.mjs +39 -0
- package/dist/providers/xai.mjs.map +1 -0
- package/dist/togetherai-BvcxUfPE.mjs +382 -0
- package/dist/togetherai-BvcxUfPE.mjs.map +1 -0
- package/dist/types-DjdaZckF.d.mts +71 -0
- package/dist/types-DjdaZckF.d.mts.map +1 -0
- package/dist/xai-fSuAkQJo.mjs +587 -0
- package/dist/xai-fSuAkQJo.mjs.map +1 -0
- package/docs/catalog/filtering.md +102 -0
- package/docs/catalog/overview.md +168 -0
- package/docs/catalog/providers.md +73 -0
- package/docs/cost/overview.md +125 -0
- package/docs/guides/filter-models.md +113 -0
- package/docs/guides/setup-resolver.md +106 -0
- package/docs/guides/track-costs.md +133 -0
- package/docs/overview.md +139 -0
- package/docs/provider/configuration.md +100 -0
- package/docs/provider/openrouter.md +105 -0
- package/docs/provider/overview.md +131 -0
- package/docs/troubleshooting.md +100 -0
- package/package.json +142 -0
- package/providers.json +39 -0
- package/scripts/generate-models.ts +392 -0
- package/src/catalog/index.test.ts +124 -0
- package/src/catalog/index.ts +65 -0
- package/src/catalog/providers/alibaba.ts +468 -0
- package/src/catalog/providers/amazon-bedrock.ts +941 -0
- package/src/catalog/providers/anthropic.ts +270 -0
- package/src/catalog/providers/cerebras.ts +61 -0
- package/src/catalog/providers/cohere.ts +149 -0
- package/src/catalog/providers/deepinfra.ts +325 -0
- package/src/catalog/providers/deepseek.ts +39 -0
- package/src/catalog/providers/fireworks-ai.ts +160 -0
- package/src/catalog/providers/google-vertex.ts +314 -0
- package/src/catalog/providers/google.ts +347 -0
- package/src/catalog/providers/groq.ts +204 -0
- package/src/catalog/providers/huggingface.ts +237 -0
- package/src/catalog/providers/inception.ts +61 -0
- package/src/catalog/providers/index.ts +59 -0
- package/src/catalog/providers/llama.ts +94 -0
- package/src/catalog/providers/mistral.ts +303 -0
- package/src/catalog/providers/nvidia.ts +820 -0
- package/src/catalog/providers/openai.ts +501 -0
- package/src/catalog/providers/openrouter.ts +2201 -0
- package/src/catalog/providers/perplexity.ts +61 -0
- package/src/catalog/providers/togetherai.ts +204 -0
- package/src/catalog/providers/xai.ts +292 -0
- package/src/catalog/types.ts +86 -0
- package/src/cost/calculate.test.ts +157 -0
- package/src/cost/calculate.ts +43 -0
- package/src/cost/index.ts +2 -0
- package/src/cost/types.ts +25 -0
- package/src/index.ts +25 -0
- package/src/provider/index.ts +9 -0
- package/src/provider/openrouter.test.ts +125 -0
- package/src/provider/openrouter.ts +110 -0
- package/src/provider/resolver.test.ts +138 -0
- package/src/provider/resolver.ts +125 -0
- package/src/provider/types.ts +39 -0
- package/src/providers/alibaba.ts +65 -0
- package/src/providers/amazon-bedrock.ts +67 -0
- package/src/providers/anthropic.ts +65 -0
- package/src/providers/cerebras.ts +65 -0
- package/src/providers/cohere.ts +65 -0
- package/src/providers/deepinfra.ts +65 -0
- package/src/providers/deepseek.ts +65 -0
- package/src/providers/fireworks-ai.ts +65 -0
- package/src/providers/google-vertex.ts +67 -0
- package/src/providers/google.ts +65 -0
- package/src/providers/groq.ts +65 -0
- package/src/providers/huggingface.ts +67 -0
- package/src/providers/inception.ts +65 -0
- package/src/providers/llama.ts +65 -0
- package/src/providers/mistral.ts +65 -0
- package/src/providers/nvidia.ts +65 -0
- package/src/providers/openai.ts +65 -0
- package/src/providers/openrouter.ts +67 -0
- package/src/providers/perplexity.ts +67 -0
- package/src/providers/togetherai.ts +65 -0
- package/src/providers/xai.ts +65 -0
- package/tsconfig.json +25 -0
- package/tsdown.config.ts +23 -0
- 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)
|
package/docs/overview.md
ADDED
|
@@ -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)
|