orquesta-cli 0.2.85 → 0.2.87
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.
|
@@ -322,22 +322,23 @@ export class ConfigManager {
|
|
|
322
322
|
added++;
|
|
323
323
|
}
|
|
324
324
|
}
|
|
325
|
+
const serverDefaultId = orquestaEndpoints.find((e) => e.is_default === true)?.id;
|
|
326
|
+
const serverDefault = serverDefaultId ? config.endpoints.find((e) => e.id === serverDefaultId) : undefined;
|
|
327
|
+
const batuta = config.endpoints.find((e) => e.provider === 'batuta' || e.id === 'batuta-proxy');
|
|
325
328
|
const currentExists = !!config.currentEndpoint && config.endpoints.some((e) => e.id === config.currentEndpoint);
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
config.currentModel = enabledModel.id;
|
|
329
|
+
const preferred = serverDefault || batuta || (!currentExists ? config.endpoints[0] : undefined);
|
|
330
|
+
if (preferred) {
|
|
331
|
+
config.currentEndpoint = preferred.id;
|
|
332
|
+
const m = preferred.models.find((x) => x.enabled) || preferred.models[0];
|
|
333
|
+
if (m)
|
|
334
|
+
config.currentModel = m.id;
|
|
333
335
|
}
|
|
334
336
|
else if (currentExists) {
|
|
335
337
|
const ep = config.endpoints.find((e) => e.id === config.currentEndpoint);
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
config.currentModel = enabledModel.id;
|
|
338
|
+
if (!ep.models.some((m) => m.id === config.currentModel)) {
|
|
339
|
+
const m = ep.models.find((x) => x.enabled) || ep.models[0];
|
|
340
|
+
if (m)
|
|
341
|
+
config.currentModel = m.id;
|
|
341
342
|
}
|
|
342
343
|
}
|
|
343
344
|
await this.saveConfig();
|
|
@@ -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;
|