@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.
- package/.metadata/anthropic.response-map.json +1 -0
- package/.metadata/google.response-map.json +1 -0
- package/.metadata/groq.response-map.json +1 -0
- package/.metadata/llm-request-config-registry.json +111 -0
- package/.metadata/llm-response-config-registry.json +1 -0
- package/.metadata/model-aliases.json +1 -0
- package/.metadata/model-normalization.json +1 -0
- package/.metadata/moonshot.response-map.json +1 -0
- package/.metadata/openai.response-map.json +1 -0
- package/.metadata/openrouter_catalog_with_vendor_mapping.json +15781 -0
- package/.metadata/reasoning-support.json +159 -0
- package/.metadata/xai.response-map.json +1 -0
- package/README.md +480 -0
- package/dist/adapters/grok/GrokAdapter.d.ts +50 -0
- package/dist/adapters/grok/GrokAdapter.js +342 -0
- package/dist/adapters/openai/OpenAIAdapter.d.ts +50 -0
- package/dist/adapters/openai/OpenAIAdapter.js +401 -0
- package/dist/adapters/openrouter/OpenRouterAdapter.d.ts +87 -0
- package/dist/adapters/openrouter/OpenRouterAdapter.js +1449 -0
- package/dist/adapters/openrouter/reasoning-capabilities.d.ts +26 -0
- package/dist/adapters/openrouter/reasoning-capabilities.js +79 -0
- package/dist/discovery.d.ts +6 -0
- package/dist/discovery.js +114 -0
- package/dist/errors.d.ts +27 -0
- package/dist/errors.js +33 -0
- package/dist/factory.d.ts +15 -0
- package/dist/factory.js +206 -0
- package/dist/gateway.d.ts +22 -0
- package/dist/gateway.js +154 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +42 -0
- package/dist/interceptors.d.ts +10 -0
- package/dist/interceptors.js +1 -0
- package/dist/logger.d.ts +70 -0
- package/dist/logger.js +222 -0
- package/dist/openrouter-catalog.d.ts +119 -0
- package/dist/openrouter-catalog.js +222 -0
- package/dist/providers/OpenRouterProvider.d.ts +16 -0
- package/dist/providers/OpenRouterProvider.js +171 -0
- package/dist/registry/AdapterRegistry.d.ts +86 -0
- package/dist/registry/AdapterRegistry.js +36 -0
- package/dist/registry/ProviderRegistry.d.ts +24 -0
- package/dist/registry/ProviderRegistry.js +46 -0
- package/dist/router/Router.d.ts +33 -0
- package/dist/router/Router.js +258 -0
- package/dist/router/RouterTypes.d.ts +138 -0
- package/dist/router/RouterTypes.js +5 -0
- package/dist/router/RouterWrapper.d.ts +83 -0
- package/dist/router/RouterWrapper.js +744 -0
- package/dist/router.d.ts +13 -0
- package/dist/router.js +8 -0
- package/dist/types.d.ts +33 -0
- package/dist/types.js +1 -0
- package/dist/utils/esm-compat.d.ts +9 -0
- package/dist/utils/esm-compat.js +13 -0
- package/dist/utils/ids.d.ts +4 -0
- package/dist/utils/ids.js +6 -0
- package/package.json +66 -0
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import { newId } from '../utils/ids.js';
|
|
2
|
+
/**
|
|
3
|
+
* Main router class
|
|
4
|
+
* Orchestrates provider execution using ProviderModules and router-side adapters
|
|
5
|
+
*/
|
|
6
|
+
export class AIRouter {
|
|
7
|
+
constructor(providers, adapters) {
|
|
8
|
+
this.providers = providers;
|
|
9
|
+
this.adapters = adapters;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Resolve provider name from request, checking OpenRouter mode first
|
|
13
|
+
*/
|
|
14
|
+
resolveProviderName(input) {
|
|
15
|
+
const hasOpenRouterAdapter = this.adapters.has('openrouter');
|
|
16
|
+
const hasOpenRouterProvider = this.providers.has('openrouter');
|
|
17
|
+
// Check if OPEN_ROUTER_KEY is set in environment (completely automatic detection)
|
|
18
|
+
const hasOpenRouterKey = !!(process.env.OPEN_ROUTER_KEY &&
|
|
19
|
+
process.env.OPEN_ROUTER_KEY.trim() !== '' &&
|
|
20
|
+
!process.env.OPEN_ROUTER_KEY.startsWith('ENV.'));
|
|
21
|
+
// Normalize config.provider (handle string, trim whitespace)
|
|
22
|
+
const cfgProviderRaw = input.request?.config?.provider;
|
|
23
|
+
const cfgProvider = typeof cfgProviderRaw === 'string' ? cfgProviderRaw.trim() : undefined;
|
|
24
|
+
const hasProviderInConfig = !!cfgProvider && cfgProvider !== '';
|
|
25
|
+
const hasProviderAtTopLevel = input.provider === 'openrouter';
|
|
26
|
+
const hasRegisteredProviders = this.providers.list().length > 0;
|
|
27
|
+
// OpenRouter mode detection (COMPLETELY AUTOMATIC):
|
|
28
|
+
// 1. Explicit: top-level provider is 'openrouter'
|
|
29
|
+
// 2. OpenRouter provider is registered (when OPEN_ROUTER_KEY is set via factory)
|
|
30
|
+
// 3. AUTOMATIC: OpenRouter adapter exists + OPEN_ROUTER_KEY env var is set + config.provider is specified
|
|
31
|
+
// This works even when router is created manually without calling createRouter()
|
|
32
|
+
// 4. Fallback: adapter exists + config.provider + no providers registered (auto-detect)
|
|
33
|
+
//
|
|
34
|
+
// IMPORTANT: When OpenRouter provider is registered OR OPEN_ROUTER_KEY is set, OpenRouter mode is active.
|
|
35
|
+
// All provider names (openai, grok, anthropic, etc.) route through OpenRouter.
|
|
36
|
+
// The interceptor sets input.provider = 'openrouter' when OpenRouter mode is enabled.
|
|
37
|
+
const isOpenRouterMode = hasProviderAtTopLevel ||
|
|
38
|
+
hasOpenRouterProvider || // OpenRouter provider registered = OpenRouter mode is DEFAULT
|
|
39
|
+
(hasOpenRouterAdapter && hasOpenRouterKey && hasProviderInConfig) || // AUTOMATIC: env var + adapter + config.provider
|
|
40
|
+
(hasOpenRouterAdapter && hasProviderInConfig && !hasRegisteredProviders); // Fallback: adapter + config.provider + no providers
|
|
41
|
+
if (isOpenRouterMode) {
|
|
42
|
+
return 'openrouter';
|
|
43
|
+
}
|
|
44
|
+
// Honor request.config.provider in normal flow
|
|
45
|
+
if (hasProviderInConfig) {
|
|
46
|
+
// Validate provider is registered
|
|
47
|
+
if (!this.providers.has(cfgProvider)) {
|
|
48
|
+
const available = this.providers.list().join(', ');
|
|
49
|
+
// Check if OpenRouter mode could work automatically
|
|
50
|
+
if (hasOpenRouterAdapter && !hasOpenRouterKey) {
|
|
51
|
+
throw new Error(`Provider '${cfgProvider}' specified in request.config.provider but not registered. ` +
|
|
52
|
+
`OpenRouter adapter is available - set OPEN_ROUTER_KEY environment variable to enable automatic OpenRouter mode. ` +
|
|
53
|
+
`Available providers: ${available || '(none)'}`);
|
|
54
|
+
}
|
|
55
|
+
throw new Error(`Provider '${cfgProvider}' specified in request.config.provider but not registered. Available providers: ${available || '(none)'}`);
|
|
56
|
+
}
|
|
57
|
+
// Validate adapter is registered
|
|
58
|
+
if (!this.adapters.has(cfgProvider)) {
|
|
59
|
+
const available = this.adapters.list().join(', ');
|
|
60
|
+
throw new Error(`Adapter '${cfgProvider}' specified in request.config.provider but not registered. Available adapters: ${available || '(none)'}`);
|
|
61
|
+
}
|
|
62
|
+
return cfgProvider;
|
|
63
|
+
}
|
|
64
|
+
// Existing fallback: top-level provider or first registered provider
|
|
65
|
+
const providerName = input.provider ?? this.providers.list()[0];
|
|
66
|
+
if (!providerName) {
|
|
67
|
+
// Improved error messages with OpenRouter mode guidance
|
|
68
|
+
if (hasProviderInConfig && hasOpenRouterAdapter && !hasRegisteredProviders) {
|
|
69
|
+
if (!hasOpenRouterKey) {
|
|
70
|
+
throw new Error('OpenRouter adapter available and config.provider specified, but OpenRouter provider module not registered. ' +
|
|
71
|
+
'Set OPEN_ROUTER_KEY environment variable to enable automatic OpenRouter mode (works with both createRouter() and manual initialization).');
|
|
72
|
+
}
|
|
73
|
+
throw new Error('OpenRouter adapter available and config.provider specified, but OpenRouter provider module not registered. ' +
|
|
74
|
+
'OPEN_ROUTER_KEY is set but provider module initialization failed. Check that @x12i/ai-provider-openai is installed.');
|
|
75
|
+
}
|
|
76
|
+
if (hasProviderInConfig && !hasOpenRouterAdapter) {
|
|
77
|
+
throw new Error(`Provider '${input.request?.config?.provider}' specified in config but no providers registered and OpenRouter adapter not available.`);
|
|
78
|
+
}
|
|
79
|
+
// Final fallback error with OpenRouter suggestion
|
|
80
|
+
if (hasOpenRouterAdapter && !hasOpenRouterKey) {
|
|
81
|
+
throw new Error('No provider specified and no providers registered. ' +
|
|
82
|
+
'OpenRouter adapter is available - set OPEN_ROUTER_KEY environment variable to enable automatic OpenRouter mode.');
|
|
83
|
+
}
|
|
84
|
+
throw new Error('No provider specified and no providers registered');
|
|
85
|
+
}
|
|
86
|
+
return providerName;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Execute a sync request
|
|
90
|
+
*/
|
|
91
|
+
async runSync(input) {
|
|
92
|
+
const requestId = input.requestId ?? newId();
|
|
93
|
+
const providerName = this.resolveProviderName(input);
|
|
94
|
+
const provider = this.providers.get(providerName);
|
|
95
|
+
const adapter = this.adapters.get(providerName);
|
|
96
|
+
// Check capabilities
|
|
97
|
+
if (!provider.capabilities.modes.sync) {
|
|
98
|
+
throw new Error(`Provider '${providerName}' does not support sync mode`);
|
|
99
|
+
}
|
|
100
|
+
// Build call spec
|
|
101
|
+
const spec = await adapter.buildCallSpec({
|
|
102
|
+
requestId,
|
|
103
|
+
mode: 'sync',
|
|
104
|
+
request: input.request,
|
|
105
|
+
exec: input.exec,
|
|
106
|
+
});
|
|
107
|
+
// Execute
|
|
108
|
+
const execResult = await provider.execute(spec);
|
|
109
|
+
// Parse response
|
|
110
|
+
return adapter.parseResponse({
|
|
111
|
+
requestId,
|
|
112
|
+
request: { ...input.request, _callSpec: spec }, // Include call spec for adapter access
|
|
113
|
+
execResult,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Execute a streaming request
|
|
118
|
+
*/
|
|
119
|
+
async *runStream(input) {
|
|
120
|
+
const requestId = input.requestId ?? newId();
|
|
121
|
+
const providerName = this.resolveProviderName(input);
|
|
122
|
+
const provider = this.providers.get(providerName);
|
|
123
|
+
const adapter = this.adapters.get(providerName);
|
|
124
|
+
// Check capabilities
|
|
125
|
+
if (!provider.capabilities.modes.stream) {
|
|
126
|
+
throw new Error(`Provider '${providerName}' does not support stream mode`);
|
|
127
|
+
}
|
|
128
|
+
if (!provider.stream) {
|
|
129
|
+
throw new Error(`Provider '${providerName}' does not implement stream()`);
|
|
130
|
+
}
|
|
131
|
+
if (!adapter.parseStreamChunk || !adapter.finalizeStream) {
|
|
132
|
+
throw new Error(`Adapter for '${providerName}' does not support streaming`);
|
|
133
|
+
}
|
|
134
|
+
// Build call spec
|
|
135
|
+
const spec = await adapter.buildCallSpec({
|
|
136
|
+
requestId,
|
|
137
|
+
mode: 'stream',
|
|
138
|
+
request: input.request,
|
|
139
|
+
exec: input.exec,
|
|
140
|
+
});
|
|
141
|
+
// Collect stream data
|
|
142
|
+
const collected = {
|
|
143
|
+
rawEvents: [],
|
|
144
|
+
outputText: '',
|
|
145
|
+
};
|
|
146
|
+
try {
|
|
147
|
+
// Stream chunks
|
|
148
|
+
for await (const chunk of provider.stream(spec)) {
|
|
149
|
+
collected.rawEvents.push(chunk.raw);
|
|
150
|
+
// Emit provider_raw event
|
|
151
|
+
yield {
|
|
152
|
+
type: 'provider_raw',
|
|
153
|
+
requestId,
|
|
154
|
+
provider: providerName,
|
|
155
|
+
raw: chunk.raw,
|
|
156
|
+
};
|
|
157
|
+
// Parse chunk to router events
|
|
158
|
+
const events = adapter.parseStreamChunk({
|
|
159
|
+
requestId,
|
|
160
|
+
request: input.request,
|
|
161
|
+
chunk,
|
|
162
|
+
});
|
|
163
|
+
for (const ev of events) {
|
|
164
|
+
// Accumulate output text
|
|
165
|
+
if (ev.type === 'output_text_delta') {
|
|
166
|
+
collected.outputText += ev.delta;
|
|
167
|
+
}
|
|
168
|
+
yield ev;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
// Finalize stream
|
|
172
|
+
const response = adapter.finalizeStream({
|
|
173
|
+
requestId,
|
|
174
|
+
request: input.request,
|
|
175
|
+
collected,
|
|
176
|
+
});
|
|
177
|
+
yield {
|
|
178
|
+
type: 'completed',
|
|
179
|
+
requestId,
|
|
180
|
+
response,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
catch (error) {
|
|
184
|
+
yield {
|
|
185
|
+
type: 'error',
|
|
186
|
+
requestId,
|
|
187
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
188
|
+
};
|
|
189
|
+
throw error;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Execute a batch request
|
|
194
|
+
*/
|
|
195
|
+
async runBatch(providerName, items, exec) {
|
|
196
|
+
const provider = this.providers.get(providerName);
|
|
197
|
+
const adapter = this.adapters.get(providerName);
|
|
198
|
+
// Check capabilities
|
|
199
|
+
if (!provider.capabilities.modes.batch) {
|
|
200
|
+
throw new Error(`Provider '${providerName}' does not support batch mode`);
|
|
201
|
+
}
|
|
202
|
+
if (!provider.submitBatch || !provider.getBatchStatus || !provider.fetchBatchResults) {
|
|
203
|
+
throw new Error(`Provider '${providerName}' does not implement batch methods`);
|
|
204
|
+
}
|
|
205
|
+
if (!adapter.parseBatchItem) {
|
|
206
|
+
throw new Error(`Adapter for '${providerName}' does not support batch parsing`);
|
|
207
|
+
}
|
|
208
|
+
// Ensure exec.timeoutMs is always set (router owns execution semantics)
|
|
209
|
+
const execWithTimeout = {
|
|
210
|
+
timeoutMs: exec?.timeoutMs ?? 60000,
|
|
211
|
+
retries: exec?.retries,
|
|
212
|
+
};
|
|
213
|
+
// Fix: Persist requestIds for items that don't have them, so we can map results back correctly
|
|
214
|
+
const itemsWithIds = items.map((it) => ({
|
|
215
|
+
...it,
|
|
216
|
+
requestId: it.requestId ?? newId(),
|
|
217
|
+
}));
|
|
218
|
+
// Build call specs for each item using persisted requestIds
|
|
219
|
+
const specs = await Promise.all(itemsWithIds.map((it) => adapter.buildCallSpec({
|
|
220
|
+
requestId: it.requestId,
|
|
221
|
+
mode: 'sync', // Batch items are sync specs
|
|
222
|
+
request: it.request,
|
|
223
|
+
exec: execWithTimeout,
|
|
224
|
+
})));
|
|
225
|
+
// Submit batch
|
|
226
|
+
const handle = await provider.submitBatch(specs);
|
|
227
|
+
// Poll until complete
|
|
228
|
+
while (true) {
|
|
229
|
+
const status = await provider.getBatchStatus(handle);
|
|
230
|
+
if (status.state === 'completed') {
|
|
231
|
+
break;
|
|
232
|
+
}
|
|
233
|
+
if (status.state === 'failed' || status.state === 'canceled') {
|
|
234
|
+
throw new Error(`Batch ended with state: ${status.state}`);
|
|
235
|
+
}
|
|
236
|
+
// Wait before polling again
|
|
237
|
+
await new Promise((r) => setTimeout(r, 2000));
|
|
238
|
+
}
|
|
239
|
+
// Fetch results
|
|
240
|
+
const batchResults = await provider.fetchBatchResults(handle);
|
|
241
|
+
// Fix: Use Map for efficient lookup by requestId
|
|
242
|
+
const originalById = new Map(itemsWithIds.map((it) => [it.requestId, it]));
|
|
243
|
+
// Parse each item using the Map for correct correlation
|
|
244
|
+
const parsedItems = batchResults.items.map((item) => {
|
|
245
|
+
const originalItem = originalById.get(item.requestId);
|
|
246
|
+
return adapter.parseBatchItem({
|
|
247
|
+
requestId: item.requestId,
|
|
248
|
+
request: originalItem?.request || {},
|
|
249
|
+
item,
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
return {
|
|
253
|
+
provider: providerName,
|
|
254
|
+
rawBatch: batchResults.rawResponse,
|
|
255
|
+
items: parsedItems,
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Router-level request and response types
|
|
3
|
+
* These are the public API of the router
|
|
4
|
+
*/
|
|
5
|
+
export type RouterMode = 'sync' | 'stream' | 'batch';
|
|
6
|
+
/**
|
|
7
|
+
* Router configuration
|
|
8
|
+
*/
|
|
9
|
+
export interface RouterConfig {
|
|
10
|
+
usageTracker?: {
|
|
11
|
+
recordRequest(event: {
|
|
12
|
+
provider: string;
|
|
13
|
+
timestamp: number;
|
|
14
|
+
duration: number;
|
|
15
|
+
tokens: {
|
|
16
|
+
input: number;
|
|
17
|
+
output: number;
|
|
18
|
+
total: number;
|
|
19
|
+
};
|
|
20
|
+
cost?: number;
|
|
21
|
+
success: boolean;
|
|
22
|
+
}): void;
|
|
23
|
+
};
|
|
24
|
+
verbose?: boolean;
|
|
25
|
+
logLevel?: 'error' | 'warn' | 'info' | 'debug' | 'verbose';
|
|
26
|
+
logger?: any;
|
|
27
|
+
timeoutMs?: number;
|
|
28
|
+
defaultTimeoutMs?: number;
|
|
29
|
+
/** OpenRouter provider config (e.g. from gateway); used when env OPEN_ROUTER_KEY is not visible */
|
|
30
|
+
openrouter?: {
|
|
31
|
+
apiKey?: string;
|
|
32
|
+
httpReferer?: string;
|
|
33
|
+
xTitle?: string;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Router request - the input to the router
|
|
38
|
+
*/
|
|
39
|
+
export interface AIRouterRequest {
|
|
40
|
+
requestId?: string;
|
|
41
|
+
request: any;
|
|
42
|
+
provider?: string;
|
|
43
|
+
mode: RouterMode;
|
|
44
|
+
exec?: {
|
|
45
|
+
timeoutMs?: number;
|
|
46
|
+
retries?: number;
|
|
47
|
+
idempotencyKey?: string;
|
|
48
|
+
signal?: AbortSignal;
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Router response for sync mode
|
|
53
|
+
*/
|
|
54
|
+
export interface AIResponse {
|
|
55
|
+
requestId: string;
|
|
56
|
+
provider: string;
|
|
57
|
+
rawResponse: unknown;
|
|
58
|
+
outputText?: string;
|
|
59
|
+
usage?: any;
|
|
60
|
+
reasoning: {
|
|
61
|
+
requested: {
|
|
62
|
+
effort: string;
|
|
63
|
+
visibility: string;
|
|
64
|
+
};
|
|
65
|
+
applied: {
|
|
66
|
+
effort: string;
|
|
67
|
+
visibility: string;
|
|
68
|
+
};
|
|
69
|
+
artifacts: {
|
|
70
|
+
encrypted?: any[];
|
|
71
|
+
summary?: {
|
|
72
|
+
text: string;
|
|
73
|
+
format: string;
|
|
74
|
+
};
|
|
75
|
+
trace?: {
|
|
76
|
+
text: string;
|
|
77
|
+
format: string;
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
availability: {
|
|
81
|
+
supportsEffort: boolean;
|
|
82
|
+
supportsSummary: boolean;
|
|
83
|
+
supportsTrace: boolean;
|
|
84
|
+
supportsEncrypted: boolean;
|
|
85
|
+
};
|
|
86
|
+
warnings?: any[];
|
|
87
|
+
};
|
|
88
|
+
metadata?: any;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Router stream events
|
|
92
|
+
*/
|
|
93
|
+
export type AIStreamEvent = {
|
|
94
|
+
type: 'provider_raw';
|
|
95
|
+
requestId: string;
|
|
96
|
+
provider: string;
|
|
97
|
+
raw: unknown;
|
|
98
|
+
} | {
|
|
99
|
+
type: 'output_text_delta';
|
|
100
|
+
requestId: string;
|
|
101
|
+
delta: string;
|
|
102
|
+
} | {
|
|
103
|
+
type: 'reasoning_summary_delta';
|
|
104
|
+
requestId: string;
|
|
105
|
+
delta: string;
|
|
106
|
+
} | {
|
|
107
|
+
type: 'reasoning_trace_delta';
|
|
108
|
+
requestId: string;
|
|
109
|
+
delta: string;
|
|
110
|
+
} | {
|
|
111
|
+
type: 'completed';
|
|
112
|
+
requestId: string;
|
|
113
|
+
response: AIResponse;
|
|
114
|
+
} | {
|
|
115
|
+
type: 'error';
|
|
116
|
+
requestId: string;
|
|
117
|
+
error: any;
|
|
118
|
+
};
|
|
119
|
+
/**
|
|
120
|
+
* Batch request item
|
|
121
|
+
*/
|
|
122
|
+
export interface AIBatchRequestItem {
|
|
123
|
+
requestId?: string;
|
|
124
|
+
request: any;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Batch response
|
|
128
|
+
*/
|
|
129
|
+
export interface AIBatchResponse {
|
|
130
|
+
provider: string;
|
|
131
|
+
items: Array<{
|
|
132
|
+
requestId: string;
|
|
133
|
+
rawResponse?: unknown;
|
|
134
|
+
outputText?: string;
|
|
135
|
+
error?: any;
|
|
136
|
+
}>;
|
|
137
|
+
rawBatch?: unknown;
|
|
138
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { ProviderRegistry } from '../registry/ProviderRegistry.js';
|
|
2
|
+
import { AdapterRegistry } from '../registry/AdapterRegistry.js';
|
|
3
|
+
import type { AIRouterRequest, AIResponse, AIStreamEvent, AIBatchResponse, AIBatchRequestItem } from './RouterTypes.js';
|
|
4
|
+
import type { RouterConfig } from './RouterTypes.js';
|
|
5
|
+
import type { RequestInterceptor, ResponseInterceptor } from '../interceptors.js';
|
|
6
|
+
/**
|
|
7
|
+
* Wrapper around AIRouter that adds logging, interceptors, and usage tracking
|
|
8
|
+
* Maintains backward compatibility with existing router API
|
|
9
|
+
*/
|
|
10
|
+
export declare class LLMProviderRouter {
|
|
11
|
+
private router;
|
|
12
|
+
private providerRegistry;
|
|
13
|
+
private adapterRegistry;
|
|
14
|
+
private config;
|
|
15
|
+
private requestInterceptors;
|
|
16
|
+
private responseInterceptors;
|
|
17
|
+
private logger;
|
|
18
|
+
private providerConfigs;
|
|
19
|
+
private lastConfiguredProvider;
|
|
20
|
+
private autoRegistrationAttempted;
|
|
21
|
+
constructor(config?: RouterConfig);
|
|
22
|
+
/**
|
|
23
|
+
* Register a provider module
|
|
24
|
+
*/
|
|
25
|
+
registerProvider(providerModuleOrFactory: any, factoryName?: string): void;
|
|
26
|
+
/**
|
|
27
|
+
* Configure provider SDK client config
|
|
28
|
+
*/
|
|
29
|
+
configureProvider(providerName: string, config: any): void;
|
|
30
|
+
/**
|
|
31
|
+
* Add request interceptor
|
|
32
|
+
*/
|
|
33
|
+
addRequestInterceptor(interceptor: RequestInterceptor): void;
|
|
34
|
+
/**
|
|
35
|
+
* Add response interceptor
|
|
36
|
+
*/
|
|
37
|
+
addResponseInterceptor(interceptor: ResponseInterceptor): void;
|
|
38
|
+
/**
|
|
39
|
+
* Invoke router (sync mode)
|
|
40
|
+
*/
|
|
41
|
+
invoke(request: AIRouterRequest): Promise<AIResponse>;
|
|
42
|
+
/**
|
|
43
|
+
* Stream request
|
|
44
|
+
*/
|
|
45
|
+
stream(request: AIRouterRequest): AsyncIterable<AIStreamEvent>;
|
|
46
|
+
/**
|
|
47
|
+
* Batch request
|
|
48
|
+
*/
|
|
49
|
+
createBatch(providerName: string, items: AIBatchRequestItem[], exec?: {
|
|
50
|
+
timeoutMs?: number;
|
|
51
|
+
retries?: number;
|
|
52
|
+
idempotencyKey?: string;
|
|
53
|
+
signal?: AbortSignal;
|
|
54
|
+
}): Promise<AIBatchResponse>;
|
|
55
|
+
/**
|
|
56
|
+
* List registered providers
|
|
57
|
+
*/
|
|
58
|
+
listProviders(): string[];
|
|
59
|
+
/**
|
|
60
|
+
* Check health of a specific provider
|
|
61
|
+
*/
|
|
62
|
+
checkHealth(provider: string): Promise<import('../router.js').HealthCheckResult>;
|
|
63
|
+
/**
|
|
64
|
+
* Get provider registry (for advanced usage)
|
|
65
|
+
*/
|
|
66
|
+
getProviderRegistry(): ProviderRegistry;
|
|
67
|
+
/**
|
|
68
|
+
* Get adapter registry (for advanced usage)
|
|
69
|
+
*/
|
|
70
|
+
getAdapterRegistry(): AdapterRegistry;
|
|
71
|
+
/**
|
|
72
|
+
* Try to register OpenRouter if a key is available (constructor config or resolved env).
|
|
73
|
+
* Used by ensureProvidersRegistered() and by the OpenRouter retry path when the first run skipped registration.
|
|
74
|
+
*/
|
|
75
|
+
private tryRegisterOpenRouter;
|
|
76
|
+
/**
|
|
77
|
+
* Automatically detect and register providers based on environment variables
|
|
78
|
+
* This ensures "Zero-Config" initialization works even if createRouter() isn't used.
|
|
79
|
+
* When the first run did not register OpenRouter (e.g. key was missing), a later invoke
|
|
80
|
+
* can still register it by retrying OpenRouter-only registration when the key is now available.
|
|
81
|
+
*/
|
|
82
|
+
private ensureProvidersRegistered;
|
|
83
|
+
}
|