language-models 2.1.3 → 2.3.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 (54) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +26 -0
  3. package/README.md +2 -0
  4. package/dist/index.d.ts +3 -1
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +13 -1
  7. package/dist/index.js.map +1 -1
  8. package/dist/models.d.ts +1 -1
  9. package/dist/models.d.ts.map +1 -1
  10. package/dist/models.js +8 -10
  11. package/dist/models.js.map +1 -1
  12. package/dist/policy.d.ts +127 -0
  13. package/dist/policy.d.ts.map +1 -0
  14. package/dist/policy.js +246 -0
  15. package/dist/policy.js.map +1 -0
  16. package/dist/pricing/index.d.ts +19 -0
  17. package/dist/pricing/index.d.ts.map +1 -0
  18. package/dist/pricing/index.js +18 -0
  19. package/dist/pricing/index.js.map +1 -0
  20. package/dist/pricing/lookup.d.ts +46 -0
  21. package/dist/pricing/lookup.d.ts.map +1 -0
  22. package/dist/pricing/lookup.js +94 -0
  23. package/dist/pricing/lookup.js.map +1 -0
  24. package/dist/pricing/table.d.ts +46 -0
  25. package/dist/pricing/table.d.ts.map +1 -0
  26. package/dist/pricing/table.js +214 -0
  27. package/dist/pricing/table.js.map +1 -0
  28. package/dist/pricing/types.d.ts +84 -0
  29. package/dist/pricing/types.d.ts.map +1 -0
  30. package/dist/pricing/types.js +32 -0
  31. package/dist/pricing/types.js.map +1 -0
  32. package/package.json +16 -12
  33. package/src/index.ts +42 -1
  34. package/src/models.ts +8 -12
  35. package/src/policy.ts +343 -0
  36. package/src/pricing/index.ts +29 -0
  37. package/src/pricing/lookup.ts +124 -0
  38. package/src/pricing/table.ts +235 -0
  39. package/src/pricing/types.ts +90 -0
  40. package/{src → test}/aliases.test.ts +20 -22
  41. package/{src → test}/index.test.ts +9 -9
  42. package/{src → test}/models.test.ts +8 -6
  43. package/test/policy.test.ts +203 -0
  44. package/test/pricing.test.ts +279 -0
  45. package/vitest.config.ts +21 -1
  46. package/.turbo/turbo-test.log +0 -18
  47. package/LICENSE +0 -21
  48. package/src/aliases.js +0 -40
  49. package/src/aliases.test.js +0 -264
  50. package/src/index.js +0 -9
  51. package/src/index.test.js +0 -320
  52. package/src/models.js +0 -108
  53. package/src/models.test.js +0 -335
  54. package/vitest.config.js +0 -10
@@ -0,0 +1,235 @@
1
+ /**
2
+ * language-models / pricing — canonical model pricing table.
3
+ *
4
+ * All rates are USD per 1,000,000 tokens (per1M form, NOT per1k). Sourced
5
+ * from public list prices on 2026-05-07. Where prior repo tables disagree,
6
+ * the most-recently-updated source wins:
7
+ *
8
+ * - **Vertex Gemini 3.x**: startup-builder/packages/llm-vertex-batch
9
+ * (sb-srnl 2026-05-07 verified) is canonical for the flex/batch tier.
10
+ * startup-builder/packages/llm-vertex (same date) is canonical for
11
+ * standard interactive pricing.
12
+ * - **Vertex Gemini 2.5**: startup-builder/llm-vertex-batch is canonical
13
+ * (icps's `gemini-2.5-flash` rates were ~4× off — likely transposed
14
+ * from a different SKU).
15
+ * - **Bedrock Anthropic**: startup-builder/packages/llm-bedrock is the
16
+ * most-recently-curated table (matches AWS Bedrock public pricing on
17
+ * 2026-05-07).
18
+ * - **Embeddings**: icps/packages/llm/pricing.ts is the source for
19
+ * `gemini-embedding-2`. Bedrock has no first-party Gemini embedding
20
+ * SKU; Anthropic has no embedding SKU. Embedding lives under
21
+ * `aistudio/` since the cheapest path is the AI Studio API key (icps's
22
+ * 2026-05-07 fallback chain — the path startup-builder also uses).
23
+ *
24
+ * 200K-context tier breakpoint:
25
+ *
26
+ * Gemini 3.1 Pro Preview list pricing has a 2× rate above 200K input
27
+ * tokens. We model this with `contextTierBreakpoint: 200_000` +
28
+ * `contextTierAbove`. Anthropic Claude has flat pricing (no breakpoint).
29
+ * Gemini 2.5 + 3.x flash-lite + embedding SKUs are flat too.
30
+ *
31
+ * Adding a new SKU:
32
+ *
33
+ * 1. Append a new row keyed on `<provider>/<short-slug>` + `tier`. Both
34
+ * fields together form the lookup key; duplicates are caught by the
35
+ * table-integrity test.
36
+ * 2. If the SKU has a context-tier breakpoint, add
37
+ * `contextTierBreakpoint` + `contextTierAbove`. Otherwise omit them.
38
+ * 3. If both standard + batch tiers exist, add BOTH rows; downstream
39
+ * consumers select via `priceFor({ tier: ... })`. Vertex's batch tier
40
+ * is ~50% of standard; that's not enforced — declare the actual
41
+ * published rates.
42
+ * 4. Bump the package version (semver minor for additions).
43
+ */
44
+
45
+ import type { ModelPricing } from './types.js'
46
+
47
+ export const PRICING_TABLE: readonly ModelPricing[] = [
48
+ // ---------------------------------------------------------------------
49
+ // Vertex Gemini 3.x (200K-context breakpoint applies to 3.1 Pro)
50
+ // ---------------------------------------------------------------------
51
+
52
+ // gemini-3.1-pro-preview standard interactive
53
+ // ≤200K: $2/M in, $12/M out ; >200K: $4/M in, $18/M out
54
+ {
55
+ provider: 'vertex',
56
+ slug: 'vertex/gemini-3.1-pro',
57
+ tier: 'standard',
58
+ inputPer1M: 2.0,
59
+ outputPer1M: 12.0,
60
+ contextTierBreakpoint: 200_000,
61
+ contextTierAbove: { inputPer1M: 4.0, outputPer1M: 18.0 },
62
+ },
63
+
64
+ // gemini-3.1-pro-preview flex/batch (sb-srnl 2026-05-07 verified)
65
+ // ≤200K: $1/M in, $6/M out ; >200K: $2/M in, $9/M out
66
+ {
67
+ provider: 'vertex',
68
+ slug: 'vertex/gemini-3.1-pro',
69
+ tier: 'batch',
70
+ inputPer1M: 1.0,
71
+ outputPer1M: 6.0,
72
+ contextTierBreakpoint: 200_000,
73
+ contextTierAbove: { inputPer1M: 2.0, outputPer1M: 9.0 },
74
+ },
75
+
76
+ // gemini-3.1-flash-lite-preview standard
77
+ // Flat: $0.25/M in, $1.50/M out (no breakpoint per public table)
78
+ {
79
+ provider: 'vertex',
80
+ slug: 'vertex/gemini-3.1-flash-lite',
81
+ tier: 'standard',
82
+ inputPer1M: 0.25,
83
+ outputPer1M: 1.5,
84
+ },
85
+
86
+ // gemini-3.1-flash-lite-preview flex/batch
87
+ // Flat: $0.13/M in, $0.75/M out
88
+ {
89
+ provider: 'vertex',
90
+ slug: 'vertex/gemini-3.1-flash-lite',
91
+ tier: 'batch',
92
+ inputPer1M: 0.13,
93
+ outputPer1M: 0.75,
94
+ },
95
+
96
+ // gemini-3-pro-preview — placeholder pricing using 3.1 sibling rates
97
+ // (Vertex hasn't published separate 3.0 list prices as of 2026-05-07)
98
+ {
99
+ provider: 'vertex',
100
+ slug: 'vertex/gemini-3-pro',
101
+ tier: 'standard',
102
+ inputPer1M: 2.0,
103
+ outputPer1M: 12.0,
104
+ contextTierBreakpoint: 200_000,
105
+ contextTierAbove: { inputPer1M: 4.0, outputPer1M: 18.0 },
106
+ },
107
+ {
108
+ provider: 'vertex',
109
+ slug: 'vertex/gemini-3-pro',
110
+ tier: 'batch',
111
+ inputPer1M: 1.0,
112
+ outputPer1M: 6.0,
113
+ contextTierBreakpoint: 200_000,
114
+ contextTierAbove: { inputPer1M: 2.0, outputPer1M: 9.0 },
115
+ },
116
+
117
+ // gemini-3-flash-preview — placeholder using 3.1 flash-lite sibling rates
118
+ {
119
+ provider: 'vertex',
120
+ slug: 'vertex/gemini-3-flash',
121
+ tier: 'standard',
122
+ inputPer1M: 0.25,
123
+ outputPer1M: 1.5,
124
+ },
125
+ {
126
+ provider: 'vertex',
127
+ slug: 'vertex/gemini-3-flash',
128
+ tier: 'batch',
129
+ inputPer1M: 0.13,
130
+ outputPer1M: 0.75,
131
+ },
132
+
133
+ // ---------------------------------------------------------------------
134
+ // Vertex Gemini 2.5 (no context-tier breakpoint per public table)
135
+ // ---------------------------------------------------------------------
136
+
137
+ // gemini-2.5-pro standard: $1.25/M in, $10/M out
138
+ {
139
+ provider: 'vertex',
140
+ slug: 'vertex/gemini-2.5-pro',
141
+ tier: 'standard',
142
+ inputPer1M: 1.25,
143
+ outputPer1M: 10.0,
144
+ },
145
+
146
+ // gemini-2.5-pro batch: ~50% of standard
147
+ {
148
+ provider: 'vertex',
149
+ slug: 'vertex/gemini-2.5-pro',
150
+ tier: 'batch',
151
+ inputPer1M: 0.625,
152
+ outputPer1M: 5.0,
153
+ },
154
+
155
+ // gemini-2.5-flash standard: $0.075/M in, $0.30/M out
156
+ // (startup-builder/llm-vertex-batch source — supersedes icps's stale rate)
157
+ {
158
+ provider: 'vertex',
159
+ slug: 'vertex/gemini-2.5-flash',
160
+ tier: 'standard',
161
+ inputPer1M: 0.075,
162
+ outputPer1M: 0.3,
163
+ },
164
+
165
+ // gemini-2.5-flash batch: ~50% of standard
166
+ {
167
+ provider: 'vertex',
168
+ slug: 'vertex/gemini-2.5-flash',
169
+ tier: 'batch',
170
+ inputPer1M: 0.0375,
171
+ outputPer1M: 0.15,
172
+ },
173
+
174
+ // ---------------------------------------------------------------------
175
+ // Bedrock Anthropic Claude (flat pricing — no context-tier breakpoint)
176
+ // ---------------------------------------------------------------------
177
+
178
+ // claude-opus-4-7: $15/M in, $75/M out
179
+ {
180
+ provider: 'bedrock',
181
+ slug: 'bedrock/claude-opus-4-7',
182
+ tier: 'standard',
183
+ inputPer1M: 15.0,
184
+ outputPer1M: 75.0,
185
+ },
186
+
187
+ // claude-opus-4-6: same as 4-7
188
+ {
189
+ provider: 'bedrock',
190
+ slug: 'bedrock/claude-opus-4-6',
191
+ tier: 'standard',
192
+ inputPer1M: 15.0,
193
+ outputPer1M: 75.0,
194
+ },
195
+
196
+ // claude-sonnet-4-7: $3/M in, $15/M out
197
+ {
198
+ provider: 'bedrock',
199
+ slug: 'bedrock/claude-sonnet-4-7',
200
+ tier: 'standard',
201
+ inputPer1M: 3.0,
202
+ outputPer1M: 15.0,
203
+ },
204
+
205
+ // claude-sonnet-4-6: same as 4-7
206
+ {
207
+ provider: 'bedrock',
208
+ slug: 'bedrock/claude-sonnet-4-6',
209
+ tier: 'standard',
210
+ inputPer1M: 3.0,
211
+ outputPer1M: 15.0,
212
+ },
213
+
214
+ // claude-haiku-4-5: $1/M in, $5/M out
215
+ {
216
+ provider: 'bedrock',
217
+ slug: 'bedrock/claude-haiku-4-5',
218
+ tier: 'standard',
219
+ inputPer1M: 1.0,
220
+ outputPer1M: 5.0,
221
+ },
222
+
223
+ // ---------------------------------------------------------------------
224
+ // Google AI Studio embeddings
225
+ // ---------------------------------------------------------------------
226
+
227
+ // gemini-embedding-2: $0.15/M input (no output side — pure embedding SKU)
228
+ {
229
+ provider: 'google-ai-studio',
230
+ slug: 'aistudio/gemini-embedding-2',
231
+ tier: 'standard',
232
+ inputPer1M: 0.15,
233
+ outputPer1M: 0,
234
+ },
235
+ ]
@@ -0,0 +1,90 @@
1
+ /**
2
+ * language-models / pricing — type definitions for the canonical pricing table.
3
+ *
4
+ * Schema design notes (sb-ncer 2026-05-07):
5
+ *
6
+ * 1. **Provider** is the upstream API surface, not the model family. AWS
7
+ * Bedrock hosts Anthropic Claude, but `provider: 'bedrock'` (not
8
+ * `'anthropic'`) — same model, different cost when consumed via
9
+ * Anthropic's first-party API key (provider: 'anthropic' would be a
10
+ * DIFFERENT row with potentially different rates).
11
+ *
12
+ * 2. **Slug** is the caller-facing string: `<provider>/<short-name>`.
13
+ * Multiple slugs (the cascade short slug, the SDK-native id, etc.)
14
+ * can map to the same logical SKU — but for the canonical primitive
15
+ * we only carry the cascade short slug (`vertex/gemini-3.1-pro`,
16
+ * `bedrock/claude-opus-4-7`). Adapter packages can layer their own
17
+ * rewrite tables on top.
18
+ *
19
+ * 3. **Tier** distinguishes pricing modes for the same SKU:
20
+ * - `standard`: synchronous interactive pricing (full price)
21
+ * - `batch`: async batch-prediction pricing (typically 50% discount)
22
+ * - `flex`: flex-tier pricing (Vertex's name for batch, kept as alias)
23
+ * - `provisioned`: provisioned-throughput pricing (per-hour, not
24
+ * currently modeled — placeholder for future PT entries)
25
+ *
26
+ * 4. **contextTierBreakpoint + contextTierAbove**: Gemini 3.x SKUs apply
27
+ * a 2× rate above 200K input tokens; Anthropic Claude pricing is flat.
28
+ * Optional fields — when absent, the base rate applies regardless of
29
+ * input size.
30
+ */
31
+
32
+ export type Provider = 'vertex' | 'bedrock' | 'openai' | 'anthropic' | 'google-ai-studio'
33
+
34
+ export type PricingTier = 'standard' | 'batch' | 'flex' | 'provisioned'
35
+
36
+ /** Rates expressed in USD per 1,000,000 tokens. */
37
+ export interface RateBlock {
38
+ /** USD per 1M input tokens. */
39
+ readonly inputPer1M: number
40
+ /** USD per 1M output (completion) tokens. */
41
+ readonly outputPer1M: number
42
+ /**
43
+ * Optional USD per 1M cached input tokens (prompt-caching tier). When
44
+ * absent, callers should fall back to inputPer1M (no cache discount).
45
+ */
46
+ readonly cachedInputPer1M?: number
47
+ }
48
+
49
+ /**
50
+ * Single canonical pricing row. Identity is `(slug, tier)` — provider is
51
+ * derived from the slug prefix and stored explicitly only for tooling
52
+ * convenience.
53
+ */
54
+ export interface ModelPricing extends RateBlock {
55
+ readonly provider: Provider
56
+ readonly slug: string
57
+ readonly tier: PricingTier
58
+ /**
59
+ * Token count at which pricing changes (inclusive — i.e. inputs >=
60
+ * breakpoint use contextTierAbove). Currently only Gemini 3.x SKUs
61
+ * have a breakpoint (200_000). Anthropic Claude has flat pricing.
62
+ */
63
+ readonly contextTierBreakpoint?: number
64
+ /** Rates that apply when inputTokens >= contextTierBreakpoint. */
65
+ readonly contextTierAbove?: RateBlock
66
+ }
67
+
68
+ export interface PriceForArgs {
69
+ readonly slug: string
70
+ readonly tier: PricingTier
71
+ readonly inputTokens: number
72
+ readonly outputTokens: number
73
+ /**
74
+ * Optional cached input tokens (subset of inputTokens that hit a
75
+ * prompt-caching tier). Billed at cachedInputPer1M when the row
76
+ * defines it; otherwise at inputPer1M (i.e. no discount).
77
+ */
78
+ readonly cachedInputTokens?: number
79
+ }
80
+
81
+ export interface PriceForResult {
82
+ readonly inputUsd: number
83
+ readonly outputUsd: number
84
+ readonly totalUsd: number
85
+ }
86
+
87
+ export interface HasPricingArgs {
88
+ readonly slug: string
89
+ readonly tier: PricingTier
90
+ }
@@ -6,7 +6,7 @@
6
6
  */
7
7
 
8
8
  import { describe, it, expect } from 'vitest'
9
- import { ALIASES } from './aliases.js'
9
+ import { ALIASES } from '../src/aliases.js'
10
10
 
11
11
  describe('ALIASES', () => {
12
12
  it('is an object', () => {
@@ -217,7 +217,7 @@ describe('ALIASES', () => {
217
217
  })
218
218
 
219
219
  it('has unique lowercase keys', () => {
220
- const lowerKeys = Object.keys(ALIASES).map(k => k.toLowerCase())
220
+ const lowerKeys = Object.keys(ALIASES).map((k) => k.toLowerCase())
221
221
  const uniqueLowerKeys = new Set(lowerKeys)
222
222
  expect(lowerKeys.length).toBe(uniqueLowerKeys.size)
223
223
  })
@@ -249,9 +249,7 @@ describe('ALIASES', () => {
249
249
 
250
250
  describe('provider coverage', () => {
251
251
  it('covers major AI providers', () => {
252
- const providers = new Set(
253
- Object.values(ALIASES).map(v => v.split('/')[0])
254
- )
252
+ const providers = new Set(Object.values(ALIASES).map((v) => v.split('/')[0]))
255
253
 
256
254
  expect(providers.has('anthropic')).toBe(true)
257
255
  expect(providers.has('openai')).toBe(true)
@@ -278,29 +276,29 @@ describe('ALIASES', () => {
278
276
  it('matches all aliases documented in README', () => {
279
277
  // These are the aliases listed in the README.md table
280
278
  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',
279
+ opus: 'anthropic/claude-opus-4.5',
280
+ sonnet: 'anthropic/claude-sonnet-4.5',
281
+ haiku: 'anthropic/claude-haiku-4.5',
282
+ claude: 'anthropic/claude-sonnet-4.5',
283
+ gpt: 'openai/gpt-4o',
286
284
  'gpt-4o': 'openai/gpt-4o',
287
285
  '4o': 'openai/gpt-4o',
288
- 'o1': 'openai/o1',
289
- 'o3': 'openai/o3',
286
+ o1: 'openai/o1',
287
+ o3: 'openai/o3',
290
288
  'o3-mini': 'openai/o3-mini',
291
- 'gemini': 'google/gemini-2.5-flash',
292
- 'flash': 'google/gemini-2.5-flash',
289
+ gemini: 'google/gemini-2.5-flash',
290
+ flash: 'google/gemini-2.5-flash',
293
291
  'gemini-pro': 'google/gemini-2.5-pro',
294
- 'llama': 'meta-llama/llama-4-maverick',
292
+ llama: 'meta-llama/llama-4-maverick',
295
293
  'llama-4': 'meta-llama/llama-4-maverick',
296
294
  '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',
295
+ mistral: 'mistralai/mistral-large-2411',
296
+ codestral: 'mistralai/codestral-2501',
297
+ deepseek: 'deepseek/deepseek-chat',
298
+ r1: 'deepseek/deepseek-r1',
299
+ qwen: 'qwen/qwen3-235b-a22b',
300
+ grok: 'x-ai/grok-3',
301
+ sonar: 'perplexity/sonar-pro',
304
302
  }
305
303
 
306
304
  for (const [alias, expectedId] of Object.entries(documentedAliases)) {
@@ -17,7 +17,7 @@ import {
17
17
  type ProviderEndpoint,
18
18
  type ResolvedModel,
19
19
  type DirectProvider,
20
- } from './index.js'
20
+ } from '../src/index.js'
21
21
 
22
22
  describe('package exports', () => {
23
23
  it('exports resolve function', () => {
@@ -166,8 +166,8 @@ describe('end-to-end workflows', () => {
166
166
  expect(allModels.length).toBeGreaterThanOrEqual(0)
167
167
 
168
168
  if (allModels.length > 0) {
169
- const anthropicModels = allModels.filter(m => m.id.startsWith('anthropic/'))
170
- const openaiModels = allModels.filter(m => m.id.startsWith('openai/'))
169
+ const anthropicModels = allModels.filter((m) => m.id.startsWith('anthropic/'))
170
+ const openaiModels = allModels.filter((m) => m.id.startsWith('openai/'))
171
171
 
172
172
  if (anthropicModels.length > 0) {
173
173
  expect(anthropicModels[0].id).toContain('anthropic/')
@@ -182,7 +182,7 @@ describe('end-to-end workflows', () => {
182
182
  const allModels = list()
183
183
 
184
184
  if (allModels.length > 0) {
185
- const directModels = allModels.filter(m => {
185
+ const directModels = allModels.filter((m) => {
186
186
  const provider = m.id.split('/')[0]
187
187
  return (DIRECT_PROVIDERS as readonly string[]).includes(provider)
188
188
  })
@@ -282,8 +282,8 @@ describe('end-to-end workflows', () => {
282
282
  expect(Array.isArray(claudeModels)).toBe(true)
283
283
  if (claudeModels.length > 0) {
284
284
  expect(
285
- claudeModels.some(m =>
286
- m.id.includes('claude') || m.name.toLowerCase().includes('claude')
285
+ claudeModels.some(
286
+ (m) => m.id.includes('claude') || m.name.toLowerCase().includes('claude')
287
287
  )
288
288
  ).toBe(true)
289
289
  }
@@ -329,7 +329,7 @@ describe('end-to-end workflows', () => {
329
329
  it('models may have architecture info', () => {
330
330
  const models = list()
331
331
  if (models.length > 0) {
332
- const modelWithArch = models.find(m => m.architecture)
332
+ const modelWithArch = models.find((m) => m.architecture)
333
333
  if (modelWithArch?.architecture) {
334
334
  expect(modelWithArch.architecture.modality).toBeDefined()
335
335
  expect(Array.isArray(modelWithArch.architecture.input_modalities)).toBe(true)
@@ -344,7 +344,7 @@ describe('end-to-end workflows', () => {
344
344
  const directProviders = ['anthropic', 'openai', 'google']
345
345
 
346
346
  for (const provider of directProviders) {
347
- const models = list().filter(m => m.id.startsWith(`${provider}/`))
347
+ const models = list().filter((m) => m.id.startsWith(`${provider}/`))
348
348
  if (models.length > 0) {
349
349
  const resolved = resolveWithProvider(models[0].id)
350
350
  expect(resolved.supportsDirectRouting).toBe(true)
@@ -355,7 +355,7 @@ describe('end-to-end workflows', () => {
355
355
 
356
356
  it('identifies non-direct routing providers', () => {
357
357
  const models = list()
358
- const nonDirectModel = models.find(m => {
358
+ const nonDirectModel = models.find((m) => {
359
359
  const provider = m.id.split('/')[0]
360
360
  return !(DIRECT_PROVIDERS as readonly string[]).includes(provider)
361
361
  })
@@ -14,8 +14,8 @@ import {
14
14
  DIRECT_PROVIDERS,
15
15
  type ModelInfo,
16
16
  type ResolvedModel,
17
- } from './models.js'
18
- import { ALIASES } from './aliases.js'
17
+ } from '../src/models.js'
18
+ import { ALIASES } from '../src/aliases.js'
19
19
 
20
20
  describe('list', () => {
21
21
  it('returns an array of models', () => {
@@ -93,7 +93,7 @@ describe('search', () => {
93
93
  const idPart = model.id.split('/')[0] // Provider name
94
94
  const results = search(idPart)
95
95
  expect(results.length).toBeGreaterThan(0)
96
- expect(results.some(m => m.id.includes(idPart))).toBe(true)
96
+ expect(results.some((m) => m.id.includes(idPart))).toBe(true)
97
97
  }
98
98
  })
99
99
 
@@ -123,11 +123,13 @@ describe('search', () => {
123
123
  const models = list()
124
124
  if (models.length > 0) {
125
125
  // Find a model and search for part of its name
126
- const model = models.find(m => m.name.includes(' '))
126
+ const model = models.find((m) => m.name.includes(' '))
127
127
  if (model) {
128
128
  const namePart = model.name.split(' ')[0].toLowerCase()
129
129
  const results = search(namePart)
130
- expect(results.some(m => m.id === model.id || m.name.toLowerCase().includes(namePart))).toBe(true)
130
+ expect(
131
+ results.some((m) => m.id === model.id || m.name.toLowerCase().includes(namePart))
132
+ ).toBe(true)
131
133
  }
132
134
  }
133
135
  })
@@ -299,7 +301,7 @@ describe('resolveWithProvider', () => {
299
301
  it('identifies non-direct providers', () => {
300
302
  // Use a model from a provider not in DIRECT_PROVIDERS
301
303
  const models = list()
302
- const nonDirectModel = models.find(m => {
304
+ const nonDirectModel = models.find((m) => {
303
305
  const provider = m.id.split('/')[0]
304
306
  return !(DIRECT_PROVIDERS as readonly string[]).includes(provider)
305
307
  })