@joshuaswarren/openclaw-engram 9.1.12 → 9.1.13

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.
@@ -3,7 +3,7 @@ import {
3
3
  EngramAccessService,
4
4
  Orchestrator,
5
5
  parseConfig
6
- } from "./chunk-BEELVTEN.js";
6
+ } from "./chunk-7L2Q2HQB.js";
7
7
  import "./chunk-4PXEFPHR.js";
8
8
  import "./chunk-IMMYYNXG.js";
9
9
  import "./chunk-4XBRUH6C.js";
@@ -3412,6 +3412,7 @@ var LOCAL_SERVERS = [
3412
3412
  detectFn: (resp) => resp === "" || typeof resp === "object" && resp !== null
3413
3413
  }
3414
3414
  ];
3415
+ var LOCAL_LLM_GLOBAL_BACKEND_STATE = "__openclawEngramLocalLlmBackendState";
3415
3416
  var LocalLlmClient = class _LocalLlmClient {
3416
3417
  config;
3417
3418
  isAvailable = null;
@@ -3476,6 +3477,61 @@ var LocalLlmClient = class _LocalLlmClient {
3476
3477
  getDetectedType() {
3477
3478
  return this.detectedType;
3478
3479
  }
3480
+ getBackendKey() {
3481
+ return this.config.localLlmUrl.replace("localhost", "127.0.0.1").replace(/\/+$/, "").replace(/\/v1$/, "");
3482
+ }
3483
+ getGlobalBackendState() {
3484
+ const globalAny = globalThis;
3485
+ if (!globalAny[LOCAL_LLM_GLOBAL_BACKEND_STATE]) {
3486
+ globalAny[LOCAL_LLM_GLOBAL_BACKEND_STATE] = /* @__PURE__ */ new Map();
3487
+ }
3488
+ return globalAny[LOCAL_LLM_GLOBAL_BACKEND_STATE];
3489
+ }
3490
+ getTrippedBackendState(now) {
3491
+ const state = this.getGlobalBackendState().get(this.getBackendKey()) ?? null;
3492
+ if (!state) return null;
3493
+ if (state.untilMs <= now) {
3494
+ this.getGlobalBackendState().delete(this.getBackendKey());
3495
+ this.lastHealthCheck = 0;
3496
+ return null;
3497
+ }
3498
+ return state;
3499
+ }
3500
+ markBackendUnavailable(reason, durationMs) {
3501
+ const normalizedReason = this.normalizeBackendTripReason(reason);
3502
+ if (durationMs > 0) {
3503
+ const untilMs = Date.now() + durationMs;
3504
+ this.getGlobalBackendState().set(this.getBackendKey(), { untilMs, reason: normalizedReason });
3505
+ } else {
3506
+ this.getGlobalBackendState().delete(this.getBackendKey());
3507
+ }
3508
+ this.isAvailable = false;
3509
+ this.lastHealthCheck = 0;
3510
+ log.warn(
3511
+ `local LLM backend unavailable for ${durationMs}ms: model=${this.config.localLlmModel} reason=${normalizedReason}`
3512
+ );
3513
+ }
3514
+ extractNonRecoverableBackendReason(reason) {
3515
+ const match = reason.match(
3516
+ /Failed to load model|Library not loaded|different Team IDs|code signature|llm_engine_mlx_amphibian/i
3517
+ );
3518
+ return match?.[0] ?? null;
3519
+ }
3520
+ extractNonRecoverableBackendReasonFromErrorText(errorText) {
3521
+ const directReason = this.extractNonRecoverableBackendReason(errorText);
3522
+ if (directReason) return directReason;
3523
+ try {
3524
+ const parsed = JSON.parse(errorText);
3525
+ return this.extractNonRecoverableBackendReason(parsed?.error?.message ?? "");
3526
+ } catch {
3527
+ return null;
3528
+ }
3529
+ }
3530
+ normalizeBackendTripReason(reason) {
3531
+ const cleaned = reason.replace(/\s+/g, " ").replace(/^[-:–—\s]+/, "").trim();
3532
+ if (!cleaned) return "unknown local backend failure";
3533
+ return cleaned.length > 160 ? `${cleaned.slice(0, 157)}...` : cleaned;
3534
+ }
3479
3535
  /**
3480
3536
  * Fetch with timeout for health checks
3481
3537
  */
@@ -3508,6 +3564,15 @@ var LocalLlmClient = class _LocalLlmClient {
3508
3564
  */
3509
3565
  async checkAvailability() {
3510
3566
  const now = Date.now();
3567
+ const trippedState = this.getTrippedBackendState(now);
3568
+ if (trippedState) {
3569
+ this.isAvailable = false;
3570
+ this.lastHealthCheck = 0;
3571
+ log.info(
3572
+ `local LLM availability: backend circuit open for ${Math.max(0, trippedState.untilMs - now)}ms (${trippedState.reason})`
3573
+ );
3574
+ return false;
3575
+ }
3511
3576
  if (this.isAvailable !== null && now - this.lastHealthCheck < _LocalLlmClient.HEALTH_CHECK_INTERVAL_MS) {
3512
3577
  return this.isAvailable;
3513
3578
  }
@@ -3908,6 +3973,22 @@ var LocalLlmClient = class _LocalLlmClient {
3908
3973
  clearTimeout(attemptTimeout);
3909
3974
  }
3910
3975
  if (response.ok) break;
3976
+ if (response.status >= 500 && attempt < maxAttempts) {
3977
+ try {
3978
+ const errorText = await response.clone().text();
3979
+ const nonRecoverableReason = this.extractNonRecoverableBackendReasonFromErrorText(errorText);
3980
+ if (nonRecoverableReason) {
3981
+ this.markBackendUnavailable(
3982
+ nonRecoverableReason,
3983
+ this.config.localLlm400CooldownMs
3984
+ );
3985
+ this.consecutive400s = 0;
3986
+ return null;
3987
+ }
3988
+ } catch (e) {
3989
+ log.debug(`local LLM failed to inspect retryable error body: ${e}`);
3990
+ }
3991
+ }
3911
3992
  if (response.status < 500 || attempt >= maxAttempts) break;
3912
3993
  const backoffMs = this.config.localLlmRetryBackoffMs * attempt;
3913
3994
  log.warn(
@@ -3932,8 +4013,9 @@ var LocalLlmClient = class _LocalLlmClient {
3932
4013
  }
3933
4014
  if (!response.ok) {
3934
4015
  let reason = "";
4016
+ let errorText = "";
3935
4017
  try {
3936
- const errorText = await response.text();
4018
+ errorText = await response.text();
3937
4019
  try {
3938
4020
  const parsed = JSON.parse(errorText);
3939
4021
  reason = parsed?.error?.message ? ` \u2014 ${parsed.error.message}` : "";
@@ -3946,6 +4028,15 @@ var LocalLlmClient = class _LocalLlmClient {
3946
4028
  log.warn(
3947
4029
  `local LLM request failed: ${response.status} ${response.statusText}${reason} (op=${operation}, model=${this.config.localLlmModel}, url=${chatUrl}, promptChars=${promptChars}, maxTokens=${requestBody.max_tokens})`
3948
4030
  );
4031
+ const nonRecoverableReason = this.extractNonRecoverableBackendReason(reason) ?? this.extractNonRecoverableBackendReasonFromErrorText(errorText);
4032
+ if (nonRecoverableReason) {
4033
+ this.markBackendUnavailable(
4034
+ nonRecoverableReason,
4035
+ this.config.localLlm400CooldownMs
4036
+ );
4037
+ this.consecutive400s = 0;
4038
+ return null;
4039
+ }
3949
4040
  if (response.status === 400) {
3950
4041
  this.consecutive400s += 1;
3951
4042
  if (this.consecutive400s >= this.config.localLlm400TripThreshold) {
@@ -3997,6 +4088,13 @@ var LocalLlmClient = class _LocalLlmClient {
3997
4088
  }
3998
4089
  log.warn(`local LLM request error: op=${operation} error=${errMsg}`);
3999
4090
  this.isAvailable = false;
4091
+ const nonRecoverableReason = this.extractNonRecoverableBackendReason(errMsg);
4092
+ if (nonRecoverableReason) {
4093
+ this.markBackendUnavailable(
4094
+ nonRecoverableReason,
4095
+ this.config.localLlm400CooldownMs
4096
+ );
4097
+ }
4000
4098
  return null;
4001
4099
  } finally {
4002
4100
  if (queueMeta) {
@@ -31330,4 +31428,4 @@ export {
31330
31428
  EngramAccessInputError,
31331
31429
  EngramAccessService
31332
31430
  };
31333
- //# sourceMappingURL=chunk-BEELVTEN.js.map
31431
+ //# sourceMappingURL=chunk-7L2Q2HQB.js.map