orquesta-cli 0.2.87 → 0.2.89
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.
|
@@ -96,7 +96,9 @@ export declare class LLMClient {
|
|
|
96
96
|
healthy: boolean;
|
|
97
97
|
latency?: number;
|
|
98
98
|
error?: string;
|
|
99
|
+
switchedToBatuta?: boolean;
|
|
99
100
|
}>;
|
|
101
|
+
private static probeEndpoint;
|
|
100
102
|
static testConnection(baseUrl: string, apiKey: string, model: string): Promise<{
|
|
101
103
|
success: boolean;
|
|
102
104
|
latency?: number;
|
|
@@ -6,6 +6,22 @@ import { LLMError, TokenLimitError, RateLimitError, ContextLengthError, } from '
|
|
|
6
6
|
import { logger, isLLMLogEnabled } from '../../utils/logger.js';
|
|
7
7
|
import { usageTracker } from '../usage-tracker.js';
|
|
8
8
|
import { getForcedTier, getBatutaSessionId, setLastBatutaRoute } from '../routing-state.js';
|
|
9
|
+
function safeStringify(value, space) {
|
|
10
|
+
const seen = new WeakSet();
|
|
11
|
+
try {
|
|
12
|
+
return JSON.stringify(value, (_key, val) => {
|
|
13
|
+
if (typeof val === 'object' && val !== null) {
|
|
14
|
+
if (seen.has(val))
|
|
15
|
+
return '[Circular]';
|
|
16
|
+
seen.add(val);
|
|
17
|
+
}
|
|
18
|
+
return val;
|
|
19
|
+
}, space);
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return String(value);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
9
25
|
function extractTopLevelJsonObjects(s) {
|
|
10
26
|
const objects = [];
|
|
11
27
|
let depth = 0;
|
|
@@ -972,11 +988,11 @@ export class LLMClient {
|
|
|
972
988
|
errorMessage,
|
|
973
989
|
errorType,
|
|
974
990
|
errorCode,
|
|
975
|
-
responseBody:
|
|
991
|
+
responseBody: safeStringify(data, 2),
|
|
976
992
|
requestMethod: requestContext?.method,
|
|
977
993
|
requestUrl: requestContext?.url,
|
|
978
994
|
requestBody: requestContext?.body
|
|
979
|
-
?
|
|
995
|
+
? safeStringify(requestContext.body, 2).substring(0, 5000)
|
|
980
996
|
: undefined,
|
|
981
997
|
responseHeaders: axiosError.response.headers,
|
|
982
998
|
});
|
|
@@ -1033,6 +1049,16 @@ export class LLMClient {
|
|
|
1033
1049
|
},
|
|
1034
1050
|
});
|
|
1035
1051
|
}
|
|
1052
|
+
if (status === 402) {
|
|
1053
|
+
logger.error('Batuta not available (402)', { errorMessage });
|
|
1054
|
+
return new APIError(errorMessage || 'Payment required', 402, this.baseUrl, {
|
|
1055
|
+
isRecoverable: false,
|
|
1056
|
+
cause: axiosError,
|
|
1057
|
+
userMessage: `Batuta isn't available for this account (402): ${errorMessage || 'add-on required'}.\n` +
|
|
1058
|
+
`Enable the Batuta add-on for your organization in the Orquesta dashboard, or point the CLI at a local LLM provider.`,
|
|
1059
|
+
details: { endpoint: this.baseUrl, fullError: data },
|
|
1060
|
+
});
|
|
1061
|
+
}
|
|
1036
1062
|
if (status === 401) {
|
|
1037
1063
|
logger.error('Authentication Failed', {
|
|
1038
1064
|
endpoint: this.baseUrl,
|
|
@@ -1276,6 +1302,22 @@ export class LLMClient {
|
|
|
1276
1302
|
if (!endpoint || !model) {
|
|
1277
1303
|
return { healthy: false, error: 'No endpoint/model configured' };
|
|
1278
1304
|
}
|
|
1305
|
+
const first = await LLMClient.probeEndpoint(endpoint, model.id);
|
|
1306
|
+
if (first.healthy)
|
|
1307
|
+
return first;
|
|
1308
|
+
const batuta = configManager.getAllEndpoints().find((e) => e.id === 'batuta-proxy' || e.provider === 'batuta');
|
|
1309
|
+
if (batuta && batuta.apiKey && batuta.id !== endpoint.id) {
|
|
1310
|
+
const m = batuta.models.find((x) => x.enabled) || batuta.models[0];
|
|
1311
|
+
if (m) {
|
|
1312
|
+
await configManager.setCurrentEndpoint(batuta.id);
|
|
1313
|
+
await configManager.setCurrentModel(m.id);
|
|
1314
|
+
const second = await LLMClient.probeEndpoint(batuta, m.id);
|
|
1315
|
+
return { ...second, switchedToBatuta: true };
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
return first;
|
|
1319
|
+
}
|
|
1320
|
+
static async probeEndpoint(endpoint, modelId) {
|
|
1279
1321
|
const startTime = Date.now();
|
|
1280
1322
|
try {
|
|
1281
1323
|
const axiosInstance = axios.create({
|
|
@@ -1287,7 +1329,7 @@ export class LLMClient {
|
|
|
1287
1329
|
timeout: 10000,
|
|
1288
1330
|
});
|
|
1289
1331
|
const response = await axiosInstance.post('/chat/completions', {
|
|
1290
|
-
model:
|
|
1332
|
+
model: modelId,
|
|
1291
1333
|
messages: [{ role: 'user', content: 'ping' }],
|
|
1292
1334
|
max_tokens: 1,
|
|
1293
1335
|
});
|
|
@@ -69,6 +69,7 @@ export async function runCliUpdate(version, onProgress) {
|
|
|
69
69
|
return new Promise((resolve) => {
|
|
70
70
|
const child = spawn('npm', ['install', '-g', `orquesta-cli@${version}`], {
|
|
71
71
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
72
|
+
shell: process.platform === 'win32',
|
|
72
73
|
});
|
|
73
74
|
let err = '';
|
|
74
75
|
child.stdout?.on('data', (d) => onProgress?.(d.toString().trim()));
|