@khanglvm/llm-router 1.1.1 → 1.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.
- package/CHANGELOG.md +40 -0
- package/README.md +29 -14
- package/package.json +1 -1
- package/src/cli/router-module.js +1469 -565
- package/src/node/config-workflows.js +3 -1
- package/src/runtime/codex-request-transformer.js +284 -28
- package/src/runtime/codex-response-transformer.js +433 -0
- package/src/runtime/config.js +21 -15
- package/src/runtime/handler/provider-call.js +217 -106
- package/src/runtime/subscription-auth.js +228 -95
- package/src/runtime/subscription-constants.js +43 -7
- package/src/runtime/subscription-provider.js +311 -38
|
@@ -10,13 +10,20 @@ import {
|
|
|
10
10
|
CODEX_ENDPOINT,
|
|
11
11
|
mapCodexVariant
|
|
12
12
|
} from './codex-request-transformer.js';
|
|
13
|
+
import {
|
|
14
|
+
CLAUDE_CODE_OAUTH_CONFIG
|
|
15
|
+
} from './subscription-constants.js';
|
|
13
16
|
import { FORMATS } from '../translator/index.js';
|
|
14
17
|
|
|
18
|
+
const UNSUPPORTED_PARAMETER_PATTERN = /Unsupported parameter:\s*([A-Za-z0-9_.-]+)/gi;
|
|
19
|
+
const MAX_UNSUPPORTED_PARAMETER_RETRIES = 6;
|
|
20
|
+
|
|
15
21
|
/**
|
|
16
22
|
* Subscription provider types.
|
|
17
23
|
*/
|
|
18
24
|
export const SUBSCRIPTION_TYPES = {
|
|
19
|
-
CHATGPT_CODEX: 'chatgpt-codex'
|
|
25
|
+
CHATGPT_CODEX: 'chatgpt-codex',
|
|
26
|
+
CLAUDE_CODE: 'claude-code'
|
|
20
27
|
};
|
|
21
28
|
|
|
22
29
|
/**
|
|
@@ -105,7 +112,7 @@ export async function makeSubscriptionProviderCall({ provider, body, stream }) {
|
|
|
105
112
|
// Get valid access token (auto-refreshes if expired)
|
|
106
113
|
let accessToken;
|
|
107
114
|
try {
|
|
108
|
-
accessToken = await getValidAccessToken(profileId);
|
|
115
|
+
accessToken = await getValidAccessToken(profileId, { subscriptionType: subType });
|
|
109
116
|
} catch (error) {
|
|
110
117
|
return {
|
|
111
118
|
ok: false,
|
|
@@ -148,6 +155,9 @@ export async function makeSubscriptionProviderCall({ provider, body, stream }) {
|
|
|
148
155
|
if (subType === SUBSCRIPTION_TYPES.CHATGPT_CODEX) {
|
|
149
156
|
return makeCodexProviderCall({ provider, body, stream, accessToken });
|
|
150
157
|
}
|
|
158
|
+
if (subType === SUBSCRIPTION_TYPES.CLAUDE_CODE) {
|
|
159
|
+
return makeClaudeCodeProviderCall({ provider, body, stream, accessToken });
|
|
160
|
+
}
|
|
151
161
|
|
|
152
162
|
return {
|
|
153
163
|
ok: false,
|
|
@@ -176,6 +186,7 @@ export async function makeSubscriptionProviderCall({ provider, body, stream }) {
|
|
|
176
186
|
async function makeCodexProviderCall({ provider, body, stream, accessToken }) {
|
|
177
187
|
// Transform request for Codex backend
|
|
178
188
|
const codexBody = transformRequestForCodex(body);
|
|
189
|
+
stripCodexTokenLimitFields(codexBody);
|
|
179
190
|
|
|
180
191
|
// Apply variant settings if specified in model config
|
|
181
192
|
const modelConfig = (provider.models || []).find(m => m.id === body.model);
|
|
@@ -188,15 +199,60 @@ async function makeCodexProviderCall({ provider, body, stream, accessToken }) {
|
|
|
188
199
|
|
|
189
200
|
// Make the request
|
|
190
201
|
try {
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
202
|
+
const removedUnsupportedParameters = new Set();
|
|
203
|
+
for (let attempt = 0; attempt <= MAX_UNSUPPORTED_PARAMETER_RETRIES; attempt += 1) {
|
|
204
|
+
const response = await fetch(CODEX_ENDPOINT, {
|
|
205
|
+
method: 'POST',
|
|
206
|
+
headers,
|
|
207
|
+
body: JSON.stringify(codexBody),
|
|
208
|
+
signal: stream ? undefined : AbortSignal.timeout(120000) // 2 min timeout for non-streaming
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
if (response.ok) {
|
|
212
|
+
// For streaming, pass through the response
|
|
213
|
+
if (stream) {
|
|
214
|
+
return {
|
|
215
|
+
ok: true,
|
|
216
|
+
status: 200,
|
|
217
|
+
retryable: false,
|
|
218
|
+
response: new Response(response.body, {
|
|
219
|
+
status: 200,
|
|
220
|
+
headers: {
|
|
221
|
+
'Content-Type': 'text/event-stream',
|
|
222
|
+
'Cache-Control': 'no-cache',
|
|
223
|
+
'Connection': 'keep-alive'
|
|
224
|
+
}
|
|
225
|
+
})
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// For non-streaming, pass through
|
|
230
|
+
const responseText = await response.text();
|
|
231
|
+
return {
|
|
232
|
+
ok: true,
|
|
233
|
+
status: 200,
|
|
234
|
+
retryable: false,
|
|
235
|
+
response: new Response(responseText, {
|
|
236
|
+
status: 200,
|
|
237
|
+
headers: { 'Content-Type': 'application/json' }
|
|
238
|
+
})
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
|
|
199
242
|
const errorText = await response.text();
|
|
243
|
+
const unsupportedParameters = extractUnsupportedParameters(errorText);
|
|
244
|
+
let removedAnyUnsupportedParameter = false;
|
|
245
|
+
for (const parameter of unsupportedParameters) {
|
|
246
|
+
const normalized = parameter.toLowerCase();
|
|
247
|
+
if (removedUnsupportedParameters.has(normalized)) continue;
|
|
248
|
+
if (!removeUnsupportedParameter(codexBody, parameter)) continue;
|
|
249
|
+
removedUnsupportedParameters.add(normalized);
|
|
250
|
+
removedAnyUnsupportedParameter = true;
|
|
251
|
+
}
|
|
252
|
+
if (removedAnyUnsupportedParameter) {
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
|
|
200
256
|
return {
|
|
201
257
|
ok: false,
|
|
202
258
|
status: response.status,
|
|
@@ -208,48 +264,145 @@ async function makeCodexProviderCall({ provider, body, stream, accessToken }) {
|
|
|
208
264
|
})
|
|
209
265
|
};
|
|
210
266
|
}
|
|
267
|
+
|
|
268
|
+
return {
|
|
269
|
+
ok: false,
|
|
270
|
+
status: 400,
|
|
271
|
+
retryable: false,
|
|
272
|
+
errorKind: 'provider_error',
|
|
273
|
+
response: new Response(JSON.stringify({
|
|
274
|
+
detail: 'Codex request failed after removing unsupported parameters.'
|
|
275
|
+
}), {
|
|
276
|
+
status: 400,
|
|
277
|
+
headers: { 'Content-Type': 'application/json' }
|
|
278
|
+
})
|
|
279
|
+
};
|
|
280
|
+
} catch (error) {
|
|
281
|
+
// Handle timeout or network errors
|
|
282
|
+
if (error.name === 'TimeoutError' || error.name === 'AbortError') {
|
|
283
|
+
return {
|
|
284
|
+
ok: false,
|
|
285
|
+
status: 504,
|
|
286
|
+
retryable: true,
|
|
287
|
+
errorKind: 'timeout_error',
|
|
288
|
+
response: new Response(JSON.stringify({
|
|
289
|
+
type: 'error',
|
|
290
|
+
error: {
|
|
291
|
+
type: 'timeout_error',
|
|
292
|
+
message: 'Codex API request timed out'
|
|
293
|
+
}
|
|
294
|
+
}), {
|
|
295
|
+
status: 504,
|
|
296
|
+
headers: { 'Content-Type': 'application/json' }
|
|
297
|
+
})
|
|
298
|
+
};
|
|
299
|
+
}
|
|
211
300
|
|
|
212
|
-
|
|
213
|
-
|
|
301
|
+
return {
|
|
302
|
+
ok: false,
|
|
303
|
+
status: 503,
|
|
304
|
+
retryable: true,
|
|
305
|
+
errorKind: 'network_error',
|
|
306
|
+
response: new Response(JSON.stringify({
|
|
307
|
+
type: 'error',
|
|
308
|
+
error: {
|
|
309
|
+
type: 'api_error',
|
|
310
|
+
message: `Codex API network error: ${error instanceof Error ? error.message : String(error)}`
|
|
311
|
+
}
|
|
312
|
+
}), {
|
|
313
|
+
status: 503,
|
|
314
|
+
headers: { 'Content-Type': 'application/json' }
|
|
315
|
+
})
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Make a Claude Code OAuth API call.
|
|
322
|
+
*
|
|
323
|
+
* @param {Object} options - Call options
|
|
324
|
+
* @returns {Promise<Object>} Call result
|
|
325
|
+
*/
|
|
326
|
+
async function makeClaudeCodeProviderCall({ provider, body, stream, accessToken }) {
|
|
327
|
+
const apiBaseUrl = String(CLAUDE_CODE_OAUTH_CONFIG.apiBaseUrl || '').replace(/\/+$/, '');
|
|
328
|
+
const messagesPath = String(CLAUDE_CODE_OAUTH_CONFIG.messagesPath || '/v1/messages?beta=true');
|
|
329
|
+
const endpoint = `${apiBaseUrl}${messagesPath.startsWith('/') ? messagesPath : `/${messagesPath}`}`;
|
|
330
|
+
const headers = {
|
|
331
|
+
'Content-Type': 'application/json',
|
|
332
|
+
'Authorization': `Bearer ${accessToken}`,
|
|
333
|
+
'anthropic-beta': CLAUDE_CODE_OAUTH_CONFIG.oauthBeta,
|
|
334
|
+
'anthropic-version': provider?.anthropicVersion || '2023-06-01',
|
|
335
|
+
...(provider.headers || {})
|
|
336
|
+
};
|
|
337
|
+
const claudeBody = {
|
|
338
|
+
...(body || {}),
|
|
339
|
+
stream: Boolean(stream)
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
try {
|
|
343
|
+
const response = await fetch(endpoint, {
|
|
344
|
+
method: 'POST',
|
|
345
|
+
headers,
|
|
346
|
+
body: JSON.stringify(claudeBody),
|
|
347
|
+
signal: stream ? undefined : AbortSignal.timeout(120000)
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
if (response.ok) {
|
|
351
|
+
if (stream) {
|
|
352
|
+
return {
|
|
353
|
+
ok: true,
|
|
354
|
+
status: 200,
|
|
355
|
+
retryable: false,
|
|
356
|
+
subscriptionType: SUBSCRIPTION_TYPES.CLAUDE_CODE,
|
|
357
|
+
response: new Response(response.body, {
|
|
358
|
+
status: 200,
|
|
359
|
+
headers: {
|
|
360
|
+
'Content-Type': 'text/event-stream',
|
|
361
|
+
'Cache-Control': 'no-cache',
|
|
362
|
+
'Connection': 'keep-alive'
|
|
363
|
+
}
|
|
364
|
+
})
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
const responseText = await response.text();
|
|
214
369
|
return {
|
|
215
370
|
ok: true,
|
|
216
371
|
status: 200,
|
|
217
372
|
retryable: false,
|
|
218
|
-
|
|
373
|
+
subscriptionType: SUBSCRIPTION_TYPES.CLAUDE_CODE,
|
|
374
|
+
response: new Response(responseText, {
|
|
219
375
|
status: 200,
|
|
220
|
-
headers: {
|
|
221
|
-
'Content-Type': 'text/event-stream',
|
|
222
|
-
'Cache-Control': 'no-cache',
|
|
223
|
-
'Connection': 'keep-alive'
|
|
224
|
-
}
|
|
376
|
+
headers: { 'Content-Type': 'application/json' }
|
|
225
377
|
})
|
|
226
378
|
};
|
|
227
379
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
const responseText = await response.text();
|
|
380
|
+
|
|
381
|
+
const errorText = await response.text();
|
|
231
382
|
return {
|
|
232
|
-
ok:
|
|
233
|
-
status:
|
|
234
|
-
retryable:
|
|
235
|
-
|
|
236
|
-
|
|
383
|
+
ok: false,
|
|
384
|
+
status: response.status,
|
|
385
|
+
retryable: isRetryableStatus(response.status),
|
|
386
|
+
errorKind: 'provider_error',
|
|
387
|
+
subscriptionType: SUBSCRIPTION_TYPES.CLAUDE_CODE,
|
|
388
|
+
response: new Response(errorText, {
|
|
389
|
+
status: response.status,
|
|
237
390
|
headers: { 'Content-Type': 'application/json' }
|
|
238
391
|
})
|
|
239
392
|
};
|
|
240
393
|
} catch (error) {
|
|
241
|
-
// Handle timeout or network errors
|
|
242
394
|
if (error.name === 'TimeoutError' || error.name === 'AbortError') {
|
|
243
395
|
return {
|
|
244
396
|
ok: false,
|
|
245
397
|
status: 504,
|
|
246
398
|
retryable: true,
|
|
247
399
|
errorKind: 'timeout_error',
|
|
400
|
+
subscriptionType: SUBSCRIPTION_TYPES.CLAUDE_CODE,
|
|
248
401
|
response: new Response(JSON.stringify({
|
|
249
402
|
type: 'error',
|
|
250
403
|
error: {
|
|
251
404
|
type: 'timeout_error',
|
|
252
|
-
message: '
|
|
405
|
+
message: 'Claude Code OAuth API request timed out'
|
|
253
406
|
}
|
|
254
407
|
}), {
|
|
255
408
|
status: 504,
|
|
@@ -257,17 +410,18 @@ async function makeCodexProviderCall({ provider, body, stream, accessToken }) {
|
|
|
257
410
|
})
|
|
258
411
|
};
|
|
259
412
|
}
|
|
260
|
-
|
|
413
|
+
|
|
261
414
|
return {
|
|
262
415
|
ok: false,
|
|
263
416
|
status: 503,
|
|
264
417
|
retryable: true,
|
|
265
418
|
errorKind: 'network_error',
|
|
419
|
+
subscriptionType: SUBSCRIPTION_TYPES.CLAUDE_CODE,
|
|
266
420
|
response: new Response(JSON.stringify({
|
|
267
421
|
type: 'error',
|
|
268
422
|
error: {
|
|
269
423
|
type: 'api_error',
|
|
270
|
-
message: `
|
|
424
|
+
message: `Claude Code OAuth API network error: ${error instanceof Error ? error.message : String(error)}`
|
|
271
425
|
}
|
|
272
426
|
}), {
|
|
273
427
|
status: 503,
|
|
@@ -287,6 +441,100 @@ function isRetryableStatus(status) {
|
|
|
287
441
|
return status === 429 || (status >= 500 && status < 600);
|
|
288
442
|
}
|
|
289
443
|
|
|
444
|
+
function stripCodexTokenLimitFields(body) {
|
|
445
|
+
if (!body || typeof body !== 'object') return;
|
|
446
|
+
delete body.max_tokens;
|
|
447
|
+
delete body.max_output_tokens;
|
|
448
|
+
delete body.max_completion_tokens;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
function extractUnsupportedParameters(errorText) {
|
|
452
|
+
const detail = extractErrorDetail(errorText);
|
|
453
|
+
if (!detail) return [];
|
|
454
|
+
const matches = [];
|
|
455
|
+
let match = UNSUPPORTED_PARAMETER_PATTERN.exec(detail);
|
|
456
|
+
while (match) {
|
|
457
|
+
const name = String(match[1] || '').trim();
|
|
458
|
+
if (name) matches.push(name);
|
|
459
|
+
match = UNSUPPORTED_PARAMETER_PATTERN.exec(detail);
|
|
460
|
+
}
|
|
461
|
+
UNSUPPORTED_PARAMETER_PATTERN.lastIndex = 0;
|
|
462
|
+
return [...new Set(matches)];
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
function extractErrorDetail(errorText) {
|
|
466
|
+
const raw = String(errorText || '').trim();
|
|
467
|
+
if (!raw) return '';
|
|
468
|
+
try {
|
|
469
|
+
const parsed = JSON.parse(raw);
|
|
470
|
+
if (typeof parsed?.detail === 'string' && parsed.detail.trim()) return parsed.detail.trim();
|
|
471
|
+
if (typeof parsed?.error?.message === 'string' && parsed.error.message.trim()) return parsed.error.message.trim();
|
|
472
|
+
if (typeof parsed?.message === 'string' && parsed.message.trim()) return parsed.message.trim();
|
|
473
|
+
} catch {
|
|
474
|
+
// keep raw payload if not JSON
|
|
475
|
+
}
|
|
476
|
+
return raw;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
function removeUnsupportedParameter(body, parameterPath) {
|
|
480
|
+
const normalizedPath = String(parameterPath || '').trim();
|
|
481
|
+
if (!normalizedPath || !body || typeof body !== 'object') return false;
|
|
482
|
+
|
|
483
|
+
if (Object.prototype.hasOwnProperty.call(body, normalizedPath)) {
|
|
484
|
+
delete body[normalizedPath];
|
|
485
|
+
return true;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
const parts = normalizedPath
|
|
489
|
+
.replace(/\[(\d+)\]/g, '.$1')
|
|
490
|
+
.split('.')
|
|
491
|
+
.filter(Boolean);
|
|
492
|
+
if (parts.length < 2) {
|
|
493
|
+
return removeKeysRecursively(body, normalizedPath);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
let node = body;
|
|
497
|
+
for (let index = 0; index < parts.length - 1; index += 1) {
|
|
498
|
+
const segment = parts[index];
|
|
499
|
+
if (!node || typeof node !== 'object' || !Object.prototype.hasOwnProperty.call(node, segment)) {
|
|
500
|
+
return false;
|
|
501
|
+
}
|
|
502
|
+
node = node[segment];
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
const leaf = parts[parts.length - 1];
|
|
506
|
+
if (!node || typeof node !== 'object' || !Object.prototype.hasOwnProperty.call(node, leaf)) {
|
|
507
|
+
return removeKeysRecursively(body, leaf);
|
|
508
|
+
}
|
|
509
|
+
delete node[leaf];
|
|
510
|
+
return true;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
function removeKeysRecursively(node, targetKey) {
|
|
514
|
+
if (!node || typeof node !== 'object') return false;
|
|
515
|
+
let removed = false;
|
|
516
|
+
if (Array.isArray(node)) {
|
|
517
|
+
for (const item of node) {
|
|
518
|
+
if (removeKeysRecursively(item, targetKey)) {
|
|
519
|
+
removed = true;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
return removed;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
for (const key of Object.keys(node)) {
|
|
526
|
+
if (key === targetKey) {
|
|
527
|
+
delete node[key];
|
|
528
|
+
removed = true;
|
|
529
|
+
continue;
|
|
530
|
+
}
|
|
531
|
+
if (removeKeysRecursively(node[key], targetKey)) {
|
|
532
|
+
removed = true;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
return removed;
|
|
536
|
+
}
|
|
537
|
+
|
|
290
538
|
/**
|
|
291
539
|
* Login to a subscription provider.
|
|
292
540
|
*
|
|
@@ -300,11 +548,13 @@ function isRetryableStatus(status) {
|
|
|
300
548
|
export async function loginSubscription(profileId, options = {}) {
|
|
301
549
|
if (options.deviceCode) {
|
|
302
550
|
return loginWithDeviceCode(profileId, {
|
|
551
|
+
subscriptionType: options.subscriptionType,
|
|
303
552
|
onCode: options.onCode
|
|
304
553
|
});
|
|
305
554
|
}
|
|
306
555
|
|
|
307
556
|
return loginWithBrowser(profileId, {
|
|
557
|
+
subscriptionType: options.subscriptionType,
|
|
308
558
|
onUrl: options.onUrl
|
|
309
559
|
});
|
|
310
560
|
}
|
|
@@ -313,19 +563,27 @@ export async function loginSubscription(profileId, options = {}) {
|
|
|
313
563
|
* Logout from a subscription provider.
|
|
314
564
|
*
|
|
315
565
|
* @param {string} profileId - Profile ID
|
|
566
|
+
* @param {Object} [options] - Options
|
|
567
|
+
* @param {string} [options.subscriptionType] - Subscription type
|
|
316
568
|
*/
|
|
317
|
-
export async function logoutSubscription(profileId) {
|
|
318
|
-
await logout(profileId
|
|
569
|
+
export async function logoutSubscription(profileId, options = {}) {
|
|
570
|
+
await logout(profileId, {
|
|
571
|
+
subscriptionType: options.subscriptionType || SUBSCRIPTION_TYPES.CHATGPT_CODEX
|
|
572
|
+
});
|
|
319
573
|
}
|
|
320
574
|
|
|
321
575
|
/**
|
|
322
576
|
* Get authentication status for a subscription profile.
|
|
323
577
|
*
|
|
324
578
|
* @param {string} profileId - Profile ID
|
|
579
|
+
* @param {Object} [options] - Options
|
|
580
|
+
* @param {string} [options.subscriptionType] - Subscription type
|
|
325
581
|
* @returns {Promise<Object>} Status object
|
|
326
582
|
*/
|
|
327
|
-
export async function getSubscriptionStatus(profileId) {
|
|
328
|
-
return getAuthStatus(profileId
|
|
583
|
+
export async function getSubscriptionStatus(profileId, options = {}) {
|
|
584
|
+
return getAuthStatus(profileId, {
|
|
585
|
+
subscriptionType: options.subscriptionType || SUBSCRIPTION_TYPES.CHATGPT_CODEX
|
|
586
|
+
});
|
|
329
587
|
}
|
|
330
588
|
|
|
331
589
|
/**
|
|
@@ -337,25 +595,40 @@ export async function getSubscriptionStatus(profileId) {
|
|
|
337
595
|
* @returns {Object} Headers object
|
|
338
596
|
*/
|
|
339
597
|
export function buildSubscriptionProviderHeaders(provider, accessToken) {
|
|
340
|
-
|
|
598
|
+
const subType = provider?.subscriptionType || provider?.subscription_type;
|
|
599
|
+
const headers = {
|
|
341
600
|
'Content-Type': 'application/json',
|
|
342
601
|
'Authorization': `Bearer ${accessToken}`,
|
|
343
602
|
...(provider.headers || {})
|
|
344
603
|
};
|
|
604
|
+
if (subType === SUBSCRIPTION_TYPES.CLAUDE_CODE) {
|
|
605
|
+
headers['anthropic-beta'] = CLAUDE_CODE_OAUTH_CONFIG.oauthBeta;
|
|
606
|
+
headers['anthropic-version'] = provider?.anthropicVersion || '2023-06-01';
|
|
607
|
+
}
|
|
608
|
+
return headers;
|
|
345
609
|
}
|
|
346
610
|
|
|
347
611
|
/**
|
|
348
612
|
* Get the target format for a subscription provider.
|
|
349
|
-
*
|
|
613
|
+
* Target format depends on subscription provider type.
|
|
350
614
|
*
|
|
351
615
|
* @param {Object} provider - Provider config
|
|
352
616
|
* @param {string} sourceFormat - Source format
|
|
353
617
|
* @returns {string} Target format
|
|
354
618
|
*/
|
|
355
619
|
export function resolveSubscriptionProviderFormat(provider, sourceFormat) {
|
|
356
|
-
|
|
620
|
+
void sourceFormat;
|
|
621
|
+
const subType = provider?.subscriptionType || provider?.subscription_type;
|
|
622
|
+
if (subType === SUBSCRIPTION_TYPES.CLAUDE_CODE) {
|
|
623
|
+
return FORMATS.CLAUDE;
|
|
624
|
+
}
|
|
357
625
|
return FORMATS.OPENAI;
|
|
358
626
|
}
|
|
359
627
|
|
|
360
628
|
// Re-export for convenience
|
|
361
|
-
export {
|
|
629
|
+
export {
|
|
630
|
+
CODEX_SUBSCRIPTION_MODELS,
|
|
631
|
+
CODEX_OAUTH_CONFIG,
|
|
632
|
+
CLAUDE_CODE_SUBSCRIPTION_MODELS,
|
|
633
|
+
CLAUDE_CODE_OAUTH_CONFIG
|
|
634
|
+
} from './subscription-constants.js';
|