language-models 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +5 -0
- package/.turbo/turbo-test.log +18 -0
- package/README.md +56 -142
- package/data/models.json +18805 -0
- package/dist/aliases.d.ts +5 -1
- package/dist/aliases.d.ts.map +1 -0
- package/dist/aliases.js +40 -4
- package/dist/aliases.js.map +1 -0
- package/dist/data/models.json +18805 -0
- package/dist/index.d.ts +10 -7
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -7
- package/dist/index.js.map +1 -0
- package/dist/models.d.ts +94 -167
- package/dist/models.d.ts.map +1 -0
- package/dist/models.js +109 -69803
- package/dist/models.js.map +1 -0
- package/package.json +25 -23
- package/scripts/fetch-models.ts +115 -0
- package/src/aliases.test.ts +319 -0
- package/src/aliases.ts +48 -4
- package/src/index.test.ts +400 -0
- package/src/index.ts +20 -9
- package/src/models.test.ts +392 -0
- package/src/models.ts +174 -0
- package/tsconfig.json +5 -15
- package/vitest.config.ts +4 -14
- package/.editorconfig +0 -10
- package/.gitattributes +0 -4
- package/.releaserc.js +0 -129
- package/LICENSE +0 -21
- package/dist/parser.d.ts +0 -86
- package/dist/parser.js +0 -390
- package/dist/providers.d.ts +0 -1
- package/dist/providers.js +0 -76
- package/dist/types.d.ts +0 -127
- package/dist/types.js +0 -1
- package/eslint.config.js +0 -3
- package/generate/build-models.ts +0 -150
- package/generate/overwrites.ts +0 -12
- package/publish.js +0 -32
- package/roadmap.md +0 -54
- package/src/models.d.ts +0 -170
- package/src/models.js +0 -70434
- package/src/parser.ts +0 -485
- package/src/providers.ts +0 -79
- package/src/types.ts +0 -135
- package/tests/parser.test.ts +0 -11
- package/tests/regex.test.ts +0 -42
- package/tests/selector.test.ts +0 -53
- package/tests/setup.ts +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"models.js","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,32 +1,34 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "language-models",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Model listing and resolution for LLM providers",
|
|
5
|
+
"type": "module",
|
|
5
6
|
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
6
14
|
"scripts": {
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"generate:models": "npx tsx generate/build-models.ts",
|
|
10
|
-
"clean": "rimraf dist",
|
|
11
|
-
"lint": "tsc --noEmit",
|
|
12
|
-
"prepublishOnly": "pnpm clean && pnpm build",
|
|
15
|
+
"build": "tsc -p tsconfig.json && cp -r data dist/",
|
|
16
|
+
"dev": "tsc -p tsconfig.json --watch",
|
|
13
17
|
"test": "vitest",
|
|
14
|
-
"typecheck": "tsc --noEmit"
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
"devDependencies": {
|
|
19
|
-
"@types/node": "^22.13.10",
|
|
20
|
-
"rimraf": "^6.0.1",
|
|
21
|
-
"typescript": "^5.8.2",
|
|
22
|
-
"vitest": "^3.0.9",
|
|
23
|
-
"tsconfig": "0.0.0",
|
|
24
|
-
"eslint-config": "0.1.0"
|
|
18
|
+
"typecheck": "tsc --noEmit",
|
|
19
|
+
"lint": "eslint .",
|
|
20
|
+
"clean": "rm -rf dist",
|
|
21
|
+
"fetch-models": "npx tsx scripts/fetch-models.ts"
|
|
25
22
|
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"ai",
|
|
25
|
+
"llm",
|
|
26
|
+
"language-models",
|
|
27
|
+
"openrouter",
|
|
28
|
+
"primitives"
|
|
29
|
+
],
|
|
30
|
+
"license": "MIT",
|
|
26
31
|
"dependencies": {
|
|
27
|
-
"
|
|
28
|
-
"camelcase": "^8.0.0",
|
|
29
|
-
"flat": "^6.0.1",
|
|
30
|
-
"vitest": "^3.0.9"
|
|
32
|
+
"rpc.do": "^0.1.0"
|
|
31
33
|
}
|
|
32
34
|
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
#!/usr/bin/env npx tsx
|
|
2
|
+
/**
|
|
3
|
+
* Fetch models from OpenRouter API and save to data/models.json
|
|
4
|
+
*
|
|
5
|
+
* Fetches from both:
|
|
6
|
+
* - /api/v1/models - Basic model info
|
|
7
|
+
* - /api/frontend/models - Provider routing info (provider_model_id, endpoint)
|
|
8
|
+
*
|
|
9
|
+
* Usage: npx tsx scripts/fetch-models.ts
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { writeFileSync } from 'fs'
|
|
13
|
+
import { dirname, join } from 'path'
|
|
14
|
+
import { fileURLToPath } from 'url'
|
|
15
|
+
|
|
16
|
+
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
17
|
+
const OUTPUT_PATH = join(__dirname, '..', 'data', 'models.json')
|
|
18
|
+
|
|
19
|
+
interface FrontendModel {
|
|
20
|
+
slug: string
|
|
21
|
+
endpoint?: {
|
|
22
|
+
provider_model_id?: string
|
|
23
|
+
provider_slug?: string
|
|
24
|
+
provider_info?: {
|
|
25
|
+
baseUrl?: string
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function fetchModels() {
|
|
31
|
+
console.log('Fetching models from OpenRouter API...')
|
|
32
|
+
|
|
33
|
+
// Fetch basic model info
|
|
34
|
+
const modelsResponse = await fetch('https://openrouter.ai/api/v1/models')
|
|
35
|
+
if (!modelsResponse.ok) {
|
|
36
|
+
throw new Error(`Failed to fetch models: ${modelsResponse.status} ${modelsResponse.statusText}`)
|
|
37
|
+
}
|
|
38
|
+
const modelsData = await modelsResponse.json()
|
|
39
|
+
const models = modelsData.data || modelsData
|
|
40
|
+
|
|
41
|
+
console.log(`Found ${models.length} models from v1 API`)
|
|
42
|
+
|
|
43
|
+
// Fetch frontend models for provider routing info
|
|
44
|
+
console.log('Fetching provider routing info from frontend API...')
|
|
45
|
+
let frontendModels: FrontendModel[] = []
|
|
46
|
+
try {
|
|
47
|
+
const frontendResponse = await fetch('https://openrouter.ai/api/frontend/models')
|
|
48
|
+
if (frontendResponse.ok) {
|
|
49
|
+
const frontendData = await frontendResponse.json()
|
|
50
|
+
frontendModels = Array.isArray(frontendData) ? frontendData : (frontendData.data || [])
|
|
51
|
+
console.log(`Found ${frontendModels.length} models from frontend API`)
|
|
52
|
+
}
|
|
53
|
+
} catch (err) {
|
|
54
|
+
console.warn('Could not fetch frontend models, continuing without provider routing info')
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Create a map of frontend models by slug for quick lookup
|
|
58
|
+
const frontendMap = new Map<string, FrontendModel>()
|
|
59
|
+
for (const fm of frontendModels) {
|
|
60
|
+
if (fm.slug) {
|
|
61
|
+
frontendMap.set(fm.slug, fm)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Merge frontend data into models
|
|
66
|
+
let enrichedCount = 0
|
|
67
|
+
for (const model of models) {
|
|
68
|
+
const frontend = frontendMap.get(model.id)
|
|
69
|
+
if (frontend?.endpoint) {
|
|
70
|
+
// Add provider routing info from nested endpoint
|
|
71
|
+
if (frontend.endpoint.provider_model_id) {
|
|
72
|
+
model.provider_model_id = frontend.endpoint.provider_model_id
|
|
73
|
+
}
|
|
74
|
+
if (frontend.endpoint.provider_slug) {
|
|
75
|
+
model.provider = frontend.endpoint.provider_slug
|
|
76
|
+
}
|
|
77
|
+
if (frontend.endpoint.provider_info?.baseUrl) {
|
|
78
|
+
model.endpoint = {
|
|
79
|
+
baseUrl: frontend.endpoint.provider_info.baseUrl,
|
|
80
|
+
modelId: frontend.endpoint.provider_model_id || model.id.split('/')[1]
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
enrichedCount++
|
|
84
|
+
} else {
|
|
85
|
+
// Extract provider from ID as fallback
|
|
86
|
+
const slashIndex = model.id.indexOf('/')
|
|
87
|
+
if (slashIndex > 0) {
|
|
88
|
+
model.provider = model.id.substring(0, slashIndex)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
console.log(`Enriched ${enrichedCount} models with provider routing info`)
|
|
94
|
+
|
|
95
|
+
// Sort by created date (newest first)
|
|
96
|
+
models.sort((a: any, b: any) => (b.created || 0) - (a.created || 0))
|
|
97
|
+
|
|
98
|
+
writeFileSync(OUTPUT_PATH, JSON.stringify(models, null, 2))
|
|
99
|
+
console.log(`Saved to ${OUTPUT_PATH}`)
|
|
100
|
+
|
|
101
|
+
// Print some stats
|
|
102
|
+
const providers = new Set(models.map((m: any) => m.id.split('/')[0]))
|
|
103
|
+
console.log(`\nProviders: ${[...providers].join(', ')}`)
|
|
104
|
+
|
|
105
|
+
// Show which direct providers have routing info
|
|
106
|
+
const directProviders = ['openai', 'anthropic', 'google']
|
|
107
|
+
for (const provider of directProviders) {
|
|
108
|
+
const withRouting = models.filter((m: any) =>
|
|
109
|
+
m.id.startsWith(`${provider}/`) && m.provider_model_id
|
|
110
|
+
)
|
|
111
|
+
console.log(`${provider}: ${withRouting.length} models with provider_model_id`)
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
fetchModels().catch(console.error)
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for model aliases
|
|
3
|
+
*
|
|
4
|
+
* These tests verify the alias mapping and ensure all documented
|
|
5
|
+
* aliases are present and correctly mapped.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { describe, it, expect } from 'vitest'
|
|
9
|
+
import { ALIASES } from './aliases.js'
|
|
10
|
+
|
|
11
|
+
describe('ALIASES', () => {
|
|
12
|
+
it('is an object', () => {
|
|
13
|
+
expect(typeof ALIASES).toBe('object')
|
|
14
|
+
expect(ALIASES).not.toBeNull()
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
it('has string keys and values', () => {
|
|
18
|
+
for (const [key, value] of Object.entries(ALIASES)) {
|
|
19
|
+
expect(typeof key).toBe('string')
|
|
20
|
+
expect(typeof value).toBe('string')
|
|
21
|
+
}
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('has no empty keys or values', () => {
|
|
25
|
+
for (const [key, value] of Object.entries(ALIASES)) {
|
|
26
|
+
expect(key.length).toBeGreaterThan(0)
|
|
27
|
+
expect(value.length).toBeGreaterThan(0)
|
|
28
|
+
}
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('all values are valid model IDs with provider prefix', () => {
|
|
32
|
+
for (const [key, value] of Object.entries(ALIASES)) {
|
|
33
|
+
expect(value).toContain('/')
|
|
34
|
+
const [provider, modelName] = value.split('/')
|
|
35
|
+
expect(provider.length).toBeGreaterThan(0)
|
|
36
|
+
expect(modelName.length).toBeGreaterThan(0)
|
|
37
|
+
}
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
describe('Claude (Anthropic) aliases', () => {
|
|
41
|
+
it('has opus alias', () => {
|
|
42
|
+
expect(ALIASES['opus']).toBe('anthropic/claude-opus-4.5')
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('has sonnet alias', () => {
|
|
46
|
+
expect(ALIASES['sonnet']).toBe('anthropic/claude-sonnet-4.5')
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('has haiku alias', () => {
|
|
50
|
+
expect(ALIASES['haiku']).toBe('anthropic/claude-haiku-4.5')
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('has claude default alias', () => {
|
|
54
|
+
expect(ALIASES['claude']).toBe('anthropic/claude-sonnet-4.5')
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it('claude aliases point to anthropic provider', () => {
|
|
58
|
+
expect(ALIASES['opus']).toContain('anthropic/')
|
|
59
|
+
expect(ALIASES['sonnet']).toContain('anthropic/')
|
|
60
|
+
expect(ALIASES['haiku']).toContain('anthropic/')
|
|
61
|
+
expect(ALIASES['claude']).toContain('anthropic/')
|
|
62
|
+
})
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
describe('GPT (OpenAI) aliases', () => {
|
|
66
|
+
it('has gpt alias', () => {
|
|
67
|
+
expect(ALIASES['gpt']).toBe('openai/gpt-4o')
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
it('has gpt-4o alias', () => {
|
|
71
|
+
expect(ALIASES['gpt-4o']).toBe('openai/gpt-4o')
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
it('has gpt-4o-mini alias', () => {
|
|
75
|
+
expect(ALIASES['gpt-4o-mini']).toBe('openai/gpt-4o-mini')
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
it('has 4o shorthand', () => {
|
|
79
|
+
expect(ALIASES['4o']).toBe('openai/gpt-4o')
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
it('has o1 alias', () => {
|
|
83
|
+
expect(ALIASES['o1']).toBe('openai/o1')
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
it('has o3 alias', () => {
|
|
87
|
+
expect(ALIASES['o3']).toBe('openai/o3')
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
it('has o3-mini alias', () => {
|
|
91
|
+
expect(ALIASES['o3-mini']).toBe('openai/o3-mini')
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
it('has o4-mini alias', () => {
|
|
95
|
+
expect(ALIASES['o4-mini']).toBe('openai/o4-mini')
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
it('openai aliases point to openai provider', () => {
|
|
99
|
+
expect(ALIASES['gpt']).toContain('openai/')
|
|
100
|
+
expect(ALIASES['gpt-4o']).toContain('openai/')
|
|
101
|
+
expect(ALIASES['4o']).toContain('openai/')
|
|
102
|
+
expect(ALIASES['o1']).toContain('openai/')
|
|
103
|
+
expect(ALIASES['o3']).toContain('openai/')
|
|
104
|
+
})
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
describe('Gemini (Google) aliases', () => {
|
|
108
|
+
it('has gemini alias', () => {
|
|
109
|
+
expect(ALIASES['gemini']).toBe('google/gemini-2.5-flash')
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
it('has flash alias', () => {
|
|
113
|
+
expect(ALIASES['flash']).toBe('google/gemini-2.5-flash')
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
it('has gemini-flash alias', () => {
|
|
117
|
+
expect(ALIASES['gemini-flash']).toBe('google/gemini-2.5-flash')
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
it('has gemini-pro alias', () => {
|
|
121
|
+
expect(ALIASES['gemini-pro']).toBe('google/gemini-2.5-pro')
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
it('google aliases point to google provider', () => {
|
|
125
|
+
expect(ALIASES['gemini']).toContain('google/')
|
|
126
|
+
expect(ALIASES['flash']).toContain('google/')
|
|
127
|
+
expect(ALIASES['gemini-flash']).toContain('google/')
|
|
128
|
+
expect(ALIASES['gemini-pro']).toContain('google/')
|
|
129
|
+
})
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
describe('Llama (Meta) aliases', () => {
|
|
133
|
+
it('has llama alias', () => {
|
|
134
|
+
expect(ALIASES['llama']).toBe('meta-llama/llama-4-maverick')
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
it('has llama-4 alias', () => {
|
|
138
|
+
expect(ALIASES['llama-4']).toBe('meta-llama/llama-4-maverick')
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
it('has llama-70b alias', () => {
|
|
142
|
+
expect(ALIASES['llama-70b']).toBe('meta-llama/llama-3.3-70b-instruct')
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
it('llama aliases point to meta-llama provider', () => {
|
|
146
|
+
expect(ALIASES['llama']).toContain('meta-llama/')
|
|
147
|
+
expect(ALIASES['llama-4']).toContain('meta-llama/')
|
|
148
|
+
expect(ALIASES['llama-70b']).toContain('meta-llama/')
|
|
149
|
+
})
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
describe('DeepSeek aliases', () => {
|
|
153
|
+
it('has deepseek alias', () => {
|
|
154
|
+
expect(ALIASES['deepseek']).toBe('deepseek/deepseek-chat')
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
it('has r1 alias', () => {
|
|
158
|
+
expect(ALIASES['r1']).toBe('deepseek/deepseek-r1')
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
it('deepseek aliases point to deepseek provider', () => {
|
|
162
|
+
expect(ALIASES['deepseek']).toContain('deepseek/')
|
|
163
|
+
expect(ALIASES['r1']).toContain('deepseek/')
|
|
164
|
+
})
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
describe('Mistral aliases', () => {
|
|
168
|
+
it('has mistral alias', () => {
|
|
169
|
+
expect(ALIASES['mistral']).toBe('mistralai/mistral-large-2411')
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
it('has codestral alias', () => {
|
|
173
|
+
expect(ALIASES['codestral']).toBe('mistralai/codestral-2501')
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
it('mistral aliases point to mistralai provider', () => {
|
|
177
|
+
expect(ALIASES['mistral']).toContain('mistralai/')
|
|
178
|
+
expect(ALIASES['codestral']).toContain('mistralai/')
|
|
179
|
+
})
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
describe('Qwen aliases', () => {
|
|
183
|
+
it('has qwen alias', () => {
|
|
184
|
+
expect(ALIASES['qwen']).toBe('qwen/qwen3-235b-a22b')
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
it('qwen alias points to qwen provider', () => {
|
|
188
|
+
expect(ALIASES['qwen']).toContain('qwen/')
|
|
189
|
+
})
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
describe('Grok (X.AI) aliases', () => {
|
|
193
|
+
it('has grok alias', () => {
|
|
194
|
+
expect(ALIASES['grok']).toBe('x-ai/grok-3')
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
it('grok alias points to x-ai provider', () => {
|
|
198
|
+
expect(ALIASES['grok']).toContain('x-ai/')
|
|
199
|
+
})
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
describe('Perplexity aliases', () => {
|
|
203
|
+
it('has sonar alias', () => {
|
|
204
|
+
expect(ALIASES['sonar']).toBe('perplexity/sonar-pro')
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
it('sonar alias points to perplexity provider', () => {
|
|
208
|
+
expect(ALIASES['sonar']).toContain('perplexity/')
|
|
209
|
+
})
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
describe('alias uniqueness', () => {
|
|
213
|
+
it('has no duplicate keys', () => {
|
|
214
|
+
const keys = Object.keys(ALIASES)
|
|
215
|
+
const uniqueKeys = new Set(keys)
|
|
216
|
+
expect(keys.length).toBe(uniqueKeys.size)
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
it('has unique lowercase keys', () => {
|
|
220
|
+
const lowerKeys = Object.keys(ALIASES).map(k => k.toLowerCase())
|
|
221
|
+
const uniqueLowerKeys = new Set(lowerKeys)
|
|
222
|
+
expect(lowerKeys.length).toBe(uniqueLowerKeys.size)
|
|
223
|
+
})
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
describe('alias conventions', () => {
|
|
227
|
+
it('uses lowercase keys', () => {
|
|
228
|
+
for (const key of Object.keys(ALIASES)) {
|
|
229
|
+
expect(key).toBe(key.toLowerCase())
|
|
230
|
+
}
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
it('uses lowercase provider names in values', () => {
|
|
234
|
+
for (const value of Object.values(ALIASES)) {
|
|
235
|
+
const provider = value.split('/')[0]
|
|
236
|
+
expect(provider).toBe(provider.toLowerCase())
|
|
237
|
+
}
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
it('uses kebab-case or lowercase for model names', () => {
|
|
241
|
+
for (const value of Object.values(ALIASES)) {
|
|
242
|
+
const modelName = value.split('/')[1]
|
|
243
|
+
// Model names should not have uppercase letters or spaces
|
|
244
|
+
expect(modelName).not.toMatch(/[A-Z]/)
|
|
245
|
+
expect(modelName).not.toContain(' ')
|
|
246
|
+
}
|
|
247
|
+
})
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
describe('provider coverage', () => {
|
|
251
|
+
it('covers major AI providers', () => {
|
|
252
|
+
const providers = new Set(
|
|
253
|
+
Object.values(ALIASES).map(v => v.split('/')[0])
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
expect(providers.has('anthropic')).toBe(true)
|
|
257
|
+
expect(providers.has('openai')).toBe(true)
|
|
258
|
+
expect(providers.has('google')).toBe(true)
|
|
259
|
+
expect(providers.has('meta-llama')).toBe(true)
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
it('has multiple aliases per major provider', () => {
|
|
263
|
+
const providerCounts: Record<string, number> = {}
|
|
264
|
+
|
|
265
|
+
for (const value of Object.values(ALIASES)) {
|
|
266
|
+
const provider = value.split('/')[0]
|
|
267
|
+
providerCounts[provider] = (providerCounts[provider] || 0) + 1
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Major providers should have multiple aliases
|
|
271
|
+
expect(providerCounts['anthropic']).toBeGreaterThanOrEqual(3)
|
|
272
|
+
expect(providerCounts['openai']).toBeGreaterThanOrEqual(5)
|
|
273
|
+
expect(providerCounts['google']).toBeGreaterThanOrEqual(3)
|
|
274
|
+
})
|
|
275
|
+
})
|
|
276
|
+
|
|
277
|
+
describe('README documentation alignment', () => {
|
|
278
|
+
it('matches all aliases documented in README', () => {
|
|
279
|
+
// These are the aliases listed in the README.md table
|
|
280
|
+
const documentedAliases = {
|
|
281
|
+
'opus': 'anthropic/claude-opus-4.5',
|
|
282
|
+
'sonnet': 'anthropic/claude-sonnet-4.5',
|
|
283
|
+
'haiku': 'anthropic/claude-haiku-4.5',
|
|
284
|
+
'claude': 'anthropic/claude-sonnet-4.5',
|
|
285
|
+
'gpt': 'openai/gpt-4o',
|
|
286
|
+
'gpt-4o': 'openai/gpt-4o',
|
|
287
|
+
'4o': 'openai/gpt-4o',
|
|
288
|
+
'o1': 'openai/o1',
|
|
289
|
+
'o3': 'openai/o3',
|
|
290
|
+
'o3-mini': 'openai/o3-mini',
|
|
291
|
+
'gemini': 'google/gemini-2.5-flash',
|
|
292
|
+
'flash': 'google/gemini-2.5-flash',
|
|
293
|
+
'gemini-pro': 'google/gemini-2.5-pro',
|
|
294
|
+
'llama': 'meta-llama/llama-4-maverick',
|
|
295
|
+
'llama-4': 'meta-llama/llama-4-maverick',
|
|
296
|
+
'llama-70b': 'meta-llama/llama-3.3-70b-instruct',
|
|
297
|
+
'mistral': 'mistralai/mistral-large-2411',
|
|
298
|
+
'codestral': 'mistralai/codestral-2501',
|
|
299
|
+
'deepseek': 'deepseek/deepseek-chat',
|
|
300
|
+
'r1': 'deepseek/deepseek-r1',
|
|
301
|
+
'qwen': 'qwen/qwen3-235b-a22b',
|
|
302
|
+
'grok': 'x-ai/grok-3',
|
|
303
|
+
'sonar': 'perplexity/sonar-pro',
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
for (const [alias, expectedId] of Object.entries(documentedAliases)) {
|
|
307
|
+
expect(ALIASES[alias]).toBe(expectedId)
|
|
308
|
+
}
|
|
309
|
+
})
|
|
310
|
+
})
|
|
311
|
+
|
|
312
|
+
describe('alias count', () => {
|
|
313
|
+
it('has reasonable number of aliases', () => {
|
|
314
|
+
const count = Object.keys(ALIASES).length
|
|
315
|
+
expect(count).toBeGreaterThanOrEqual(20)
|
|
316
|
+
expect(count).toBeLessThanOrEqual(100)
|
|
317
|
+
})
|
|
318
|
+
})
|
|
319
|
+
})
|
package/src/aliases.ts
CHANGED
|
@@ -1,5 +1,49 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Model aliases - map simple names to full model IDs
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export const ALIASES: Record<string, string> = {
|
|
6
|
+
// Claude (Anthropic)
|
|
7
|
+
'opus': 'anthropic/claude-opus-4.5',
|
|
8
|
+
'sonnet': 'anthropic/claude-sonnet-4.5',
|
|
9
|
+
'haiku': 'anthropic/claude-haiku-4.5',
|
|
10
|
+
'claude': 'anthropic/claude-sonnet-4.5',
|
|
11
|
+
|
|
12
|
+
// GPT (OpenAI)
|
|
13
|
+
'gpt': 'openai/gpt-4o',
|
|
14
|
+
'gpt-4o': 'openai/gpt-4o',
|
|
15
|
+
'gpt-4o-mini': 'openai/gpt-4o-mini',
|
|
16
|
+
'4o': 'openai/gpt-4o',
|
|
17
|
+
'o1': 'openai/o1',
|
|
18
|
+
'o3': 'openai/o3',
|
|
19
|
+
'o3-mini': 'openai/o3-mini',
|
|
20
|
+
'o4-mini': 'openai/o4-mini',
|
|
21
|
+
|
|
22
|
+
// Gemini (Google)
|
|
23
|
+
'gemini': 'google/gemini-2.5-flash',
|
|
24
|
+
'flash': 'google/gemini-2.5-flash',
|
|
25
|
+
'gemini-flash': 'google/gemini-2.5-flash',
|
|
26
|
+
'gemini-pro': 'google/gemini-2.5-pro',
|
|
27
|
+
|
|
28
|
+
// Llama (Meta)
|
|
29
|
+
'llama': 'meta-llama/llama-4-maverick',
|
|
30
|
+
'llama-4': 'meta-llama/llama-4-maverick',
|
|
31
|
+
'llama-70b': 'meta-llama/llama-3.3-70b-instruct',
|
|
32
|
+
|
|
33
|
+
// DeepSeek
|
|
34
|
+
'deepseek': 'deepseek/deepseek-chat',
|
|
35
|
+
'r1': 'deepseek/deepseek-r1',
|
|
36
|
+
|
|
37
|
+
// Mistral
|
|
38
|
+
'mistral': 'mistralai/mistral-large-2411',
|
|
39
|
+
'codestral': 'mistralai/codestral-2501',
|
|
40
|
+
|
|
41
|
+
// Qwen
|
|
42
|
+
'qwen': 'qwen/qwen3-235b-a22b',
|
|
43
|
+
|
|
44
|
+
// Grok
|
|
45
|
+
'grok': 'x-ai/grok-3',
|
|
46
|
+
|
|
47
|
+
// Perplexity
|
|
48
|
+
'sonar': 'perplexity/sonar-pro',
|
|
5
49
|
}
|