orquesta-cli 0.2.84 → 0.2.86
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/dist/cli.js +31 -0
- package/dist/core/llm/llm-client.d.ts +3 -0
- package/dist/core/llm/llm-client.js +33 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -41,6 +41,36 @@ async function readPromptFromStdin() {
|
|
|
41
41
|
}
|
|
42
42
|
return Buffer.concat(chunks).toString('utf-8').trim();
|
|
43
43
|
}
|
|
44
|
+
async function ensureBatutaFromEnv() {
|
|
45
|
+
const token = process.env['ORQUESTA_TOKEN'];
|
|
46
|
+
if (!token)
|
|
47
|
+
return;
|
|
48
|
+
const apiUrl = (process.env['ORQUESTA_API_URL'] || 'https://getorquesta.com').replace(/\/+$/, '');
|
|
49
|
+
if (configManager.getAllEndpoints().some((e) => e.id === 'batuta-proxy')) {
|
|
50
|
+
await configManager.removeEndpoint('batuta-proxy');
|
|
51
|
+
}
|
|
52
|
+
await configManager.addEndpoint({
|
|
53
|
+
id: 'batuta-proxy',
|
|
54
|
+
name: 'Batuta',
|
|
55
|
+
baseUrl: `${apiUrl}/api/v1`,
|
|
56
|
+
apiKey: token,
|
|
57
|
+
provider: 'batuta',
|
|
58
|
+
models: [{
|
|
59
|
+
id: 'batuta-auto',
|
|
60
|
+
name: 'Batuta Auto (smart routing)',
|
|
61
|
+
maxTokens: 200000,
|
|
62
|
+
enabled: true,
|
|
63
|
+
healthStatus: 'healthy',
|
|
64
|
+
lastHealthCheck: new Date(),
|
|
65
|
+
}],
|
|
66
|
+
createdAt: new Date(),
|
|
67
|
+
updatedAt: new Date(),
|
|
68
|
+
});
|
|
69
|
+
if (!configManager.getCurrentEndpoint()) {
|
|
70
|
+
await configManager.setCurrentEndpoint('batuta-proxy');
|
|
71
|
+
await configManager.setCurrentModel('batuta-auto');
|
|
72
|
+
}
|
|
73
|
+
}
|
|
44
74
|
async function resolveHookToken(explicitToken) {
|
|
45
75
|
await configManager.initialize();
|
|
46
76
|
const saved = configManager.getOrquestaConfig();
|
|
@@ -129,6 +159,7 @@ program
|
|
|
129
159
|
return;
|
|
130
160
|
}
|
|
131
161
|
await configManager.initialize();
|
|
162
|
+
await ensureBatutaFromEnv();
|
|
132
163
|
if (shouldShowOnboarding()) {
|
|
133
164
|
await runOnboarding();
|
|
134
165
|
}
|
|
@@ -43,6 +43,7 @@ export declare class LLMClient {
|
|
|
43
43
|
private apiKey;
|
|
44
44
|
private model;
|
|
45
45
|
private modelName;
|
|
46
|
+
private triedBatutaFallback;
|
|
46
47
|
private currentAbortController;
|
|
47
48
|
private isInterrupted;
|
|
48
49
|
onStreamingContent: ((token: string) => void) | null;
|
|
@@ -54,6 +55,8 @@ export declare class LLMClient {
|
|
|
54
55
|
checkInterrupted(): boolean;
|
|
55
56
|
resetInterrupt(): void;
|
|
56
57
|
isRequestActive(): boolean;
|
|
58
|
+
private isConnectionError;
|
|
59
|
+
private switchToBatutaProxy;
|
|
57
60
|
private isRetryableError;
|
|
58
61
|
private sleep;
|
|
59
62
|
chatCompletionStream(options: Partial<LLMRequestOptions>): AsyncGenerator<LLMStreamChunk, void, unknown>;
|
|
@@ -106,6 +106,7 @@ export class LLMClient {
|
|
|
106
106
|
apiKey;
|
|
107
107
|
model;
|
|
108
108
|
modelName;
|
|
109
|
+
triedBatutaFallback = false;
|
|
109
110
|
currentAbortController = null;
|
|
110
111
|
isInterrupted = false;
|
|
111
112
|
onStreamingContent = null;
|
|
@@ -407,6 +408,11 @@ export class LLMClient {
|
|
|
407
408
|
currentAttempt: currentAttempt + 1,
|
|
408
409
|
});
|
|
409
410
|
}
|
|
411
|
+
if (!this.triedBatutaFallback && this.isConnectionError(error) && this.switchToBatutaProxy()) {
|
|
412
|
+
this.triedBatutaFallback = true;
|
|
413
|
+
logger.flow('Primary endpoint unreachable — falling back to Batuta');
|
|
414
|
+
return this.chatCompletion(options, { maxRetries, currentAttempt: 1 });
|
|
415
|
+
}
|
|
410
416
|
logger.flow('API call failed - Error handling');
|
|
411
417
|
if (currentAttempt > 1) {
|
|
412
418
|
logger.error(`LLM call final failure after ${maxRetries} retries`, {
|
|
@@ -439,6 +445,33 @@ export class LLMClient {
|
|
|
439
445
|
isRequestActive() {
|
|
440
446
|
return this.currentAbortController !== null;
|
|
441
447
|
}
|
|
448
|
+
isConnectionError(error) {
|
|
449
|
+
if (!axios.isAxiosError(error))
|
|
450
|
+
return false;
|
|
451
|
+
const e = error;
|
|
452
|
+
if (e.response)
|
|
453
|
+
return false;
|
|
454
|
+
const codes = ['ECONNREFUSED', 'ETIMEDOUT', 'ECONNRESET', 'ECONNABORTED', 'ENOTFOUND', 'EHOSTUNREACH'];
|
|
455
|
+
return (e.code ? codes.includes(e.code) : false) || /timeout|network/i.test(e.message || '');
|
|
456
|
+
}
|
|
457
|
+
switchToBatutaProxy() {
|
|
458
|
+
const ep = configManager.getAllEndpoints().find(e => e.id === 'batuta-proxy' || e.provider === 'batuta');
|
|
459
|
+
if (!ep || !ep.apiKey || ep.baseUrl === this.baseUrl)
|
|
460
|
+
return false;
|
|
461
|
+
const model = ep.models?.find(m => m.enabled) || ep.models?.[0];
|
|
462
|
+
if (!model)
|
|
463
|
+
return false;
|
|
464
|
+
this.baseUrl = ep.baseUrl;
|
|
465
|
+
this.apiKey = ep.apiKey;
|
|
466
|
+
this.model = model.id;
|
|
467
|
+
this.modelName = model.name;
|
|
468
|
+
const providerDef = ep.provider ? getProvider(ep.provider) : undefined;
|
|
469
|
+
const headers = providerDef
|
|
470
|
+
? buildAuthHeaders(providerDef, this.apiKey)
|
|
471
|
+
: { 'Content-Type': 'application/json', ...(this.apiKey && { Authorization: `Bearer ${this.apiKey}` }) };
|
|
472
|
+
this.axiosInstance = axios.create({ baseURL: this.baseUrl, headers, timeout: 600000 });
|
|
473
|
+
return true;
|
|
474
|
+
}
|
|
442
475
|
isRetryableError(error) {
|
|
443
476
|
if (error instanceof Error && error.message === 'INTERRUPTED') {
|
|
444
477
|
return false;
|