orquesta-cli 0.2.75 → 0.2.77
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.
|
@@ -89,6 +89,11 @@ export declare class LLMClient {
|
|
|
89
89
|
latency?: number;
|
|
90
90
|
error?: string;
|
|
91
91
|
}[]>>;
|
|
92
|
+
static healthCheckCurrent(): Promise<{
|
|
93
|
+
healthy: boolean;
|
|
94
|
+
latency?: number;
|
|
95
|
+
error?: string;
|
|
96
|
+
}>;
|
|
92
97
|
static testConnection(baseUrl: string, apiKey: string, model: string): Promise<{
|
|
93
98
|
success: boolean;
|
|
94
99
|
latency?: number;
|
|
@@ -1213,6 +1213,52 @@ export class LLMClient {
|
|
|
1213
1213
|
}
|
|
1214
1214
|
return results;
|
|
1215
1215
|
}
|
|
1216
|
+
static async healthCheckCurrent() {
|
|
1217
|
+
const endpoint = configManager.getCurrentEndpoint();
|
|
1218
|
+
const model = configManager.getCurrentModel();
|
|
1219
|
+
if (!endpoint || !model) {
|
|
1220
|
+
return { healthy: false, error: 'No endpoint/model configured' };
|
|
1221
|
+
}
|
|
1222
|
+
const startTime = Date.now();
|
|
1223
|
+
try {
|
|
1224
|
+
const axiosInstance = axios.create({
|
|
1225
|
+
baseURL: endpoint.baseUrl,
|
|
1226
|
+
headers: {
|
|
1227
|
+
'Content-Type': 'application/json',
|
|
1228
|
+
...(endpoint.apiKey && { Authorization: `Bearer ${endpoint.apiKey}` }),
|
|
1229
|
+
},
|
|
1230
|
+
timeout: 10000,
|
|
1231
|
+
});
|
|
1232
|
+
const response = await axiosInstance.post('/chat/completions', {
|
|
1233
|
+
model: model.id,
|
|
1234
|
+
messages: [{ role: 'user', content: 'ping' }],
|
|
1235
|
+
max_tokens: 1,
|
|
1236
|
+
});
|
|
1237
|
+
const latency = Date.now() - startTime;
|
|
1238
|
+
if (response.status === 200 && response.data.choices?.[0]?.message) {
|
|
1239
|
+
return { healthy: true, latency };
|
|
1240
|
+
}
|
|
1241
|
+
return { healthy: false, latency, error: 'Invalid response' };
|
|
1242
|
+
}
|
|
1243
|
+
catch (error) {
|
|
1244
|
+
const latency = Date.now() - startTime;
|
|
1245
|
+
const axiosError = error;
|
|
1246
|
+
let errorMessage = 'Unknown error';
|
|
1247
|
+
if (axiosError.response) {
|
|
1248
|
+
errorMessage = `HTTP ${axiosError.response.status}`;
|
|
1249
|
+
}
|
|
1250
|
+
else if (axiosError.code === 'ECONNREFUSED') {
|
|
1251
|
+
errorMessage = 'Connection refused';
|
|
1252
|
+
}
|
|
1253
|
+
else if (axiosError.code === 'ETIMEDOUT' || axiosError.code === 'ECONNABORTED') {
|
|
1254
|
+
errorMessage = 'Timeout';
|
|
1255
|
+
}
|
|
1256
|
+
else if (axiosError.request) {
|
|
1257
|
+
errorMessage = 'Network error';
|
|
1258
|
+
}
|
|
1259
|
+
return { healthy: false, latency, error: errorMessage };
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1216
1262
|
static async testConnection(baseUrl, apiKey, model) {
|
|
1217
1263
|
const startTime = Date.now();
|
|
1218
1264
|
try {
|
|
@@ -470,30 +470,24 @@ export const PlanExecuteApp = ({ llmClient: initialLlmClient, modelInfo, resumeL
|
|
|
470
470
|
logger.startTimer('app-init');
|
|
471
471
|
try {
|
|
472
472
|
setInitStep('health');
|
|
473
|
-
logger.flow('
|
|
473
|
+
logger.flow('Kicking off background health probe + docs init');
|
|
474
474
|
setHealthStatus('checking');
|
|
475
|
-
|
|
475
|
+
void (async () => {
|
|
476
476
|
if (configManager.hasEndpoints()) {
|
|
477
|
-
const
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
logger.vars({ name: 'endpointId', value: endpointId }, { name: 'healthyModels', value: modelResults.filter(r => r.healthy).length });
|
|
481
|
-
if (modelResults.some((r) => r.healthy)) {
|
|
482
|
-
hasHealthy = true;
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
logger.state('Health status', 'checking', hasHealthy ? 'healthy' : 'unhealthy');
|
|
486
|
-
setHealthStatus(hasHealthy ? 'healthy' : 'unhealthy');
|
|
487
|
-
await configManager.updateAllHealthStatus(healthResults);
|
|
477
|
+
const result = await LLMClient.healthCheckCurrent();
|
|
478
|
+
logger.state('Health status', 'checking', result.healthy ? 'healthy' : 'unhealthy');
|
|
479
|
+
setHealthStatus(result.healthy ? 'healthy' : 'unhealthy');
|
|
488
480
|
}
|
|
489
481
|
else {
|
|
490
482
|
setHealthStatus('unknown');
|
|
491
483
|
}
|
|
492
|
-
})()
|
|
493
|
-
|
|
484
|
+
})().catch((err) => {
|
|
485
|
+
logger.warn('Background health probe failed', { error: err });
|
|
486
|
+
setHealthStatus('unknown');
|
|
487
|
+
});
|
|
488
|
+
await initializeDocsDirectory().catch((err) => {
|
|
494
489
|
logger.warn('Docs directory initialization warning', { error: err });
|
|
495
490
|
});
|
|
496
|
-
await Promise.all([healthPromise, docsPromise]);
|
|
497
491
|
setInitStep('config');
|
|
498
492
|
logger.flow('Checking configuration');
|
|
499
493
|
if (!configManager.hasEndpoints()) {
|
|
@@ -523,6 +517,24 @@ export const PlanExecuteApp = ({ llmClient: initialLlmClient, modelInfo, resumeL
|
|
|
523
517
|
await closeJsonStreamLogger();
|
|
524
518
|
exit();
|
|
525
519
|
}, [exit]);
|
|
520
|
+
const [resizeTick, setResizeTick] = useState(0);
|
|
521
|
+
useEffect(() => {
|
|
522
|
+
let timer = null;
|
|
523
|
+
const onResize = () => {
|
|
524
|
+
if (timer)
|
|
525
|
+
clearTimeout(timer);
|
|
526
|
+
timer = setTimeout(() => {
|
|
527
|
+
process.stdout.write('\x1b[2J\x1b[3J\x1b[H');
|
|
528
|
+
setResizeTick((t) => t + 1);
|
|
529
|
+
}, 150);
|
|
530
|
+
};
|
|
531
|
+
process.stdout.on('resize', onResize);
|
|
532
|
+
return () => {
|
|
533
|
+
if (timer)
|
|
534
|
+
clearTimeout(timer);
|
|
535
|
+
process.stdout.off('resize', onResize);
|
|
536
|
+
};
|
|
537
|
+
}, []);
|
|
526
538
|
useInput((inputChar, key) => {
|
|
527
539
|
if (key.ctrl && inputChar === 'c') {
|
|
528
540
|
const now = Date.now();
|
|
@@ -1446,7 +1458,7 @@ export const PlanExecuteApp = ({ llmClient: initialLlmClient, modelInfo, resumeL
|
|
|
1446
1458
|
}
|
|
1447
1459
|
};
|
|
1448
1460
|
return (React.createElement(Box, { flexDirection: "column", height: "100%" },
|
|
1449
|
-
React.createElement(Static, { items: logEntries }, (entry) => renderLogEntry(entry)),
|
|
1461
|
+
React.createElement(Static, { key: resizeTick, items: logEntries }, (entry) => renderLogEntry(entry)),
|
|
1450
1462
|
pendingToolApproval && (React.createElement(Box, { marginY: 1 },
|
|
1451
1463
|
React.createElement(ApprovalDialog, { toolName: pendingToolApproval.toolName, args: pendingToolApproval.args, reason: pendingToolApproval.reason, onResponse: handleApprovalResponse }))),
|
|
1452
1464
|
isProcessing && !pendingToolApproval && !isDocsSearching && (React.createElement(Box, { marginY: 1, flexDirection: "column" },
|