@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,401 @@
|
|
|
1
|
+
// Import ai-io-normalizer functions
|
|
2
|
+
// Note: Type declaration in src/types/ai-io-normalizer.d.ts allows this import
|
|
3
|
+
import { normalizeProviderResponse, loadResponseMaps } from 'ai-io-normalizer/dist/response-normalizer.js';
|
|
4
|
+
const normalizerAvailable = true;
|
|
5
|
+
// Lazy-load response maps (loaded once, cached)
|
|
6
|
+
let cachedResponseMaps = null;
|
|
7
|
+
function getResponseMaps() {
|
|
8
|
+
if (!normalizerAvailable || !loadResponseMaps) {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
if (!cachedResponseMaps) {
|
|
12
|
+
try {
|
|
13
|
+
// Load registry - ai-io-normalizer has built-in response maps
|
|
14
|
+
const registry = {
|
|
15
|
+
schemaVersion: '1.0.0',
|
|
16
|
+
standard: 'chat_completions_like_v1',
|
|
17
|
+
providers: {
|
|
18
|
+
openai: { ref: 'openai' },
|
|
19
|
+
xai: { ref: 'xai' },
|
|
20
|
+
groq: { ref: 'groq' },
|
|
21
|
+
anthropic: { ref: 'anthropic' },
|
|
22
|
+
google: { ref: 'google' },
|
|
23
|
+
moonshot: { ref: 'moonshot' },
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
cachedResponseMaps = loadResponseMaps(registry);
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
console.warn('[OpenAIAdapter] Failed to load response maps:', error.message);
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return cachedResponseMaps;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Extract output text using ai-io-normalizer's normalizeProviderResponse
|
|
37
|
+
* This uses the same parsing logic as ai-io-normalizer adapters
|
|
38
|
+
*/
|
|
39
|
+
function extractOutputTextUsingNormalizer(raw, provider, apiVariant) {
|
|
40
|
+
if (!raw)
|
|
41
|
+
return undefined;
|
|
42
|
+
// Try to use ai-io-normalizer if available
|
|
43
|
+
if (normalizerAvailable && normalizeProviderResponse) {
|
|
44
|
+
try {
|
|
45
|
+
const maps = getResponseMaps();
|
|
46
|
+
if (maps) {
|
|
47
|
+
const normalized = normalizeProviderResponse(raw, {
|
|
48
|
+
provider,
|
|
49
|
+
apiVariant,
|
|
50
|
+
mode: 'standard',
|
|
51
|
+
keepRawOverride: false,
|
|
52
|
+
}, maps);
|
|
53
|
+
// Extract text from normalized response
|
|
54
|
+
if (normalized.choices && normalized.choices.length > 0) {
|
|
55
|
+
const firstChoice = normalized.choices[0];
|
|
56
|
+
if (firstChoice.message?.content) {
|
|
57
|
+
return firstChoice.message.content;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
// Fallback to manual extraction if normalizer fails
|
|
64
|
+
console.warn('[OpenAIAdapter] Failed to use ai-io-normalizer, falling back to manual extraction:', error.message);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// Fallback: manual extraction (same logic as before)
|
|
68
|
+
if (typeof raw.output_text === 'string' && raw.output_text.trim()) {
|
|
69
|
+
return raw.output_text;
|
|
70
|
+
}
|
|
71
|
+
const out = raw.output;
|
|
72
|
+
if (!Array.isArray(out) || out.length === 0)
|
|
73
|
+
return undefined;
|
|
74
|
+
const parts = [];
|
|
75
|
+
for (const item of out) {
|
|
76
|
+
const content = item?.content;
|
|
77
|
+
if (Array.isArray(content)) {
|
|
78
|
+
for (const c of content) {
|
|
79
|
+
if (typeof c?.text === 'string') {
|
|
80
|
+
parts.push(c.text);
|
|
81
|
+
}
|
|
82
|
+
else if (typeof c?.text?.value === 'string') {
|
|
83
|
+
parts.push(c.text.value);
|
|
84
|
+
}
|
|
85
|
+
else if (typeof c?.value === 'string') {
|
|
86
|
+
parts.push(c.value);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
else if (content && typeof content === 'object') {
|
|
91
|
+
if (typeof content.text === 'string') {
|
|
92
|
+
parts.push(content.text);
|
|
93
|
+
}
|
|
94
|
+
else if (typeof content.text?.value === 'string') {
|
|
95
|
+
parts.push(content.text.value);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (typeof item?.text === 'string') {
|
|
99
|
+
parts.push(item.text);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const joined = parts.join('');
|
|
103
|
+
return joined.trim() ? joined : undefined;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Router-side adapter for OpenAI provider
|
|
107
|
+
* Converts router requests to ProviderSDKCallSpec and parses responses
|
|
108
|
+
*/
|
|
109
|
+
export class OpenAIAdapter {
|
|
110
|
+
constructor() {
|
|
111
|
+
this.provider = 'openai';
|
|
112
|
+
}
|
|
113
|
+
async buildCallSpec(input) {
|
|
114
|
+
const { requestId, mode, request, exec } = input;
|
|
115
|
+
// Extract model from request (could be in request.model or request.config.model)
|
|
116
|
+
const model = request.model || request.config?.model || 'gpt-4o-mini';
|
|
117
|
+
// Determine operation based on mode
|
|
118
|
+
const operation = mode === 'stream' ? 'openai.responses.stream' : 'openai.responses.create';
|
|
119
|
+
// Build OpenAI Responses API args
|
|
120
|
+
// OpenAI Responses API expects: { model, input: string | { input_text: string, ... } }
|
|
121
|
+
let inputValue;
|
|
122
|
+
// Handle different request formats
|
|
123
|
+
if (typeof request.inputData === 'string') {
|
|
124
|
+
// Simple string input
|
|
125
|
+
inputValue = request.inputData;
|
|
126
|
+
}
|
|
127
|
+
else if (request.inputData) {
|
|
128
|
+
// Structured input
|
|
129
|
+
inputValue = { input_text: String(request.inputData) };
|
|
130
|
+
}
|
|
131
|
+
else if (request.messages) {
|
|
132
|
+
// Convert messages to input_text
|
|
133
|
+
const systemMessages = request.messages.filter((m) => m.role === 'system');
|
|
134
|
+
const userMessages = request.messages.filter((m) => m.role === 'user');
|
|
135
|
+
const assistantMessages = request.messages.filter((m) => m.role === 'assistant');
|
|
136
|
+
const parts = [];
|
|
137
|
+
if (systemMessages.length > 0) {
|
|
138
|
+
parts.push(...systemMessages.map((m) => `System: ${m.content || ''}`));
|
|
139
|
+
}
|
|
140
|
+
if (userMessages.length > 0) {
|
|
141
|
+
parts.push(...userMessages.map((m) => `User: ${m.content || ''}`));
|
|
142
|
+
}
|
|
143
|
+
if (assistantMessages.length > 0) {
|
|
144
|
+
parts.push(...assistantMessages.map((m) => `Assistant: ${m.content || ''}`));
|
|
145
|
+
}
|
|
146
|
+
inputValue = parts.join('\n\n');
|
|
147
|
+
}
|
|
148
|
+
else if (request.instructions) {
|
|
149
|
+
// Use instructions as input
|
|
150
|
+
inputValue = request.instructions;
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
// Fallback: stringify the request
|
|
154
|
+
inputValue = JSON.stringify(request);
|
|
155
|
+
}
|
|
156
|
+
// Build args for OpenAI Responses API
|
|
157
|
+
const args = {
|
|
158
|
+
model,
|
|
159
|
+
input: inputValue,
|
|
160
|
+
};
|
|
161
|
+
// Add config parameters
|
|
162
|
+
if (request.config) {
|
|
163
|
+
if (request.config.maxTokens !== undefined) {
|
|
164
|
+
args.max_output_tokens = request.config.maxTokens;
|
|
165
|
+
}
|
|
166
|
+
if (request.config.temperature !== undefined) {
|
|
167
|
+
args.temperature = request.config.temperature;
|
|
168
|
+
}
|
|
169
|
+
if (request.config.topP !== undefined) {
|
|
170
|
+
args.top_p = request.config.topP;
|
|
171
|
+
}
|
|
172
|
+
if (request.config.stop !== undefined) {
|
|
173
|
+
args.stop = request.config.stop;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return {
|
|
177
|
+
requestId,
|
|
178
|
+
provider: 'openai',
|
|
179
|
+
mode,
|
|
180
|
+
operation,
|
|
181
|
+
args,
|
|
182
|
+
exec: exec ? {
|
|
183
|
+
timeoutMs: exec.timeoutMs,
|
|
184
|
+
retries: exec.retries,
|
|
185
|
+
idempotencyKey: exec.idempotencyKey,
|
|
186
|
+
signal: exec.signal,
|
|
187
|
+
} : undefined,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
parseResponse(input) {
|
|
191
|
+
const { requestId, execResult } = input;
|
|
192
|
+
const rawResponse = execResult.rawResponse;
|
|
193
|
+
// Determine API variant from operation or request
|
|
194
|
+
// OpenAI provider uses 'openai.responses.create' or 'openai.responses.stream' for Responses API
|
|
195
|
+
// or 'openai.chat.completions.create' for Chat Completions API
|
|
196
|
+
const operation = execResult.rawMeta?.operation;
|
|
197
|
+
const apiVariant = operation?.includes('responses') ? 'openai.responses' : 'openai.chat_completions';
|
|
198
|
+
// Use ai-io-normalizer for parsing (router stays orchestration-only)
|
|
199
|
+
const outputText = extractOutputTextUsingNormalizer(rawResponse, 'openai', apiVariant);
|
|
200
|
+
// Extract usage using ai-io-normalizer if possible, otherwise manual extraction
|
|
201
|
+
let usage;
|
|
202
|
+
if (normalizerAvailable && normalizeProviderResponse) {
|
|
203
|
+
try {
|
|
204
|
+
const maps = getResponseMaps();
|
|
205
|
+
if (maps) {
|
|
206
|
+
const normalized = normalizeProviderResponse(rawResponse, {
|
|
207
|
+
provider: 'openai',
|
|
208
|
+
apiVariant,
|
|
209
|
+
mode: 'standard',
|
|
210
|
+
keepRawOverride: false,
|
|
211
|
+
}, maps);
|
|
212
|
+
if (normalized.usage) {
|
|
213
|
+
usage = {
|
|
214
|
+
inputTokens: normalized.usage.prompt_tokens,
|
|
215
|
+
outputTokens: normalized.usage.completion_tokens,
|
|
216
|
+
totalTokens: normalized.usage.total_tokens,
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
catch (error) {
|
|
222
|
+
// Fallback to manual extraction
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
// Fallback to manual extraction if normalizer not available or failed
|
|
226
|
+
if (!usage && rawResponse?.usage) {
|
|
227
|
+
usage = {
|
|
228
|
+
inputTokens: rawResponse.usage.input_tokens || rawResponse.usage.prompt_tokens,
|
|
229
|
+
outputTokens: rawResponse.usage.output_tokens || rawResponse.usage.completion_tokens,
|
|
230
|
+
totalTokens: rawResponse.usage.total_tokens,
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
// Build complete response object for ai-activities storage
|
|
234
|
+
const fullResponse = {
|
|
235
|
+
requestId,
|
|
236
|
+
provider: 'openai',
|
|
237
|
+
rawResponse,
|
|
238
|
+
outputText,
|
|
239
|
+
usage,
|
|
240
|
+
reasoning: {
|
|
241
|
+
requested: { effort: 'none', visibility: 'none' },
|
|
242
|
+
applied: { effort: 'none', visibility: 'none' },
|
|
243
|
+
artifacts: {},
|
|
244
|
+
availability: {
|
|
245
|
+
supportsEffort: false,
|
|
246
|
+
supportsSummary: false,
|
|
247
|
+
supportsTrace: false,
|
|
248
|
+
supportsEncrypted: false,
|
|
249
|
+
},
|
|
250
|
+
},
|
|
251
|
+
metadata: {
|
|
252
|
+
model: rawResponse?.model,
|
|
253
|
+
id: rawResponse?.id,
|
|
254
|
+
status: rawResponse?.status,
|
|
255
|
+
...execResult.rawMeta,
|
|
256
|
+
},
|
|
257
|
+
};
|
|
258
|
+
// IMPORTANT: Create a clean copy of fullResponse without circular reference
|
|
259
|
+
const fullResponseForActivities = {
|
|
260
|
+
requestId: fullResponse.requestId,
|
|
261
|
+
provider: fullResponse.provider,
|
|
262
|
+
rawResponse: fullResponse.rawResponse,
|
|
263
|
+
outputText: fullResponse.outputText,
|
|
264
|
+
usage: fullResponse.usage,
|
|
265
|
+
reasoning: fullResponse.reasoning,
|
|
266
|
+
// Don't include metadata in the copy to avoid circular reference
|
|
267
|
+
};
|
|
268
|
+
return {
|
|
269
|
+
...fullResponse,
|
|
270
|
+
metadata: {
|
|
271
|
+
...fullResponse.metadata,
|
|
272
|
+
// Include full response in metadata for ai-activities storage (without circular ref)
|
|
273
|
+
'ai-activities-response': fullResponseForActivities,
|
|
274
|
+
// Include request for complete audit trail
|
|
275
|
+
'ai-activities-request': input.request,
|
|
276
|
+
'ai-activities-raw-response': rawResponse,
|
|
277
|
+
'ai-activities-exec-meta': execResult.rawMeta,
|
|
278
|
+
},
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
parseStreamChunk(input) {
|
|
282
|
+
const { requestId, chunk } = input;
|
|
283
|
+
const raw = chunk.raw;
|
|
284
|
+
const events = [];
|
|
285
|
+
// Emit provider_raw event
|
|
286
|
+
events.push({
|
|
287
|
+
type: 'provider_raw',
|
|
288
|
+
requestId,
|
|
289
|
+
provider: 'openai',
|
|
290
|
+
raw,
|
|
291
|
+
});
|
|
292
|
+
// Parse OpenAI stream chunk format
|
|
293
|
+
// OpenAI Responses API stream format: { output: [{ content: { text_delta: "..." } }] }
|
|
294
|
+
if (raw?.output && Array.isArray(raw.output)) {
|
|
295
|
+
for (const output of raw.output) {
|
|
296
|
+
if (output.content?.text_delta) {
|
|
297
|
+
events.push({
|
|
298
|
+
type: 'output_text_delta',
|
|
299
|
+
requestId,
|
|
300
|
+
delta: output.content.text_delta,
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
return events;
|
|
306
|
+
}
|
|
307
|
+
finalizeStream(input) {
|
|
308
|
+
const { requestId, collected, finalRaw } = input;
|
|
309
|
+
// Use finalRaw if available, otherwise use last raw event
|
|
310
|
+
const rawResponse = finalRaw || (collected.rawEvents.length > 0 ? collected.rawEvents[collected.rawEvents.length - 1] : {});
|
|
311
|
+
// Build complete response object for ai-activities storage
|
|
312
|
+
const fullResponse = {
|
|
313
|
+
requestId,
|
|
314
|
+
provider: 'openai',
|
|
315
|
+
rawResponse,
|
|
316
|
+
outputText: collected.outputText,
|
|
317
|
+
reasoning: {
|
|
318
|
+
requested: { effort: 'none', visibility: 'none' },
|
|
319
|
+
applied: { effort: 'none', visibility: 'none' },
|
|
320
|
+
artifacts: {},
|
|
321
|
+
availability: {
|
|
322
|
+
supportsEffort: false,
|
|
323
|
+
supportsSummary: false,
|
|
324
|
+
supportsTrace: false,
|
|
325
|
+
supportsEncrypted: false,
|
|
326
|
+
},
|
|
327
|
+
},
|
|
328
|
+
metadata: {},
|
|
329
|
+
};
|
|
330
|
+
// IMPORTANT: Create a clean copy of fullResponse without circular reference
|
|
331
|
+
const fullResponseForActivities = {
|
|
332
|
+
requestId: fullResponse.requestId,
|
|
333
|
+
provider: fullResponse.provider,
|
|
334
|
+
rawResponse: fullResponse.rawResponse,
|
|
335
|
+
outputText: fullResponse.outputText,
|
|
336
|
+
reasoning: fullResponse.reasoning,
|
|
337
|
+
// Don't include metadata in the copy to avoid circular reference
|
|
338
|
+
};
|
|
339
|
+
// Add usage if available (may not be present in streaming responses)
|
|
340
|
+
if ('usage' in fullResponse && fullResponse.usage) {
|
|
341
|
+
fullResponseForActivities.usage = fullResponse.usage;
|
|
342
|
+
}
|
|
343
|
+
return {
|
|
344
|
+
...fullResponse,
|
|
345
|
+
metadata: {
|
|
346
|
+
...fullResponse.metadata,
|
|
347
|
+
// Include full response in metadata for ai-activities storage (without circular ref)
|
|
348
|
+
'ai-activities-response': fullResponseForActivities,
|
|
349
|
+
// Include request for complete audit trail
|
|
350
|
+
'ai-activities-request': input.request,
|
|
351
|
+
'ai-activities-raw-response': rawResponse,
|
|
352
|
+
'ai-activities-collected-events': collected.rawEvents,
|
|
353
|
+
},
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
parseBatchItem(input) {
|
|
357
|
+
const { requestId, item } = input;
|
|
358
|
+
if (item.error) {
|
|
359
|
+
return {
|
|
360
|
+
requestId,
|
|
361
|
+
error: item.error,
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
// Parse OpenAI batch item response using ai-io-normalizer
|
|
365
|
+
const rawResponse = item.rawResponse;
|
|
366
|
+
let outputText;
|
|
367
|
+
if (normalizerAvailable && normalizeProviderResponse) {
|
|
368
|
+
try {
|
|
369
|
+
const maps = getResponseMaps();
|
|
370
|
+
if (maps) {
|
|
371
|
+
// Default to responses API for batch (most common)
|
|
372
|
+
const apiVariant = 'openai.responses';
|
|
373
|
+
const normalized = normalizeProviderResponse(rawResponse, {
|
|
374
|
+
provider: 'openai',
|
|
375
|
+
apiVariant,
|
|
376
|
+
mode: 'batch',
|
|
377
|
+
keepRawOverride: false,
|
|
378
|
+
}, maps);
|
|
379
|
+
if (normalized.choices && normalized.choices.length > 0) {
|
|
380
|
+
outputText = normalized.choices[0].message?.content;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
catch (error) {
|
|
385
|
+
// Fallback to manual extraction
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
// Fallback to manual extraction if normalizer not available or failed
|
|
389
|
+
if (!outputText && rawResponse?.output && Array.isArray(rawResponse.output) && rawResponse.output.length > 0) {
|
|
390
|
+
const firstOutput = rawResponse.output[0];
|
|
391
|
+
if (firstOutput.content?.text) {
|
|
392
|
+
outputText = firstOutput.content.text;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
return {
|
|
396
|
+
requestId,
|
|
397
|
+
rawResponse,
|
|
398
|
+
outputText,
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { ProviderSDKCallSpec, ProviderSDKExecResult, ProviderSDKStreamChunk, ProviderBatchResults } from '@x12i/ai-provider-interface';
|
|
2
|
+
import type { RouterAdapter } from '../../registry/AdapterRegistry.js';
|
|
3
|
+
import type { AIResponse, AIStreamEvent } from '../../router/RouterTypes.js';
|
|
4
|
+
/**
|
|
5
|
+
* Router-side adapter for OpenRouter provider
|
|
6
|
+
* Converts router requests to ProviderSDKCallSpec and parses responses
|
|
7
|
+
* Does NOT use ai-io-normalizer - parses OpenAI-compatible responses directly
|
|
8
|
+
*/
|
|
9
|
+
export declare class OpenRouterAdapter implements RouterAdapter {
|
|
10
|
+
provider: string;
|
|
11
|
+
/**
|
|
12
|
+
* Normalize model name for OpenRouter using catalog data
|
|
13
|
+
* Uses the catalog to properly map provider + model to OpenRouter format
|
|
14
|
+
*/
|
|
15
|
+
private normalizeModelName;
|
|
16
|
+
/**
|
|
17
|
+
* Map provider name to OpenRouter model prefix (legacy fallback)
|
|
18
|
+
*/
|
|
19
|
+
private getProviderPrefix;
|
|
20
|
+
/**
|
|
21
|
+
* Get reasoning capabilities for a model
|
|
22
|
+
* Uses JSON registry for cross-vendor support detection
|
|
23
|
+
*/
|
|
24
|
+
private getReasoningCapabilities;
|
|
25
|
+
/**
|
|
26
|
+
* Map unified reasoning request to provider-specific format
|
|
27
|
+
* Implements deterministic downgrade rules based on capabilities
|
|
28
|
+
* Supports both effort (OpenAI/xAI) and max_tokens (Anthropic/Gemini) modes
|
|
29
|
+
*/
|
|
30
|
+
private mapReasoningToProvider;
|
|
31
|
+
buildCallSpec(input: {
|
|
32
|
+
requestId: string;
|
|
33
|
+
mode: 'sync' | 'stream';
|
|
34
|
+
request: any;
|
|
35
|
+
exec?: {
|
|
36
|
+
timeoutMs?: number;
|
|
37
|
+
retries?: number;
|
|
38
|
+
idempotencyKey?: string;
|
|
39
|
+
signal?: AbortSignal;
|
|
40
|
+
};
|
|
41
|
+
}): Promise<ProviderSDKCallSpec>;
|
|
42
|
+
parseResponse(input: {
|
|
43
|
+
requestId: string;
|
|
44
|
+
request: any;
|
|
45
|
+
execResult: ProviderSDKExecResult;
|
|
46
|
+
}): AIResponse;
|
|
47
|
+
/**
|
|
48
|
+
* Comprehensive content extraction function
|
|
49
|
+
* Tries all possible locations and formats to extract text content
|
|
50
|
+
* Returns the extracted text or undefined if not found
|
|
51
|
+
*/
|
|
52
|
+
private extractContentFromResponse;
|
|
53
|
+
/**
|
|
54
|
+
* Build unified reasoning response from request info and parsed artifacts
|
|
55
|
+
* Implements deterministic downgrade rules based on what was actually returned
|
|
56
|
+
* This is the ONLY source of truth for reasoning response structure
|
|
57
|
+
*/
|
|
58
|
+
private buildReasoningResponse;
|
|
59
|
+
/**
|
|
60
|
+
* Log reasoning processing summary
|
|
61
|
+
*/
|
|
62
|
+
private logReasoningSummary;
|
|
63
|
+
parseStreamChunk(input: {
|
|
64
|
+
requestId: string;
|
|
65
|
+
request: any;
|
|
66
|
+
chunk: ProviderSDKStreamChunk;
|
|
67
|
+
}): AIStreamEvent[];
|
|
68
|
+
finalizeStream(input: {
|
|
69
|
+
requestId: string;
|
|
70
|
+
request: any;
|
|
71
|
+
collected: {
|
|
72
|
+
rawEvents: unknown[];
|
|
73
|
+
outputText?: string;
|
|
74
|
+
};
|
|
75
|
+
finalRaw?: unknown;
|
|
76
|
+
}): AIResponse;
|
|
77
|
+
parseBatchItem(input: {
|
|
78
|
+
requestId: string;
|
|
79
|
+
request: any;
|
|
80
|
+
item: ProviderBatchResults['items'][number];
|
|
81
|
+
}): {
|
|
82
|
+
requestId: string;
|
|
83
|
+
rawResponse?: unknown;
|
|
84
|
+
outputText?: string;
|
|
85
|
+
error?: any;
|
|
86
|
+
};
|
|
87
|
+
}
|