@x12i/ai-providers-router 4.6.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 (58) hide show
  1. package/.metadata/anthropic.response-map.json +1 -0
  2. package/.metadata/google.response-map.json +1 -0
  3. package/.metadata/groq.response-map.json +1 -0
  4. package/.metadata/llm-request-config-registry.json +111 -0
  5. package/.metadata/llm-response-config-registry.json +1 -0
  6. package/.metadata/model-aliases.json +1 -0
  7. package/.metadata/model-normalization.json +1 -0
  8. package/.metadata/moonshot.response-map.json +1 -0
  9. package/.metadata/openai.response-map.json +1 -0
  10. package/.metadata/openrouter_catalog_with_vendor_mapping.json +15781 -0
  11. package/.metadata/reasoning-support.json +159 -0
  12. package/.metadata/xai.response-map.json +1 -0
  13. package/README.md +480 -0
  14. package/dist/adapters/grok/GrokAdapter.d.ts +50 -0
  15. package/dist/adapters/grok/GrokAdapter.js +342 -0
  16. package/dist/adapters/openai/OpenAIAdapter.d.ts +50 -0
  17. package/dist/adapters/openai/OpenAIAdapter.js +401 -0
  18. package/dist/adapters/openrouter/OpenRouterAdapter.d.ts +87 -0
  19. package/dist/adapters/openrouter/OpenRouterAdapter.js +1449 -0
  20. package/dist/adapters/openrouter/reasoning-capabilities.d.ts +26 -0
  21. package/dist/adapters/openrouter/reasoning-capabilities.js +79 -0
  22. package/dist/discovery.d.ts +6 -0
  23. package/dist/discovery.js +114 -0
  24. package/dist/errors.d.ts +27 -0
  25. package/dist/errors.js +33 -0
  26. package/dist/factory.d.ts +15 -0
  27. package/dist/factory.js +206 -0
  28. package/dist/gateway.d.ts +22 -0
  29. package/dist/gateway.js +154 -0
  30. package/dist/index.d.ts +9 -0
  31. package/dist/index.js +42 -0
  32. package/dist/interceptors.d.ts +10 -0
  33. package/dist/interceptors.js +1 -0
  34. package/dist/logger.d.ts +70 -0
  35. package/dist/logger.js +222 -0
  36. package/dist/openrouter-catalog.d.ts +119 -0
  37. package/dist/openrouter-catalog.js +222 -0
  38. package/dist/providers/OpenRouterProvider.d.ts +16 -0
  39. package/dist/providers/OpenRouterProvider.js +171 -0
  40. package/dist/registry/AdapterRegistry.d.ts +86 -0
  41. package/dist/registry/AdapterRegistry.js +36 -0
  42. package/dist/registry/ProviderRegistry.d.ts +24 -0
  43. package/dist/registry/ProviderRegistry.js +46 -0
  44. package/dist/router/Router.d.ts +33 -0
  45. package/dist/router/Router.js +258 -0
  46. package/dist/router/RouterTypes.d.ts +138 -0
  47. package/dist/router/RouterTypes.js +5 -0
  48. package/dist/router/RouterWrapper.d.ts +83 -0
  49. package/dist/router/RouterWrapper.js +744 -0
  50. package/dist/router.d.ts +13 -0
  51. package/dist/router.js +8 -0
  52. package/dist/types.d.ts +33 -0
  53. package/dist/types.js +1 -0
  54. package/dist/utils/esm-compat.d.ts +9 -0
  55. package/dist/utils/esm-compat.js +13 -0
  56. package/dist/utils/ids.d.ts +4 -0
  57. package/dist/utils/ids.js +6 -0
  58. package/package.json +66 -0
@@ -0,0 +1,154 @@
1
+ import { LLMProviderRouter } from './router/RouterWrapper.js';
2
+ export class AIGateway {
3
+ constructor(router) {
4
+ this.router = router || new LLMProviderRouter();
5
+ }
6
+ async invoke(request) {
7
+ // Accept router-wrapper requests too (provider/mode/request), and unwrap them
8
+ // so gateway doesn't treat them as an instruction object and run the instruction pipeline.
9
+ if (request && typeof request === 'object' && request.request && request.provider && request.mode) {
10
+ const inner = request.request;
11
+ request = {
12
+ ...inner,
13
+ // preserve provider/mode semantics in the gateway config
14
+ config: {
15
+ ...(inner.config || {}),
16
+ provider: (inner.config && inner.config.provider) || request.provider,
17
+ },
18
+ // optional: keep mode for downstream if you use it
19
+ mode: request.mode,
20
+ };
21
+ }
22
+ this.validateRequest(request);
23
+ const requestedProvider = request?.config?.provider;
24
+ const requestedModel = request?.config?.model;
25
+ const response = await this.invokeInternal(request);
26
+ // Optional: Diagnostic warning for JSON-looking prompts (doesn't block, just warns)
27
+ const outboundPrompt = this.extractOutboundPrompt(request);
28
+ if (outboundPrompt && this.looksLikeConfigJSON(outboundPrompt)) {
29
+ console.warn(`[WARNING] Outbound prompt appears to be JSON config data instead of user instructions. ` +
30
+ `This may indicate config data reached the AI model. Prompt preview: ${outboundPrompt.substring(0, 100)}...`);
31
+ }
32
+ // Enforce provider/model pinning when explicitly requested.
33
+ // Default behavior: warn only. Enable strict pinning via config flag.
34
+ const enforcePinning = request?.config?.enforceProviderModel === true;
35
+ const actualProvider = response?.content?.providerMetadata?.provider ||
36
+ response?.provider ||
37
+ response?.metadata?.provider;
38
+ const actualModel = response?.content?.providerMetadata?.model ||
39
+ response?.model ||
40
+ response?.metadata?.model;
41
+ if (enforcePinning && (requestedProvider || requestedModel)) {
42
+ if (requestedProvider && actualProvider && requestedProvider !== actualProvider) {
43
+ throw new Error(`Provider mismatch: requested '${requestedProvider}' but executed '${actualProvider}'. Refusing silent provider switching.`);
44
+ }
45
+ if (requestedModel && actualModel && requestedModel !== actualModel) {
46
+ throw new Error(`Model mismatch: requested '${requestedModel}' but executed '${actualModel}'. Refusing silent model fallback.`);
47
+ }
48
+ }
49
+ return response;
50
+ }
51
+ async invokeInternal(request) {
52
+ // Convert the request to router format and invoke
53
+ const routerRequest = {
54
+ provider: request.config?.provider || 'openai',
55
+ mode: request.mode || 'sync',
56
+ request: {
57
+ instructions: request.instructions || '',
58
+ inputData: request.inputData || '',
59
+ config: request.config || {},
60
+ },
61
+ exec: request.exec || {},
62
+ };
63
+ return await this.router.invoke(routerRequest);
64
+ }
65
+ validateRequest(request) {
66
+ if (!request) {
67
+ throw new Error('Request is required');
68
+ }
69
+ // 🚨 CRITICAL VALIDATION: Prevent config data from being sent as instructions
70
+ // This happens when adapters fall back to JSON.stringify(request) and send config to AI model
71
+ // Validate instruction content types
72
+ if (request.instructions !== undefined && typeof request.instructions !== 'string') {
73
+ throw new Error(`CRITICAL: instructions must be a string, got ${typeof request.instructions}. ` +
74
+ 'Non-string instructions can cause adapters to stringify the entire request object.');
75
+ }
76
+ if (request.inputData !== undefined) {
77
+ if (typeof request.inputData !== 'string' && typeof request.inputData !== 'object') {
78
+ throw new Error(`CRITICAL: inputData must be a string or object, got ${typeof request.inputData}. ` +
79
+ 'Invalid inputData types can cause adapters to stringify the entire request object.');
80
+ }
81
+ }
82
+ // Validate messages structure
83
+ if (request.messages !== undefined) {
84
+ if (!Array.isArray(request.messages)) {
85
+ throw new Error(`CRITICAL: messages must be an array, got ${typeof request.messages}. ` +
86
+ 'Invalid messages structure can cause adapters to stringify the entire request object.');
87
+ }
88
+ for (let i = 0; i < request.messages.length; i++) {
89
+ const msg = request.messages[i];
90
+ if (!msg || typeof msg !== 'object') {
91
+ throw new Error(`CRITICAL: messages[${i}] must be an object, got ${typeof msg}. ` +
92
+ 'Invalid message structure can cause adapters to stringify the entire request object.');
93
+ }
94
+ if (msg.content !== undefined && typeof msg.content !== 'string') {
95
+ throw new Error(`CRITICAL: messages[${i}].content must be a string, got ${typeof msg.content}. ` +
96
+ 'Non-string message content can cause adapters to stringify the entire request object.');
97
+ }
98
+ }
99
+ }
100
+ // Require at least one valid instruction source
101
+ const hasValidInstructions = request.instructions && typeof request.instructions === 'string' && request.instructions.trim().length > 0;
102
+ const hasValidInputData = request.inputData && ((typeof request.inputData === 'string' && request.inputData.trim().length > 0) ||
103
+ (typeof request.inputData === 'object' && Object.keys(request.inputData).length > 0));
104
+ const hasValidUserMessage = request.messages && Array.isArray(request.messages) &&
105
+ request.messages.some((msg) => msg.role === 'user' && msg.content && typeof msg.content === 'string' && msg.content.trim().length > 0);
106
+ if (!hasValidInstructions && !hasValidInputData && !hasValidUserMessage) {
107
+ throw new Error('CRITICAL: Request must have at least one valid instruction source. ' +
108
+ 'Provide either: non-empty string instructions, valid inputData, or at least one user message with string content. ' +
109
+ 'Missing/empty instruction content causes adapters to fall back to JSON.stringify(request), ' +
110
+ 'which sends configuration data to the AI model instead of proper instructions.');
111
+ }
112
+ }
113
+ extractOutboundPrompt(request) {
114
+ // Extract what will actually be sent to the AI model as the prompt/instructions
115
+ // Check inputData (most common)
116
+ if (request.inputData && typeof request.inputData === 'string' && request.inputData.trim()) {
117
+ return request.inputData.trim();
118
+ }
119
+ // Check instructions
120
+ if (request.instructions && typeof request.instructions === 'string' && request.instructions.trim()) {
121
+ return request.instructions.trim();
122
+ }
123
+ // Check messages
124
+ if (request.messages && Array.isArray(request.messages)) {
125
+ for (const msg of request.messages) {
126
+ if (msg.role === 'user' && msg.content && typeof msg.content === 'string' && msg.content.trim()) {
127
+ return msg.content.trim();
128
+ }
129
+ }
130
+ }
131
+ return null;
132
+ }
133
+ looksLikeConfigJSON(text) {
134
+ if (!text || typeof text !== 'string')
135
+ return false;
136
+ const trimmed = text.trim();
137
+ if (!trimmed.startsWith('{'))
138
+ return false;
139
+ try {
140
+ const parsed = JSON.parse(trimmed);
141
+ if (typeof parsed !== 'object' || !parsed)
142
+ return false;
143
+ // Look for config-like patterns
144
+ const keys = Object.keys(parsed);
145
+ const configIndicators = ['config', 'provider', 'model', 'apiKey', 'maxTokens', 'temperature'];
146
+ // If it has multiple config keys, it's likely config JSON
147
+ const configKeyCount = keys.filter(key => configIndicators.includes(key)).length;
148
+ return configKeyCount >= 2;
149
+ }
150
+ catch {
151
+ return false; // Not valid JSON
152
+ }
153
+ }
154
+ }
@@ -0,0 +1,9 @@
1
+ export { LLMProviderRouter } from './router.js';
2
+ export type { RouterConfig, HealthCheckResult, ProviderId, AIRouterRequest, AIResponse, AIStreamEvent, AIBatchResponse, AIBatchRequestItem, } from './router.js';
3
+ export { createRouter, createRouterFromConfig } from './factory.js';
4
+ export type { CreateRouterConfig } from './factory.js';
5
+ export { ProviderNotFoundError, FallbackExhaustedError, ProviderNotInstalledError } from './errors.js';
6
+ export type { RequestInterceptor, ResponseInterceptor } from './interceptors.js';
7
+ export type { UsageTracker, AdapterLoader, ProviderInit } from './types.js';
8
+ export { Logger, getLogger, createLogger } from './logger.js';
9
+ export type { LogLevel, LoggerConfig } from './logger.js';
package/dist/index.js ADDED
@@ -0,0 +1,42 @@
1
+ // Fix IPv6-first DNS resolution issue on Windows (forces IPv4-first to avoid connect timeouts)
2
+ // This prevents undici from trying IPv6 first on networks that silently drop IPv6 traffic
3
+ // Must be set before any network calls are made
4
+ import dns from 'node:dns';
5
+ dns.setDefaultResultOrder('ipv4first');
6
+ // Main exports
7
+ export { LLMProviderRouter } from './router.js';
8
+ // Provider/model enums and helpers
9
+ /*
10
+ export {
11
+ AIProvider,
12
+ NxAiProvider,
13
+ OpenAIModel,
14
+ GroqModel,
15
+ AnthropicModel,
16
+ GoogleModel,
17
+ GoogleEmbeddingModel,
18
+ MistralModel,
19
+ CohereModel,
20
+ CohereEmbeddingModel,
21
+ MetaModel,
22
+ DeepSeekModel,
23
+ XAIModel,
24
+ AmazonModel,
25
+ QwenModel,
26
+ PerplexityModel,
27
+ VoyageModel,
28
+ TogetherModel,
29
+ FireworksModel,
30
+ OllamaModel,
31
+ listModelsByProvider,
32
+ isValidModelForProvider,
33
+ mapProviderNameToEnum,
34
+ } from './models.js';
35
+ */
36
+ //export type { ProviderModelRef } from './models.js';
37
+ // Factory functions
38
+ export { createRouter, createRouterFromConfig } from './factory.js';
39
+ // Error classes
40
+ export { ProviderNotFoundError, FallbackExhaustedError, ProviderNotInstalledError } from './errors.js';
41
+ // Logger
42
+ export { Logger, getLogger, createLogger } from './logger.js';
@@ -0,0 +1,10 @@
1
+ import type { AIRouterRequest, AIResponse } from './router/RouterTypes.js';
2
+ import type { ProviderId } from './types.js';
3
+ /**
4
+ * Interceptor function that can modify requests before they are sent
5
+ */
6
+ export type RequestInterceptor = (request: AIRouterRequest, provider?: ProviderId) => AIRouterRequest | Promise<AIRouterRequest>;
7
+ /**
8
+ * Interceptor function that can modify responses after they are received
9
+ */
10
+ export type ResponseInterceptor = (response: AIResponse, provider: ProviderId) => AIResponse | Promise<AIResponse>;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Logging utility for AI Provider Router
3
+ * Provides structured logging with proper log levels and verbose mode support
4
+ */
5
+ export type LogLevel = 'error' | 'warn' | 'info' | 'debug' | 'verbose';
6
+ export interface LoggerConfig {
7
+ verbose?: boolean;
8
+ level?: LogLevel;
9
+ }
10
+ /**
11
+ * Logger class that wraps logxer with proper log levels
12
+ */
13
+ export declare class Logger {
14
+ verbose: boolean;
15
+ level: LogLevel;
16
+ private gateway;
17
+ constructor(config?: LoggerConfig);
18
+ /**
19
+ * Update logger configuration
20
+ */
21
+ setConfig(config: LoggerConfig): void;
22
+ /**
23
+ * Check if a log level should be logged
24
+ */
25
+ private shouldLog;
26
+ /**
27
+ * Log error messages
28
+ */
29
+ error(message: string, data?: any): void;
30
+ /**
31
+ * Log warning messages
32
+ */
33
+ warn(message: string, data?: any): void;
34
+ /**
35
+ * Log informational messages
36
+ */
37
+ info(message: string, data?: any): void;
38
+ /**
39
+ * Log debug messages
40
+ */
41
+ debug(message: string, data?: any): void;
42
+ /**
43
+ * Log verbose messages (only when verbose mode is enabled)
44
+ */
45
+ logVerbose(message: string, data?: any): void;
46
+ /**
47
+ * Log AI request (unfiltered, only in verbose mode)
48
+ */
49
+ logAIRequest(provider: string, request: any, metadata?: any): void;
50
+ /**
51
+ * Log AI response (unfiltered, only in verbose mode)
52
+ */
53
+ logAIResponse(provider: string, response: any, metadata?: any): void;
54
+ /**
55
+ * Log AI request/response pair (unfiltered, only in verbose mode)
56
+ */
57
+ logAIIteraction(provider: string, request: any, response: any, duration?: number, metadata?: any): void;
58
+ /**
59
+ * Sanitize data for logging (remove sensitive info, handle circular refs)
60
+ */
61
+ sanitizeForLogging(data: any): any;
62
+ }
63
+ /**
64
+ * Get or create the default logger instance
65
+ */
66
+ export declare function getLogger(config?: LoggerConfig): Logger;
67
+ /**
68
+ * Create a new logger instance
69
+ */
70
+ export declare function createLogger(config?: LoggerConfig): Logger;
package/dist/logger.js ADDED
@@ -0,0 +1,222 @@
1
+ /**
2
+ * Logging utility for AI Provider Router
3
+ * Provides structured logging with proper log levels and verbose mode support
4
+ */
5
+ import logxer from '@x12i/logxer';
6
+ const logs = logxer || console;
7
+ /**
8
+ * Logger class that wraps logxer with proper log levels
9
+ */
10
+ export class Logger {
11
+ constructor(config = {}) {
12
+ this.verbose = config.verbose || false;
13
+ this.level = config.level || 'info';
14
+ this.gateway = logs || console;
15
+ }
16
+ /**
17
+ * Update logger configuration
18
+ */
19
+ setConfig(config) {
20
+ if (config.verbose !== undefined) {
21
+ this.verbose = config.verbose;
22
+ }
23
+ if (config.level !== undefined) {
24
+ this.level = config.level;
25
+ }
26
+ }
27
+ /**
28
+ * Check if a log level should be logged
29
+ */
30
+ shouldLog(level) {
31
+ const levels = ['error', 'warn', 'info', 'debug', 'verbose'];
32
+ const currentIndex = levels.indexOf(this.level);
33
+ const messageIndex = levels.indexOf(level);
34
+ return messageIndex <= currentIndex;
35
+ }
36
+ /**
37
+ * Log error messages
38
+ */
39
+ error(message, data) {
40
+ if (!this.shouldLog('error'))
41
+ return;
42
+ if (this.gateway?.error) {
43
+ this.gateway.error(message, data || {});
44
+ }
45
+ else {
46
+ console.error(`[ERROR] ${message}`, data || '');
47
+ }
48
+ }
49
+ /**
50
+ * Log warning messages
51
+ */
52
+ warn(message, data) {
53
+ if (!this.shouldLog('warn'))
54
+ return;
55
+ if (this.gateway?.warn) {
56
+ this.gateway.warn(message, data || {});
57
+ }
58
+ else {
59
+ console.warn(`[WARN] ${message}`, data || '');
60
+ }
61
+ }
62
+ /**
63
+ * Log informational messages
64
+ */
65
+ info(message, data) {
66
+ if (!this.shouldLog('info'))
67
+ return;
68
+ if (this.gateway?.info) {
69
+ this.gateway.info(message, data || {});
70
+ }
71
+ else {
72
+ console.log(`[INFO] ${message}`, data || '');
73
+ }
74
+ }
75
+ /**
76
+ * Log debug messages
77
+ */
78
+ debug(message, data) {
79
+ if (!this.shouldLog('debug'))
80
+ return;
81
+ if (this.gateway?.debug) {
82
+ this.gateway.debug(message, data || {});
83
+ }
84
+ else {
85
+ console.debug(`[DEBUG] ${message}`, data || '');
86
+ }
87
+ }
88
+ /**
89
+ * Log verbose messages (only when verbose mode is enabled)
90
+ */
91
+ logVerbose(message, data) {
92
+ if (!this.verbose || !this.shouldLog('verbose'))
93
+ return;
94
+ if (this.gateway?.verbose) {
95
+ this.gateway.verbose(message, data || {});
96
+ }
97
+ else if (this.gateway?.trace) {
98
+ this.gateway.trace(message, data || {});
99
+ }
100
+ else {
101
+ console.log(`[VERBOSE] ${message}`, data || '');
102
+ }
103
+ }
104
+ /**
105
+ * Log AI request (unfiltered, only in verbose mode)
106
+ */
107
+ logAIRequest(provider, request, metadata) {
108
+ if (!this.verbose)
109
+ return;
110
+ this.logVerbose('AI Request Sent', {
111
+ provider,
112
+ request: this.sanitizeForLogging(request),
113
+ metadata: metadata || {},
114
+ timestamp: new Date().toISOString(),
115
+ });
116
+ }
117
+ /**
118
+ * Log AI response (unfiltered, only in verbose mode)
119
+ */
120
+ logAIResponse(provider, response, metadata) {
121
+ if (!this.verbose)
122
+ return;
123
+ this.logVerbose('AI Response Received', {
124
+ provider,
125
+ response: this.sanitizeForLogging(response),
126
+ metadata: metadata || {},
127
+ timestamp: new Date().toISOString(),
128
+ });
129
+ }
130
+ /**
131
+ * Log AI request/response pair (unfiltered, only in verbose mode)
132
+ */
133
+ logAIIteraction(provider, request, response, duration, metadata) {
134
+ if (!this.verbose)
135
+ return;
136
+ this.logVerbose('AI Interaction Complete', {
137
+ provider,
138
+ request: this.sanitizeForLogging(request),
139
+ response: this.sanitizeForLogging(response),
140
+ duration: duration ? `${duration}ms` : undefined,
141
+ metadata: metadata || {},
142
+ timestamp: new Date().toISOString(),
143
+ });
144
+ }
145
+ /**
146
+ * Sanitize data for logging (remove sensitive info, handle circular refs)
147
+ */
148
+ sanitizeForLogging(data) {
149
+ if (data === null || data === undefined) {
150
+ return data;
151
+ }
152
+ // Handle circular references and large objects
153
+ const seen = new WeakSet();
154
+ const sanitize = (obj, depth = 0) => {
155
+ // Prevent infinite recursion
156
+ if (depth > 10) {
157
+ return '[Max Depth Reached]';
158
+ }
159
+ // Handle primitives
160
+ if (obj === null || obj === undefined) {
161
+ return obj;
162
+ }
163
+ if (typeof obj === 'string' || typeof obj === 'number' || typeof obj === 'boolean') {
164
+ return obj;
165
+ }
166
+ // Handle circular references
167
+ if (typeof obj === 'object') {
168
+ if (seen.has(obj)) {
169
+ return '[Circular Reference]';
170
+ }
171
+ seen.add(obj);
172
+ // Handle arrays
173
+ if (Array.isArray(obj)) {
174
+ return obj.map((item) => sanitize(item, depth + 1));
175
+ }
176
+ // Handle Buffer (common in file uploads)
177
+ if (Buffer.isBuffer(obj)) {
178
+ return `[Buffer: ${obj.length} bytes]`;
179
+ }
180
+ // Handle objects
181
+ const result = {};
182
+ for (const key in obj) {
183
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
184
+ // Skip sensitive keys (can be extended)
185
+ if (key.toLowerCase().includes('password') ||
186
+ key.toLowerCase().includes('secret') ||
187
+ key.toLowerCase().includes('apikey') ||
188
+ key.toLowerCase().includes('token') && key !== 'token') {
189
+ result[key] = '[REDACTED]';
190
+ }
191
+ else {
192
+ result[key] = sanitize(obj[key], depth + 1);
193
+ }
194
+ }
195
+ }
196
+ return result;
197
+ }
198
+ return String(obj);
199
+ };
200
+ return sanitize(data);
201
+ }
202
+ }
203
+ // Default logger instance
204
+ let defaultLogger = null;
205
+ /**
206
+ * Get or create the default logger instance
207
+ */
208
+ export function getLogger(config) {
209
+ if (!defaultLogger) {
210
+ defaultLogger = new Logger(config);
211
+ }
212
+ else if (config) {
213
+ defaultLogger.setConfig(config);
214
+ }
215
+ return defaultLogger;
216
+ }
217
+ /**
218
+ * Create a new logger instance
219
+ */
220
+ export function createLogger(config = {}) {
221
+ return new Logger(config);
222
+ }
@@ -0,0 +1,119 @@
1
+ export interface OpenRouterProvider {
2
+ slug: string;
3
+ name: string;
4
+ }
5
+ export interface OpenRouterModelVendor {
6
+ author: string;
7
+ direct: {
8
+ vendorId: string;
9
+ apiStyle: string;
10
+ baseUrlHint?: string | null;
11
+ modelId: string;
12
+ confidence: string;
13
+ notes: string;
14
+ };
15
+ }
16
+ export interface OpenRouterModel {
17
+ openrouterId: string;
18
+ canonicalSlug: string;
19
+ displayName: string;
20
+ vendor: OpenRouterModelVendor;
21
+ aliases: string[];
22
+ fallbackOf: string[];
23
+ capabilities: {
24
+ contextLength: number;
25
+ supportedParameters: string[];
26
+ };
27
+ }
28
+ export interface OpenRouterCatalog {
29
+ schemaVersion: string;
30
+ source: {
31
+ providersEndpoint: string;
32
+ modelsEndpoint: string;
33
+ retrievedAt: string;
34
+ counts: {
35
+ providers: number;
36
+ models: number;
37
+ };
38
+ };
39
+ providers: OpenRouterProvider[];
40
+ models: OpenRouterModel[];
41
+ }
42
+ /**
43
+ * OpenRouter Catalog Loader
44
+ * Loads and caches the OpenRouter catalog data from JSON file
45
+ */
46
+ export declare class OpenRouterCatalogLoader {
47
+ private static instance;
48
+ private catalog;
49
+ private catalogPath;
50
+ private constructor();
51
+ static getInstance(): OpenRouterCatalogLoader;
52
+ /**
53
+ * Load the catalog from the JSON file
54
+ */
55
+ load(): Promise<OpenRouterCatalog>;
56
+ /**
57
+ * Get the loaded catalog (throws if not loaded)
58
+ */
59
+ getCatalog(): OpenRouterCatalog;
60
+ /**
61
+ * Get all providers
62
+ */
63
+ getProviders(): Promise<OpenRouterProvider[]>;
64
+ /**
65
+ * Get all models
66
+ */
67
+ getModels(): Promise<OpenRouterModel[]>;
68
+ /**
69
+ * Find a model by OpenRouter ID
70
+ */
71
+ findModelById(openrouterId: string): Promise<OpenRouterModel | null>;
72
+ /**
73
+ * Find models by canonical slug
74
+ */
75
+ findModelByCanonicalSlug(canonicalSlug: string): Promise<OpenRouterModel | null>;
76
+ /**
77
+ * Find models by aliases (returns all matches)
78
+ */
79
+ findModelsByAlias(alias: string): Promise<OpenRouterModel[]>;
80
+ /**
81
+ * Find provider by slug
82
+ */
83
+ findProviderBySlug(slug: string): Promise<OpenRouterProvider | null>;
84
+ /**
85
+ * Get all provider slugs
86
+ */
87
+ getProviderSlugs(): Promise<string[]>;
88
+ /**
89
+ * Find provider by vendor ID (from model.vendor.direct.vendorId)
90
+ * This maps vendor IDs to provider slugs for alias support
91
+ */
92
+ findProviderByVendorId(vendorId: string): Promise<OpenRouterProvider | null>;
93
+ /**
94
+ * Get all vendor IDs that map to providers
95
+ */
96
+ getAllVendorIds(): Promise<string[]>;
97
+ /**
98
+ * Check if a model is available
99
+ */
100
+ isModelAvailable(modelId: string): Promise<boolean>;
101
+ /**
102
+ * Get model capabilities
103
+ */
104
+ getModelCapabilities(modelId: string): Promise<OpenRouterModel['capabilities'] | null>;
105
+ /**
106
+ * Validate model supports a specific parameter
107
+ */
108
+ modelSupportsParameter(modelId: string, parameter: string): Promise<boolean>;
109
+ /**
110
+ * Infer provider from model name using catalog data
111
+ */
112
+ inferProviderFromModel(modelName: string): Promise<string | null>;
113
+ /**
114
+ * Normalize model name to OpenRouter format
115
+ * Returns the canonical OpenRouter ID for the model
116
+ */
117
+ normalizeModelName(modelName: string, providerHint?: string): Promise<string>;
118
+ }
119
+ export declare const openRouterCatalog: OpenRouterCatalogLoader;