ai-model-advisor-mcp 1.0.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/LICENSE +21 -0
- package/README.md +135 -0
- package/build/cache.d.ts +3 -0
- package/build/cache.d.ts.map +1 -0
- package/build/cache.js +25 -0
- package/build/cache.js.map +1 -0
- package/build/cli.d.ts +3 -0
- package/build/cli.d.ts.map +1 -0
- package/build/cli.js +14 -0
- package/build/cli.js.map +1 -0
- package/build/data/quality.d.ts +6 -0
- package/build/data/quality.d.ts.map +1 -0
- package/build/data/quality.js +61 -0
- package/build/data/quality.js.map +1 -0
- package/build/providers/fal.d.ts +3 -0
- package/build/providers/fal.d.ts.map +1 -0
- package/build/providers/fal.js +155 -0
- package/build/providers/fal.js.map +1 -0
- package/build/providers/openrouter.d.ts +3 -0
- package/build/providers/openrouter.d.ts.map +1 -0
- package/build/providers/openrouter.js +78 -0
- package/build/providers/openrouter.js.map +1 -0
- package/build/providers/registry.d.ts +7 -0
- package/build/providers/registry.d.ts.map +1 -0
- package/build/providers/registry.js +23 -0
- package/build/providers/registry.js.map +1 -0
- package/build/server.d.ts +186 -0
- package/build/server.d.ts.map +1 -0
- package/build/server.js +164 -0
- package/build/server.js.map +1 -0
- package/build/server.test.d.ts +2 -0
- package/build/server.test.d.ts.map +1 -0
- package/build/server.test.js +256 -0
- package/build/server.test.js.map +1 -0
- package/build/tools/compare.d.ts +15 -0
- package/build/tools/compare.d.ts.map +1 -0
- package/build/tools/compare.js +61 -0
- package/build/tools/compare.js.map +1 -0
- package/build/tools/discover.d.ts +15 -0
- package/build/tools/discover.d.ts.map +1 -0
- package/build/tools/discover.js +68 -0
- package/build/tools/discover.js.map +1 -0
- package/build/tools/estimate.d.ts +15 -0
- package/build/tools/estimate.d.ts.map +1 -0
- package/build/tools/estimate.js +42 -0
- package/build/tools/estimate.js.map +1 -0
- package/build/tools/info.d.ts +15 -0
- package/build/tools/info.d.ts.map +1 -0
- package/build/tools/info.js +76 -0
- package/build/tools/info.js.map +1 -0
- package/build/tools/list.d.ts +8 -0
- package/build/tools/list.d.ts.map +1 -0
- package/build/tools/list.js +56 -0
- package/build/tools/list.js.map +1 -0
- package/build/tools/recommend.d.ts +15 -0
- package/build/tools/recommend.d.ts.map +1 -0
- package/build/tools/recommend.js +121 -0
- package/build/tools/recommend.js.map +1 -0
- package/build/types.d.ts +50 -0
- package/build/types.d.ts.map +1 -0
- package/build/types.js +3 -0
- package/build/types.js.map +1 -0
- package/package.json +55 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# ai-model-advisor-mcp
|
|
2
|
+
|
|
3
|
+
The most comprehensive AI model advisor MCP server. Compare pricing, capabilities, and quality across **500+ models** — LLMs, image gen, video gen, TTS, STT, and 3D — from OpenRouter and fal.ai.
|
|
4
|
+
|
|
5
|
+
> **"I need to generate images — what's the best model for my budget?"**
|
|
6
|
+
> Just ask. Your agent now knows.
|
|
7
|
+
|
|
8
|
+
## Why?
|
|
9
|
+
|
|
10
|
+
New AI models drop constantly. Your coding agent has no idea what's available, what things cost, or which model is best for your task. This MCP fixes that — giving your agent live access to:
|
|
11
|
+
|
|
12
|
+
- 🧠 **300+ LLMs** via OpenRouter (GPT-4o, Claude, Gemini, Llama, Mistral, etc.)
|
|
13
|
+
- 🎨 **200+ Media models** via fal.ai (Flux, Stable Diffusion, Kling Video, Whisper, etc.)
|
|
14
|
+
- 💰 **Live pricing** for every model
|
|
15
|
+
- ⭐ **Curated quality tiers** (S/A/B/C) for 50+ popular models
|
|
16
|
+
- 🆕 **New model discovery** — know when models drop
|
|
17
|
+
|
|
18
|
+
## Quick Start
|
|
19
|
+
|
|
20
|
+
### Claude Desktop / Antigravity / Cursor
|
|
21
|
+
|
|
22
|
+
Add to your MCP config:
|
|
23
|
+
|
|
24
|
+
```json
|
|
25
|
+
{
|
|
26
|
+
"mcpServers": {
|
|
27
|
+
"model-advisor": {
|
|
28
|
+
"command": "npx",
|
|
29
|
+
"args": ["-y", "ai-model-advisor-mcp"],
|
|
30
|
+
"env": {
|
|
31
|
+
"FAL_KEY": "your-fal-key-here",
|
|
32
|
+
"OPENROUTER_API_KEY": "your-openrouter-key-here"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### API Keys
|
|
40
|
+
|
|
41
|
+
| Key | Required? | What it enables |
|
|
42
|
+
|-----|-----------|----------------|
|
|
43
|
+
| `FAL_KEY` | Optional | fal.ai pricing data (model listing works without it) |
|
|
44
|
+
| `OPENROUTER_API_KEY` | Optional | Better rate limits for OpenRouter API |
|
|
45
|
+
|
|
46
|
+
**Both keys are optional.** The server works without them — you'll get model listings and capabilities, just without fal.ai pricing.
|
|
47
|
+
|
|
48
|
+
## Tools
|
|
49
|
+
|
|
50
|
+
### 🎯 `recommend_model`
|
|
51
|
+
|
|
52
|
+
"I need X" → ranked models matching your task, requirements, and budget.
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
recommend_model({ task: "image generation", requirements: ["photorealistic", "fast"], budget: "low" })
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### ⚖️ `compare_models`
|
|
59
|
+
|
|
60
|
+
Side-by-side table across providers. Auto-adapts columns for LLMs vs media models.
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
compare_models({ model_ids: ["openai/gpt-4o", "anthropic/claude-sonnet-4", "google/gemini-2.5-pro-preview"] })
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### 📋 `list_models`
|
|
67
|
+
|
|
68
|
+
Browse and filter by category, provider, capability, or price.
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
list_models({ category: "text-to-image", max_price: 0.05 })
|
|
72
|
+
list_models({ provider: "fal", category: "text-to-video" })
|
|
73
|
+
list_models({ capability: "reasoning" })
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### 📖 `get_model_info`
|
|
77
|
+
|
|
78
|
+
Comprehensive model card with everything you need to decide.
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
get_model_info({ model_id: "fal-ai/flux-pro/v1.1" })
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### 💰 `estimate_cost`
|
|
85
|
+
|
|
86
|
+
Cost estimation for any usage scenario.
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
estimate_cost({ model_id: "openai/gpt-4o", usage: { input_tokens: 1000000, output_tokens: 100000 } })
|
|
90
|
+
estimate_cost({ model_id: "fal-ai/flux-pro/v1.1", usage: { images: 500 } })
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### 🆕 `whats_new`
|
|
94
|
+
|
|
95
|
+
Discover recently added models. Never miss a new release.
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
whats_new({ since: "7d" })
|
|
99
|
+
whats_new({ since: "30d", category: "text-to-video" })
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Categories
|
|
103
|
+
|
|
104
|
+
| Category | Examples |
|
|
105
|
+
|----------|---------|
|
|
106
|
+
| `llm` | GPT-4o, Claude, Gemini, Llama, Mistral |
|
|
107
|
+
| `text-to-image` | Flux Pro, Stable Diffusion, DALL-E |
|
|
108
|
+
| `image-to-image` | Flux Edit, img2img pipelines |
|
|
109
|
+
| `text-to-video` | Kling, Minimax, Hunyuan |
|
|
110
|
+
| `image-to-video` | Kling i2v, Runway |
|
|
111
|
+
| `text-to-speech` | Kokoro, ElevenLabs |
|
|
112
|
+
| `speech-to-text` | Wizper (Whisper) |
|
|
113
|
+
| `text-to-audio` | Music/sound generation |
|
|
114
|
+
| `image-to-3d` | Hunyuan3D, Trellis |
|
|
115
|
+
| `vision` | Visual understanding models |
|
|
116
|
+
|
|
117
|
+
## Quality Tiers
|
|
118
|
+
|
|
119
|
+
Popular models are rated on a curated quality scale:
|
|
120
|
+
|
|
121
|
+
- **S** — Best in class (Claude Sonnet 4, GPT-4o, Flux Pro Ultra, Kling v2.0)
|
|
122
|
+
- **A** — Excellent (Gemini Flash, Llama 3.3, Flux Dev, Recraft v3)
|
|
123
|
+
- **B** — Good (Mistral Small, Flux Schnell, SD3.5 Turbo)
|
|
124
|
+
- **C** — Adequate
|
|
125
|
+
|
|
126
|
+
## Data Sources
|
|
127
|
+
|
|
128
|
+
| Provider | Models | Auth | What |
|
|
129
|
+
|----------|--------|------|------|
|
|
130
|
+
| [OpenRouter](https://openrouter.ai) | 300+ LLMs | Optional | Pricing, capabilities, descriptions, params |
|
|
131
|
+
| [fal.ai](https://fal.ai) | 200+ media models | Optional (pricing only) | Image/video/audio/3D models with pricing |
|
|
132
|
+
|
|
133
|
+
## License
|
|
134
|
+
|
|
135
|
+
MIT
|
package/build/cache.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAWA,wBAAsB,MAAM,CAAC,CAAC,EAC5B,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACxB,OAAO,CAAC,CAAC,CAAC,CAiBZ;AAED,wBAAgB,UAAU,IAAI,IAAI,CAEjC"}
|
package/build/cache.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// ─── TTL Cache ──────────────────────────────────────────────────────────────
|
|
2
|
+
const CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes
|
|
3
|
+
const store = new Map();
|
|
4
|
+
export async function cached(key, fetcher) {
|
|
5
|
+
const now = Date.now();
|
|
6
|
+
const entry = store.get(key);
|
|
7
|
+
if (entry && now - entry.fetchedAt < CACHE_TTL_MS) {
|
|
8
|
+
return entry.data;
|
|
9
|
+
}
|
|
10
|
+
try {
|
|
11
|
+
const data = await fetcher();
|
|
12
|
+
store.set(key, { data, fetchedAt: now });
|
|
13
|
+
return data;
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
// Return stale data if available
|
|
17
|
+
if (entry)
|
|
18
|
+
return entry.data;
|
|
19
|
+
throw error;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export function clearCache() {
|
|
23
|
+
store.clear();
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAO/E,MAAM,YAAY,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,YAAY;AAEhD,MAAM,KAAK,GAAG,IAAI,GAAG,EAA+B,CAAC;AAErD,MAAM,CAAC,KAAK,UAAU,MAAM,CAC1B,GAAW,EACX,OAAyB;IAEzB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAA8B,CAAC;IAE1D,IAAI,KAAK,IAAI,GAAG,GAAG,KAAK,CAAC,SAAS,GAAG,YAAY,EAAE,CAAC;QAClD,OAAO,KAAK,CAAC,IAAI,CAAC;IACpB,CAAC;IAED,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,OAAO,EAAE,CAAC;QAC7B,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC;QACzC,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,iCAAiC;QACjC,IAAI,KAAK;YAAE,OAAO,KAAK,CAAC,IAAI,CAAC;QAC7B,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,KAAK,CAAC,KAAK,EAAE,CAAC;AAChB,CAAC"}
|
package/build/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/build/cli.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import { createServer } from "./server.js";
|
|
4
|
+
async function main() {
|
|
5
|
+
const server = createServer();
|
|
6
|
+
const transport = new StdioServerTransport();
|
|
7
|
+
await server.connect(transport);
|
|
8
|
+
console.error("Model Advisor MCP running on stdio");
|
|
9
|
+
}
|
|
10
|
+
main().catch((error) => {
|
|
11
|
+
console.error("Fatal error in main():", error);
|
|
12
|
+
process.exit(1);
|
|
13
|
+
});
|
|
14
|
+
//# sourceMappingURL=cli.js.map
|
package/build/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,KAAK,UAAU,IAAI;IACjB,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;IAC9B,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,OAAO,CAAC,KAAK,CAAC,oCAAoC,CAAC,CAAC;AACtD,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,OAAO,CAAC,KAAK,CAAC,wBAAwB,EAAE,KAAK,CAAC,CAAC;IAC/C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quality.d.ts","sourceRoot":"","sources":["../../src/data/quality.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAe,MAAM,aAAa,CAAC;AAwD7D;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,EAAE,CAQxE"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// ─── Curated Quality Tiers ──────────────────────────────────────────────────
|
|
2
|
+
// These are opinionated quality ratings for popular models, updated manually.
|
|
3
|
+
// S = Best in class, A = Excellent, B = Good, C = Adequate
|
|
4
|
+
const QUALITY_TIERS = {
|
|
5
|
+
// ─── LLMs ──────────────────────────────────────────────────
|
|
6
|
+
"anthropic/claude-opus-4": "S",
|
|
7
|
+
"anthropic/claude-sonnet-4": "S",
|
|
8
|
+
"openai/gpt-4o": "S",
|
|
9
|
+
"google/gemini-2.5-pro-preview": "S",
|
|
10
|
+
"anthropic/claude-haiku-3.5": "A",
|
|
11
|
+
"openai/gpt-4o-mini": "A",
|
|
12
|
+
"google/gemini-2.0-flash-001": "A",
|
|
13
|
+
"google/gemini-2.5-flash-preview": "A",
|
|
14
|
+
"deepseek/deepseek-chat": "A",
|
|
15
|
+
"deepseek/deepseek-r1": "S",
|
|
16
|
+
"meta-llama/llama-4-maverick": "A",
|
|
17
|
+
"meta-llama/llama-4-scout": "B",
|
|
18
|
+
"meta-llama/llama-3.3-70b-instruct": "A",
|
|
19
|
+
"mistralai/mistral-large": "A",
|
|
20
|
+
"mistralai/mistral-small-3.1-24b-instruct": "B",
|
|
21
|
+
"qwen/qwen-2.5-72b-instruct": "A",
|
|
22
|
+
"qwen/qwen-2.5-coder-32b-instruct": "A",
|
|
23
|
+
// ─── Image Generation ──────────────────────────────────────
|
|
24
|
+
"fal-ai/flux-pro/v1.1-ultra": "S",
|
|
25
|
+
"fal-ai/flux-pro/v1.1": "S",
|
|
26
|
+
"fal-ai/flux-pro": "A",
|
|
27
|
+
"fal-ai/flux/dev": "A",
|
|
28
|
+
"fal-ai/flux/schnell": "B",
|
|
29
|
+
"fal-ai/flux-realism": "A",
|
|
30
|
+
"fal-ai/stable-diffusion-v35-large": "A",
|
|
31
|
+
"fal-ai/stable-diffusion-v35-large-turbo": "B",
|
|
32
|
+
"fal-ai/recraft-v3": "A",
|
|
33
|
+
"fal-ai/ideogram/v2/turbo": "A",
|
|
34
|
+
"fal-ai/aura-flow": "B",
|
|
35
|
+
// ─── Video Generation ──────────────────────────────────────
|
|
36
|
+
"fal-ai/kling-video/v2.0/master": "S",
|
|
37
|
+
"fal-ai/kling-video/v1.5/pro": "A",
|
|
38
|
+
"fal-ai/minimax/video-01": "A",
|
|
39
|
+
"fal-ai/wan/v2.2-a14b/text-to-video": "B",
|
|
40
|
+
"fal-ai/hunyuan-video": "A",
|
|
41
|
+
"fal-ai/luma-dream-machine": "A",
|
|
42
|
+
// ─── Speech/Audio ──────────────────────────────────────────
|
|
43
|
+
"fal-ai/wizper/large-v3": "S",
|
|
44
|
+
"fal-ai/kokoro/american-english": "A",
|
|
45
|
+
// ─── 3D ────────────────────────────────────────────────────
|
|
46
|
+
"fal-ai/hunyuan3d-v21": "A",
|
|
47
|
+
"fal-ai/trellis": "B",
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Applies curated quality tiers to models in-place and returns them.
|
|
51
|
+
*/
|
|
52
|
+
export function applyQualityTiers(models) {
|
|
53
|
+
for (const model of models) {
|
|
54
|
+
const tier = QUALITY_TIERS[model.id];
|
|
55
|
+
if (tier) {
|
|
56
|
+
model.qualityTier = tier;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return models;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=quality.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quality.js","sourceRoot":"","sources":["../../src/data/quality.ts"],"names":[],"mappings":"AAEA,+EAA+E;AAC/E,8EAA8E;AAC9E,2DAA2D;AAE3D,MAAM,aAAa,GAAgC;IACjD,8DAA8D;IAC9D,yBAAyB,EAAE,GAAG;IAC9B,2BAA2B,EAAE,GAAG;IAChC,eAAe,EAAE,GAAG;IACpB,+BAA+B,EAAE,GAAG;IACpC,4BAA4B,EAAE,GAAG;IACjC,oBAAoB,EAAE,GAAG;IACzB,6BAA6B,EAAE,GAAG;IAClC,iCAAiC,EAAE,GAAG;IACtC,wBAAwB,EAAE,GAAG;IAC7B,sBAAsB,EAAE,GAAG;IAC3B,6BAA6B,EAAE,GAAG;IAClC,0BAA0B,EAAE,GAAG;IAC/B,mCAAmC,EAAE,GAAG;IACxC,yBAAyB,EAAE,GAAG;IAC9B,0CAA0C,EAAE,GAAG;IAC/C,4BAA4B,EAAE,GAAG;IACjC,kCAAkC,EAAE,GAAG;IAEvC,8DAA8D;IAC9D,4BAA4B,EAAE,GAAG;IACjC,sBAAsB,EAAE,GAAG;IAC3B,iBAAiB,EAAE,GAAG;IACtB,iBAAiB,EAAE,GAAG;IACtB,qBAAqB,EAAE,GAAG;IAC1B,qBAAqB,EAAE,GAAG;IAC1B,mCAAmC,EAAE,GAAG;IACxC,yCAAyC,EAAE,GAAG;IAC9C,mBAAmB,EAAE,GAAG;IACxB,0BAA0B,EAAE,GAAG;IAC/B,kBAAkB,EAAE,GAAG;IAEvB,8DAA8D;IAC9D,gCAAgC,EAAE,GAAG;IACrC,6BAA6B,EAAE,GAAG;IAClC,yBAAyB,EAAE,GAAG;IAC9B,oCAAoC,EAAE,GAAG;IACzC,sBAAsB,EAAE,GAAG;IAC3B,2BAA2B,EAAE,GAAG;IAEhC,8DAA8D;IAC9D,wBAAwB,EAAE,GAAG;IAC7B,gCAAgC,EAAE,GAAG;IAErC,8DAA8D;IAC9D,sBAAsB,EAAE,GAAG;IAC3B,gBAAgB,EAAE,GAAG;CACtB,CAAC;AAEF;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAAsB;IACtD,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,IAAI,GAAG,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACrC,IAAI,IAAI,EAAE,CAAC;YACT,KAAK,CAAC,WAAW,GAAG,IAAI,CAAC;QAC3B,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fal.d.ts","sourceRoot":"","sources":["../../src/providers/fal.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAgB,aAAa,EAAiB,MAAM,aAAa,CAAC;AAyL9E,eAAO,MAAM,WAAW,EAAE,aAGzB,CAAC"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { cached } from "../cache.js";
|
|
2
|
+
// ─── Category mapping ───────────────────────────────────────────────────────
|
|
3
|
+
const CATEGORY_MAP = {
|
|
4
|
+
"text-to-image": "text-to-image",
|
|
5
|
+
"image-to-image": "image-to-image",
|
|
6
|
+
"text-to-video": "text-to-video",
|
|
7
|
+
"image-to-video": "image-to-video",
|
|
8
|
+
"video-to-video": "video-to-video",
|
|
9
|
+
"text-to-speech": "text-to-speech",
|
|
10
|
+
"speech-to-text": "speech-to-text",
|
|
11
|
+
"text-to-audio": "text-to-audio",
|
|
12
|
+
"image-to-3d": "image-to-3d",
|
|
13
|
+
"vision": "vision",
|
|
14
|
+
"llm": "llm",
|
|
15
|
+
"training": "other",
|
|
16
|
+
};
|
|
17
|
+
function mapCategory(falCategory) {
|
|
18
|
+
return CATEGORY_MAP[falCategory] ?? "other";
|
|
19
|
+
}
|
|
20
|
+
// ─── Fetch models (no auth required) ────────────────────────────────────────
|
|
21
|
+
async function fetchAllFalModels() {
|
|
22
|
+
const allModels = [];
|
|
23
|
+
let cursor = null;
|
|
24
|
+
let hasMore = true;
|
|
25
|
+
while (hasMore) {
|
|
26
|
+
const url = new URL("https://api.fal.ai/v1/models");
|
|
27
|
+
url.searchParams.set("limit", "200");
|
|
28
|
+
if (cursor)
|
|
29
|
+
url.searchParams.set("cursor", cursor);
|
|
30
|
+
let retries = 3;
|
|
31
|
+
while (retries > 0) {
|
|
32
|
+
const response = await fetch(url.toString());
|
|
33
|
+
if (response.status === 429) {
|
|
34
|
+
retries--;
|
|
35
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
if (!response.ok) {
|
|
39
|
+
throw new Error(`fal.ai API error: ${response.status} ${response.statusText}`);
|
|
40
|
+
}
|
|
41
|
+
const data = await response.json();
|
|
42
|
+
const models = data.models;
|
|
43
|
+
allModels.push(...models.filter((m) => m.metadata.status === "active"));
|
|
44
|
+
cursor = data.next_cursor ?? null;
|
|
45
|
+
hasMore = data.has_more === true && cursor !== null;
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
if (retries === 0) {
|
|
49
|
+
// If we exhausted retries due to 429, just return what we have so far
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return allModels;
|
|
54
|
+
}
|
|
55
|
+
// ─── Fetch pricing (auth required) ──────────────────────────────────────────
|
|
56
|
+
async function fetchFalPricing(endpointIds) {
|
|
57
|
+
const falKey = process.env.FAL_KEY;
|
|
58
|
+
if (!falKey)
|
|
59
|
+
return new Map();
|
|
60
|
+
const priceMap = new Map();
|
|
61
|
+
// Batch in groups of 50
|
|
62
|
+
for (let i = 0; i < endpointIds.length; i += 50) {
|
|
63
|
+
const batch = endpointIds.slice(i, i + 50);
|
|
64
|
+
const url = new URL("https://api.fal.ai/v1/models/pricing");
|
|
65
|
+
for (const id of batch) {
|
|
66
|
+
url.searchParams.append("endpoint_id", id);
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
const response = await fetch(url.toString(), {
|
|
70
|
+
headers: { Authorization: `Key ${falKey}` },
|
|
71
|
+
});
|
|
72
|
+
if (!response.ok)
|
|
73
|
+
continue;
|
|
74
|
+
const data = await response.json();
|
|
75
|
+
for (const entry of (data.prices ?? [])) {
|
|
76
|
+
priceMap.set(entry.endpoint_id, entry);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
// Continue without pricing for this batch
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return priceMap;
|
|
84
|
+
}
|
|
85
|
+
// ─── Price formatting ───────────────────────────────────────────────────────
|
|
86
|
+
function formatFalPrice(price) {
|
|
87
|
+
if (!price)
|
|
88
|
+
return "Pricing requires FAL_KEY";
|
|
89
|
+
if (price.unit_price === 0)
|
|
90
|
+
return "FREE";
|
|
91
|
+
return `$${price.unit_price.toFixed(4)} / ${price.unit}`;
|
|
92
|
+
}
|
|
93
|
+
// ─── Derive capabilities from category + tags ──────────────────────────────
|
|
94
|
+
function deriveCapabilities(m) {
|
|
95
|
+
const caps = [];
|
|
96
|
+
const tags = m.metadata.tags ?? [];
|
|
97
|
+
const cat = m.metadata.category;
|
|
98
|
+
// From tags
|
|
99
|
+
if (tags.includes("realism"))
|
|
100
|
+
caps.push("photorealistic");
|
|
101
|
+
if (tags.includes("typography"))
|
|
102
|
+
caps.push("typography");
|
|
103
|
+
if (tags.includes("fast"))
|
|
104
|
+
caps.push("fast");
|
|
105
|
+
if (tags.includes("hd"))
|
|
106
|
+
caps.push("high_resolution");
|
|
107
|
+
if (tags.includes("inpaint"))
|
|
108
|
+
caps.push("inpainting");
|
|
109
|
+
if (tags.includes("controlnet"))
|
|
110
|
+
caps.push("controlnet");
|
|
111
|
+
// From category
|
|
112
|
+
if (cat === "text-to-image" || cat === "image-to-image")
|
|
113
|
+
caps.push("image_generation");
|
|
114
|
+
if (cat === "text-to-video" || cat === "image-to-video" || cat === "video-to-video")
|
|
115
|
+
caps.push("video_generation");
|
|
116
|
+
if (cat === "text-to-speech")
|
|
117
|
+
caps.push("tts");
|
|
118
|
+
if (cat === "speech-to-text")
|
|
119
|
+
caps.push("stt");
|
|
120
|
+
if (cat === "text-to-audio")
|
|
121
|
+
caps.push("audio_generation");
|
|
122
|
+
if (cat === "image-to-3d")
|
|
123
|
+
caps.push("3d_generation");
|
|
124
|
+
return caps;
|
|
125
|
+
}
|
|
126
|
+
// ─── Provider ───────────────────────────────────────────────────────────────
|
|
127
|
+
async function fetchFalModels() {
|
|
128
|
+
const models = await fetchAllFalModels();
|
|
129
|
+
const endpointIds = models.map((m) => m.endpoint_id);
|
|
130
|
+
const priceMap = await fetchFalPricing(endpointIds);
|
|
131
|
+
return models.map((m) => {
|
|
132
|
+
const price = priceMap.get(m.endpoint_id);
|
|
133
|
+
return {
|
|
134
|
+
id: m.endpoint_id,
|
|
135
|
+
name: m.metadata.display_name,
|
|
136
|
+
provider: "fal",
|
|
137
|
+
description: m.metadata.description ?? "",
|
|
138
|
+
category: mapCategory(m.metadata.category),
|
|
139
|
+
pricing: {
|
|
140
|
+
unit: price?.unit ?? "unknown",
|
|
141
|
+
unitPrice: price?.unit_price ?? -1,
|
|
142
|
+
formatted: formatFalPrice(price),
|
|
143
|
+
},
|
|
144
|
+
capabilities: deriveCapabilities(m),
|
|
145
|
+
addedDate: m.metadata.date ?? m.metadata.updated_at,
|
|
146
|
+
tags: m.metadata.tags,
|
|
147
|
+
licenseType: m.metadata.license_type,
|
|
148
|
+
};
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
export const falProvider = {
|
|
152
|
+
name: "fal",
|
|
153
|
+
fetchModels: () => cached("fal", fetchFalModels),
|
|
154
|
+
};
|
|
155
|
+
//# sourceMappingURL=fal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fal.js","sourceRoot":"","sources":["../../src/providers/fal.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openrouter.d.ts","sourceRoot":"","sources":["../../src/providers/openrouter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAgB,aAAa,EAAiB,MAAM,aAAa,CAAC;AAsG9E,eAAO,MAAM,kBAAkB,EAAE,aAGhC,CAAC"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { cached } from "../cache.js";
|
|
2
|
+
// ─── Capability derivation ──────────────────────────────────────────────────
|
|
3
|
+
function deriveCapabilities(m) {
|
|
4
|
+
const caps = [];
|
|
5
|
+
const params = m.supported_parameters ?? [];
|
|
6
|
+
const inp = m.architecture?.input_modalities ?? [];
|
|
7
|
+
const out = m.architecture?.output_modalities ?? [];
|
|
8
|
+
if (params.includes("tools") || params.includes("tool_choice"))
|
|
9
|
+
caps.push("tool_use");
|
|
10
|
+
if (params.includes("reasoning") || params.includes("include_reasoning"))
|
|
11
|
+
caps.push("reasoning");
|
|
12
|
+
if (params.includes("structured_outputs") || params.includes("response_format"))
|
|
13
|
+
caps.push("structured_output");
|
|
14
|
+
if (inp.includes("image"))
|
|
15
|
+
caps.push("vision");
|
|
16
|
+
if (inp.includes("audio"))
|
|
17
|
+
caps.push("audio_input");
|
|
18
|
+
if (out.includes("audio"))
|
|
19
|
+
caps.push("audio_output");
|
|
20
|
+
if (out.includes("image"))
|
|
21
|
+
caps.push("image_output");
|
|
22
|
+
if (inp.includes("file"))
|
|
23
|
+
caps.push("file_input");
|
|
24
|
+
return caps;
|
|
25
|
+
}
|
|
26
|
+
function deriveCategory(m) {
|
|
27
|
+
const modality = m.architecture?.modality ?? "";
|
|
28
|
+
if (modality.includes("->image"))
|
|
29
|
+
return "text-to-image";
|
|
30
|
+
if (modality.includes("->audio"))
|
|
31
|
+
return "text-to-speech";
|
|
32
|
+
return "llm";
|
|
33
|
+
}
|
|
34
|
+
// ─── Cost formatting ────────────────────────────────────────────────────────
|
|
35
|
+
function formatLLMPricing(promptCost, completionCost) {
|
|
36
|
+
const p = parseFloat(promptCost) * 1_000_000;
|
|
37
|
+
const c = parseFloat(completionCost) * 1_000_000;
|
|
38
|
+
if (p === 0 && c === 0)
|
|
39
|
+
return "FREE";
|
|
40
|
+
return `$${p.toFixed(2)}/$${c.toFixed(2)} per 1M tokens (in/out)`;
|
|
41
|
+
}
|
|
42
|
+
// ─── Provider ───────────────────────────────────────────────────────────────
|
|
43
|
+
async function fetchOpenRouterModels() {
|
|
44
|
+
const headers = { "Content-Type": "application/json" };
|
|
45
|
+
const apiKey = process.env.OPENROUTER_API_KEY;
|
|
46
|
+
if (apiKey)
|
|
47
|
+
headers["Authorization"] = `Bearer ${apiKey}`;
|
|
48
|
+
const response = await fetch("https://openrouter.ai/api/v1/models", { headers });
|
|
49
|
+
if (!response.ok) {
|
|
50
|
+
throw new Error(`OpenRouter API error: ${response.status} ${response.statusText}`);
|
|
51
|
+
}
|
|
52
|
+
const data = await response.json();
|
|
53
|
+
const models = data.data;
|
|
54
|
+
return models.map((m) => ({
|
|
55
|
+
id: m.id,
|
|
56
|
+
name: m.name,
|
|
57
|
+
provider: "openrouter",
|
|
58
|
+
description: m.description ?? "",
|
|
59
|
+
category: deriveCategory(m),
|
|
60
|
+
pricing: {
|
|
61
|
+
unit: "token",
|
|
62
|
+
unitPrice: parseFloat(m.pricing.prompt),
|
|
63
|
+
inputPrice: parseFloat(m.pricing.prompt),
|
|
64
|
+
outputPrice: parseFloat(m.pricing.completion),
|
|
65
|
+
formatted: formatLLMPricing(m.pricing.prompt, m.pricing.completion),
|
|
66
|
+
},
|
|
67
|
+
capabilities: deriveCapabilities(m),
|
|
68
|
+
addedDate: m.created ? new Date(m.created * 1000).toISOString() : undefined,
|
|
69
|
+
contextLength: m.context_length,
|
|
70
|
+
maxOutputTokens: m.top_provider?.max_completion_tokens ?? undefined,
|
|
71
|
+
tags: m.supported_parameters,
|
|
72
|
+
}));
|
|
73
|
+
}
|
|
74
|
+
export const openrouterProvider = {
|
|
75
|
+
name: "openrouter",
|
|
76
|
+
fetchModels: () => cached("openrouter", fetchOpenRouterModels),
|
|
77
|
+
};
|
|
78
|
+
//# sourceMappingURL=openrouter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openrouter.js","sourceRoot":"","sources":["../../src/providers/openrouter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { UnifiedModel } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Fetches all models from all providers, merges, and applies quality tiers.
|
|
4
|
+
* Provider failures are logged but don't break the entire fetch.
|
|
5
|
+
*/
|
|
6
|
+
export declare function getAllModels(): Promise<UnifiedModel[]>;
|
|
7
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/providers/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAiB,MAAM,aAAa,CAAC;AAO/D;;;GAGG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC,CAkB5D"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { openrouterProvider } from "./openrouter.js";
|
|
2
|
+
import { falProvider } from "./fal.js";
|
|
3
|
+
import { applyQualityTiers } from "../data/quality.js";
|
|
4
|
+
const providers = [openrouterProvider, falProvider];
|
|
5
|
+
/**
|
|
6
|
+
* Fetches all models from all providers, merges, and applies quality tiers.
|
|
7
|
+
* Provider failures are logged but don't break the entire fetch.
|
|
8
|
+
*/
|
|
9
|
+
export async function getAllModels() {
|
|
10
|
+
const results = await Promise.allSettled(providers.map((p) => p.fetchModels()));
|
|
11
|
+
const models = [];
|
|
12
|
+
for (let i = 0; i < results.length; i++) {
|
|
13
|
+
const result = results[i];
|
|
14
|
+
if (result.status === "fulfilled") {
|
|
15
|
+
models.push(...result.value);
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
console.error(`[model-advisor] Failed to fetch from ${providers[i].name}: ${result.reason}`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return applyQualityTiers(models);
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/providers/registry.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAEvD,MAAM,SAAS,GAAoB,CAAC,kBAAkB,EAAE,WAAW,CAAC,CAAC;AAErE;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY;IAChC,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,CACtC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CACtC,CAAC;IAEF,MAAM,MAAM,GAAmB,EAAE,CAAC;IAClC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACxC,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;QAC1B,IAAI,MAAM,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;YAClC,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QAC/B,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,KAAK,CACX,wCAAwC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,MAAM,EAAE,CAC9E,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,iBAAiB,CAAC,MAAM,CAAC,CAAC;AACnC,CAAC"}
|