language-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.
@@ -0,0 +1,150 @@
1
+ import camelCase from 'camelcase'
2
+ import { flatten, unflatten } from 'flat'
3
+
4
+ const overwrites = {}
5
+
6
+ const sortingValues = ['top-weekly', 'newest', 'throughput-high-to-low', 'latency-low-to-high', 'pricing-low-to-high', 'pricing-high-to-low']
7
+
8
+ function camelCaseDeep<T>(input: T): T {
9
+ if (Array.isArray(input)) {
10
+ return input.map(camelCaseDeep) as any
11
+ }
12
+
13
+ if (input !== null && typeof input === 'object') {
14
+ return Object.entries(input).reduce(
15
+ (acc, [key, value]) => {
16
+ acc[camelCase(key)] = camelCaseDeep(value)
17
+ return acc
18
+ },
19
+ {} as Record<string, any>,
20
+ ) as any
21
+ }
22
+
23
+ return input
24
+ }
25
+
26
+ async function fetchProviders(slug: string) {
27
+ const url = `https://openrouter.ai/api/frontend/stats/endpoint?permaslug=${slug}`
28
+
29
+ const response = (await fetch(url).then((res) => res.json())) as { data: any }
30
+
31
+ return camelCaseDeep(response.data || [])
32
+ }
33
+
34
+ async function main() {
35
+ try {
36
+ const URL = 'https://openrouter.ai/api/frontend/models/find?order=top-weekly'
37
+
38
+ console.log('Fetching models data from OpenRouter...')
39
+
40
+ const data = (
41
+ await Promise.all(
42
+ sortingValues.map(async (sortingValue) => {
43
+ const URL = `https://openrouter.ai/api/frontend/models/find?order=${sortingValue}`
44
+ console.log(`Fetching ${URL}...`)
45
+ const response = (await fetch(URL).then((res) => res.json())) as { data: any }
46
+ return [sortingValue, camelCaseDeep(response.data)]
47
+ }),
48
+ )
49
+ ).reduce(
50
+ (acc, [sortingValue, models]) => {
51
+ acc[sortingValue] = models
52
+ return acc
53
+ },
54
+ {} as Record<string, any>,
55
+ )
56
+
57
+ const models = data['top-weekly']
58
+
59
+ const modelsData = models.models.map((model) => {
60
+ if (model.slug in overwrites) {
61
+ console.log(`Overwriting model ${model.slug} with custom data`, overwrites[model.slug])
62
+ const tempModel: Record<string, unknown> = flatten(model)
63
+ const tempOverwrites: Record<string, unknown> = flatten(overwrites[model.slug])
64
+ const mergedModel = { ...tempModel, ...tempOverwrites }
65
+ return unflatten(mergedModel)
66
+ }
67
+
68
+ // Find the model in the other sorting values
69
+ // Then add a sorting object that has the index of the model in the other sorting values
70
+
71
+ const modelIndexes = sortingValues
72
+ .map((sortingValue) => {
73
+ const models = data[sortingValue]
74
+ const index = models.models.findIndex((m) => m.slug === model.slug)
75
+ return {
76
+ [camelCase(sortingValue)]: index,
77
+ }
78
+ })
79
+ .reduce((acc, curr) => {
80
+ return { ...acc, ...curr }
81
+ }, {})
82
+
83
+ const mergedModel = { ...model, sorting: modelIndexes }
84
+
85
+ return mergedModel
86
+ })
87
+
88
+ const finalModels = []
89
+
90
+ const providerAliases = {
91
+ 'Google AI Studio': 'google',
92
+ 'Google Vertex': 'vertex',
93
+ }
94
+
95
+ let completed = 0
96
+
97
+ for (const model of modelsData) {
98
+ console.log(`[PROVIDERS] Fetching provider metadata for ${model.permaslug}...`)
99
+
100
+ const providers = await fetchProviders(model.permaslug)
101
+ model.providers = providers.map((provider) => {
102
+ const providerName = providerAliases[provider.providerDisplayName] || provider.providerDisplayName
103
+
104
+ const priceToDollars = (price: string) => {
105
+ // To get the dollar price, we need to multiply the price by a million.
106
+ const priceNumber = parseFloat(price)
107
+ return Number(
108
+ (priceNumber * 1000000)
109
+ .toFixed(2)
110
+ // Remove trailing zeros
111
+ .replace(/\.?0+$/, ''),
112
+ )
113
+ }
114
+
115
+ return {
116
+ name: provider.providerDisplayName,
117
+ slug: camelCase(providerName),
118
+ quantization: provider.quantization,
119
+ context: provider.contextLength,
120
+ maxCompletionTokens: provider.maxCompletionTokens,
121
+ providerModelId: provider.providerModelId,
122
+ pricing: provider.pricing,
123
+ // Disable claude's reasoning parameter as it's only supported via the :thinking tag.
124
+ supportedParameters: model.slug === 'anthropic/claude-3.7-sonnet' ? ['max_tokens', 'temperature', 'stop', 'tools', 'tool_choice'] : provider.supportedParameters,
125
+ inputCost: priceToDollars(provider.pricing.prompt),
126
+ outputCost: priceToDollars(provider.pricing.completion),
127
+ throughput: provider.stats?.[0]?.p50Throughput,
128
+ latency: provider.stats?.[0]?.p50Latency,
129
+ }
130
+ })
131
+
132
+ completed++
133
+ console.log(`[PROVIDERS] ${completed}/${modelsData.length} models completed`)
134
+ }
135
+
136
+ // Write to models.json in src directory
137
+ const { resolve } = await import('node:path')
138
+ const { writeFileSync } = await import('node:fs')
139
+
140
+ const outputPath = resolve('./src/models.js')
141
+ writeFileSync(outputPath, `export default ${JSON.stringify({ models: modelsData }, null, 2)}`)
142
+
143
+ console.log(`Models data written to ${outputPath}`)
144
+ } catch (error) {
145
+ console.error('Error fetching or writing models data:', error)
146
+ process.exit(1)
147
+ }
148
+ }
149
+
150
+ main()
@@ -0,0 +1,12 @@
1
+ /*
2
+ * This file contains overwrites for the AI models.
3
+ * OpenRouter's data is not always 100% accurate, so we can overwrite certain
4
+ * models with our own data.
5
+ */
6
+
7
+ export const overwrites = {
8
+ 'anthropic/claude-3.7-sonnet': {
9
+ // Explicitly remove reasoning from this model as it's not actually supported.
10
+ 'endpoint.supportedParameters': ['max_tokens', 'temperature', 'stop', 'tools', 'tool_choice'],
11
+ },
12
+ }
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "language-models",
3
+ "version": "0.1.0",
4
+ "description": "Language model abstractions",
5
+ "main": "dist/index.js",
6
+ "scripts": {
7
+ "dev": "dotenvx run -- npx tsx watch src/demo.ts",
8
+ "build": "rimraf dist && tsc && rm dist/models.d.ts && cp src/models.d.ts dist/",
9
+ "generate:models": "npx tsx generate/build-models.ts",
10
+ "clean": "rimraf dist",
11
+ "lint": "tsc --noEmit",
12
+ "prepublishOnly": "pnpm clean && pnpm build",
13
+ "test": "vitest",
14
+ "typecheck": "tsc --noEmit"
15
+ },
16
+ "type": "module",
17
+ "packageManager": "pnpm@10.6.5",
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"
25
+ },
26
+ "dependencies": {
27
+ "@types/node": "^22.13.10",
28
+ "camelcase": "^8.0.0",
29
+ "flat": "^6.0.1",
30
+ "vitest": "^3.0.9"
31
+ }
32
+ }
package/publish.js ADDED
@@ -0,0 +1,32 @@
1
+
2
+ import { execSync } from 'child_process';
3
+ import fs from 'fs';
4
+ import path from 'path';
5
+ import { fileURLToPath } from 'url';
6
+
7
+ try {
8
+ console.log('Attempting direct NPM publish...');
9
+
10
+ const __filename = fileURLToPath(import.meta.url);
11
+ const __dirname = path.dirname(__filename);
12
+ const pkgPath = path.join(process.cwd(), 'package.json');
13
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
14
+
15
+ if (!pkg.version.startsWith('0.')) {
16
+ console.log(`Fixing package.json version: ${pkg.version} -> 0.1.0`);
17
+ pkg.version = '0.1.0';
18
+ fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n', 'utf8');
19
+ }
20
+
21
+ execSync('npm publish --access public --tag next', {
22
+ stdio: 'inherit',
23
+ env: {
24
+ ...process.env,
25
+ NPM_CONFIG_REGISTRY: 'https://registry.npmjs.org/',
26
+ }
27
+ });
28
+
29
+ console.log('Direct NPM publish successful');
30
+ } catch (error) {
31
+ console.error('Error during direct NPM publish:', error.message);
32
+ }
package/roadmap.md ADDED
@@ -0,0 +1,54 @@
1
+ # `ai-models` Roadmap
2
+
3
+ This roadmap outlines the planned features and considerations for the `ai-models` package, which provides utilities for working with AI SDKs, functions, workflows, observability, and evaluations.
4
+
5
+ ## Model Identification & Syntax
6
+
7
+ - [ ] Support for provider/creator/model naming convention
8
+ - [ ] Determine whether to use the @ sign in naming syntax
9
+ - [ ] Support for creator/model syntax without provider specification
10
+ - [ ] Alignment with OpenRouter's syntax where possible
11
+ - [ ] Design naming convention: `@{provider}/{creator}/{model}:{config,capabilities,tools,priorities}`
12
+ - [ ] Alternative syntax support: `{creator}/{model}:{config,capabilities,tools,priorities}`
13
+
14
+ ## Routing & Model Selection
15
+
16
+ - [ ] Implement our own version of openrouter/auto and openrouter/auto:online
17
+ - [ ] Tie routing logic to message content and/or priorities (performance, latency, throughput, cost)
18
+ - [ ] Support dynamic model fallback pattern
19
+ - [ ] Enable runtime tweaking of provider/model/settings/tools via query parameters
20
+
21
+ ## Capabilities & Features
22
+
23
+ - [ ] Handle reasoning capabilities (follow OpenRouter's :reasoning flag)
24
+ - [ ] Support for code execution tools
25
+ - [ ] Design composite type tools for transforming outputs between models
26
+ - [ ] Implement online search capability routing (to models like gemini, perplexity, 4o-search)
27
+ - [ ] Support for general-purpose tools (similar to agentic.so)
28
+ - [ ] Develop secure code execution tool for JavaScript (as alternative to Python-based tools)
29
+
30
+ ## Structured Output & Response Formats
31
+
32
+ - [ ] Support various methods for structured outputs:
33
+ - [ ] Native structured_output support
34
+ - [ ] Tool use with schema enforcement
35
+ - [ ] Response_format with JSON guarantees
36
+ - [ ] System prompt fallback method
37
+ - [ ] Handle compatibility between different output methods
38
+
39
+ ## Use Cases Implementation
40
+
41
+ - [ ] Evaluation framework with dynamic model/settings/tools configuration
42
+ - [ ] Experimentation support (LLM-as-Judge comparisons between models)
43
+ - [ ] "Best model" specification without cost/price requirements
44
+ - [ ] Specific capability routing (e.g., reasoning-only models)
45
+ - [ ] Caching opt-in/out functionality
46
+ - [ ] Logging controls for sensitive PII situations
47
+ - [ ] Seed parameter support
48
+
49
+ ## Phased Implementation
50
+
51
+ - [ ] Phase 1: Core model identification and routing
52
+ - [ ] Phase 2: Structured output and basic capabilities
53
+ - [ ] Phase 3: Advanced features and tools integration
54
+ - [ ] Phase 4: Comprehensive evaluation and experimentation framework
package/src/aliases.ts ADDED
@@ -0,0 +1,5 @@
1
+ export const aliases: Record<string, string> = {
2
+ gemini: 'google/gemini-2.0-flash-001',
3
+ 'claude-3.7-sonnet': 'anthropic/claude-3.7-sonnet',
4
+ r1: 'deepseek/deepseek-r1',
5
+ }
package/src/index.ts ADDED
@@ -0,0 +1,10 @@
1
+ export * from './parser'
2
+ export * from './aliases'
3
+ export * from './providers'
4
+ import allModels from './models'
5
+ import type { Model } from './types'
6
+ export * from './types'
7
+
8
+ export const models = allModels.models as unknown as Model[]
9
+
10
+ export const modelPattern = /^(?:[\w-]+\/)?[\w\.-]+(?:@[\w-]+(?::[\w-]+)?)?(?<!@)\([^()]*(?:\([^()]*\)[^()]*)*\)$/
@@ -0,0 +1,170 @@
1
+ // --- Reusable Base Interfaces ---
2
+
3
+ interface Icon {
4
+ url: string
5
+ invertRequired?: boolean
6
+ }
7
+
8
+ interface DataPolicy {
9
+ termsOfServiceUrl?: string // Optional based on examples
10
+ privacyPolicyUrl?: string // Optional based on examples
11
+ training: boolean
12
+ loggingRetentionDays?: number // Optional based on examples
13
+ requiresUserIds?: boolean // Optional based on examples
14
+ }
15
+
16
+ // Base pricing structure, allowing for optional cache fields
17
+ interface Pricing {
18
+ prompt: string
19
+ completion: string
20
+ image: string
21
+ request: string
22
+ webSearch: string
23
+ internalReasoning: string
24
+ discount: number
25
+ inputCacheRead?: string // Optional based on examples
26
+ inputCacheWrite?: string // Optional based on examples
27
+ }
28
+
29
+ interface ReasoningConfig {
30
+ startToken: string
31
+ endToken: string
32
+ }
33
+
34
+ interface Sorting {
35
+ topWeekly: number
36
+ newest: number
37
+ throughputHighToLow: number
38
+ latencyLowToHigh: number
39
+ pricingLowToHigh: number
40
+ pricingHighToLow: number
41
+ }
42
+
43
+ interface ProviderInfo {
44
+ name: string
45
+ displayName: string
46
+ baseUrl: string
47
+ dataPolicy: DataPolicy
48
+ headquarters?: string // Optional based on examples
49
+ hasChatCompletions: boolean
50
+ hasCompletions: boolean
51
+ isAbortable: boolean
52
+ moderationRequired: boolean
53
+ group: string
54
+ editors: never[] // Assuming these are always empty based on examples
55
+ owners: never[] // Assuming these are always empty based on examples
56
+ isMultipartSupported: boolean
57
+ statusPageUrl: string | null
58
+ byokEnabled: boolean
59
+ isPrimaryProvider: boolean
60
+ icon: Icon
61
+ }
62
+
63
+ // Represents an entry in the 'providers' array within a Model
64
+ interface ModelProvider {
65
+ name: string
66
+ slug: string
67
+ quantization: string | null
68
+ context: number
69
+ maxCompletionTokens: number | null // Can be null in examples
70
+ pricing: Pricing // Use the common Pricing type
71
+ supportedParameters: string[]
72
+ inputCost: number
73
+ outputCost: number
74
+ throughput?: number // Optional based on examples
75
+ latency?: number // Optional based on examples
76
+ }
77
+
78
+ // Represents the nested 'features.supportedParameters' object
79
+ interface SupportedParametersFeatures {
80
+ responseFormat?: boolean
81
+ structuredOutputs?: boolean
82
+ }
83
+
84
+ // Base Model structure (common fields without nested endpoint/providers)
85
+ // This helps avoid circular type definitions initially
86
+ interface ModelBase {
87
+ slug: string
88
+ hfSlug: string | null
89
+ updatedAt: string
90
+ createdAt: string
91
+ hfUpdatedAt: string | null // Assuming string like others, though only null seen
92
+ name: string
93
+ shortName: string
94
+ author: string
95
+ description: string
96
+ modelVersionGroupId: string | null
97
+ contextLength: number
98
+ inputModalities: string[]
99
+ outputModalities: string[]
100
+ hasTextOutput: boolean
101
+ group: string
102
+ instructType: string | null
103
+ defaultSystem: string | null // Assuming string, though only null seen
104
+ defaultStops: string[] // Use string[] as it can contain values
105
+ hidden: boolean
106
+ router: string | null // Assuming string, though only null seen
107
+ warningMessage: string | null
108
+ permaslug: string
109
+ reasoningConfig: ReasoningConfig | null
110
+ }
111
+
112
+ // Define Endpoint structure, referencing ModelBase for its 'model' property
113
+ interface Endpoint {
114
+ id: string
115
+ name: string
116
+ contextLength: number
117
+ model: ModelBase // Reference the base model structure
118
+ modelVariantSlug: string
119
+ modelVariantPermaslug: string
120
+ providerName: string
121
+ providerInfo: ProviderInfo
122
+ providerDisplayName: string
123
+ providerModelId: string
124
+ providerGroup: string
125
+ isCloaked: boolean
126
+ quantization: string | null
127
+ variant: string
128
+ isSelfHosted: boolean
129
+ canAbort: boolean
130
+ maxPromptTokens: number | null
131
+ maxCompletionTokens: number | null // Endpoint might have different limits
132
+ maxPromptImages: number | null
133
+ maxTokensPerImage: number | null
134
+ supportedParameters: string[]
135
+ isByok: boolean
136
+ moderationRequired: boolean
137
+ dataPolicy: DataPolicy // Data policy specific to the endpoint variant
138
+ pricing: Pricing // Pricing specific to the endpoint variant
139
+ isHidden: boolean
140
+ isDeranked: boolean
141
+ isDisabled: boolean
142
+ supportsToolParameters: boolean
143
+ supportsReasoning: boolean
144
+ supportsMultipart: boolean
145
+ limitRpm: number | null
146
+ limitRpd: number | null
147
+ hasCompletions: boolean
148
+ hasChatCompletions: boolean
149
+ features: {
150
+ supportedParameters?: SupportedParametersFeatures
151
+ }
152
+ providerRegion: string | null // Assuming string, though only null seen
153
+ }
154
+
155
+ // --- Final Composed Model Interface ---
156
+
157
+ // Extend ModelBase and add the nested structures
158
+ interface Model extends ModelBase {
159
+ endpoint: Endpoint | null // Endpoint can be null
160
+ sorting: Sorting
161
+ providers: ModelProvider[] // Array can be empty
162
+ }
163
+
164
+ // --- The Default Export ---
165
+
166
+ declare const _default: {
167
+ models: Model[]
168
+ }
169
+
170
+ export default _default