@resolveio/server-lib 22.0.11 → 22.0.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@resolveio/server-lib",
3
- "version": "22.0.11",
3
+ "version": "22.0.13",
4
4
  "description": "",
5
5
  "scripts": {
6
6
  "package": "./build_package.sh",
@@ -33,6 +33,12 @@ export type CodexThreadOptions = {
33
33
  export declare class CodexClient {
34
34
  private static readonly PING_PROMPT;
35
35
  private static readonly DEFAULT_FALLBACK_MODEL;
36
+ private static readonly MODEL_UNAVAILABLE_FALLBACK_FILE;
37
+ private static readonly DEFAULT_MODEL_UNAVAILABLE_TTL_MS;
38
+ private static readonly MIN_MODEL_UNAVAILABLE_TTL_MS;
39
+ private static readonly modelFallbackPreferences;
40
+ private static modelFallbackPreferencesLoaded;
41
+ private static modelFallbackPreferencesLoadPromise;
36
42
  private codex;
37
43
  private codexInit;
38
44
  private readonly threadCache;
@@ -43,6 +49,7 @@ export declare class CodexClient {
43
49
  private readonly defaultTimeoutMs;
44
50
  private readonly pingEnabled;
45
51
  private readonly pingTimeoutMs;
52
+ private readonly modelUnavailableTtlMs;
46
53
  private readonly config;
47
54
  private readonly defaultThreadOptions;
48
55
  constructor(config: CodexConfig);
@@ -65,6 +72,13 @@ export declare class CodexClient {
65
72
  private isModelAvailabilityError;
66
73
  private isRetryableError;
67
74
  private normalizeOptionalString;
75
+ private resolveModelUnavailableTtlMs;
76
+ private ensureModelFallbackPreferencesLoaded;
77
+ private pruneModelFallbackPreferences;
78
+ private getModelFallbackPreference;
79
+ private rememberModelFallbackPreference;
80
+ private clearModelFallbackPreference;
81
+ private persistModelFallbackPreferences;
68
82
  private sleep;
69
83
  private ensureCodex;
70
84
  private log;
@@ -107,6 +107,7 @@ var CodexClient = /** @class */ (function () {
107
107
  this.defaultTimeoutMs = this.resolveDefaultTimeoutMs();
108
108
  this.pingEnabled = this.resolvePingEnabled();
109
109
  this.pingTimeoutMs = this.resolvePingTimeoutMs();
110
+ this.modelUnavailableTtlMs = this.resolveModelUnavailableTtlMs();
110
111
  this.config = config;
111
112
  if (!this.config.apiKey) {
112
113
  throw new Error('OPENAI_API_KEY is required for CodexClient');
@@ -115,63 +116,78 @@ var CodexClient = /** @class */ (function () {
115
116
  }
116
117
  CodexClient.prototype.run = function (prompt, options) {
117
118
  return __awaiter(this, void 0, void 0, function () {
118
- var maxRetries, retryDelayMs, lastError, modelSequence, modelIndex, model, nextModel, modelOptions, attempt, error_1, message, delay;
119
+ var maxRetries, retryDelayMs, lastError, modelSequence, modelIndex, model, nextModel, modelOptions, attempt, result_1, result, error_1, message, delay;
119
120
  return __generator(this, function (_a) {
120
121
  switch (_a.label) {
121
- case 0:
122
+ case 0: return [4 /*yield*/, this.ensureModelFallbackPreferencesLoaded()];
123
+ case 1:
124
+ _a.sent();
122
125
  maxRetries = this.normalizeRetryCount(this.config.maxRetries);
123
126
  retryDelayMs = this.normalizeRetryDelay(this.config.retryDelayMs);
124
127
  lastError = null;
125
128
  modelSequence = this.resolveModelSequence(options);
126
129
  modelIndex = 0;
127
- _a.label = 1;
128
- case 1:
129
- if (!(modelIndex < modelSequence.length)) return [3 /*break*/, 13];
130
+ _a.label = 2;
131
+ case 2:
132
+ if (!(modelIndex < modelSequence.length)) return [3 /*break*/, 18];
130
133
  model = modelSequence[modelIndex];
131
134
  nextModel = modelSequence[modelIndex + 1];
132
135
  modelOptions = this.applyModelOverride(options, model);
133
136
  attempt = 0;
134
- _a.label = 2;
135
- case 2:
136
- if (!(attempt <= maxRetries)) return [3 /*break*/, 12];
137
137
  _a.label = 3;
138
138
  case 3:
139
- _a.trys.push([3, 9, , 11]);
139
+ if (!(attempt <= maxRetries)) return [3 /*break*/, 17];
140
+ _a.label = 4;
141
+ case 4:
142
+ _a.trys.push([4, 12, , 16]);
140
143
  this.log("run model=".concat(model || this.config.model || 'default', " attempt ").concat(attempt + 1, "/").concat(maxRetries + 1));
141
- if (!(this.pingEnabled && !this.isPingPrompt(prompt))) return [3 /*break*/, 5];
144
+ if (!(this.pingEnabled && !this.isPingPrompt(prompt))) return [3 /*break*/, 6];
142
145
  return [4 /*yield*/, this.runPing(modelOptions === null || modelOptions === void 0 ? void 0 : modelOptions.threadOptions)];
143
- case 4:
144
- _a.sent();
145
- _a.label = 5;
146
146
  case 5:
147
- if (!(this.streamEnabled && !this.isPingPrompt(prompt))) return [3 /*break*/, 7];
147
+ _a.sent();
148
+ _a.label = 6;
149
+ case 6:
150
+ if (!(this.streamEnabled && !this.isPingPrompt(prompt))) return [3 /*break*/, 9];
148
151
  return [4 /*yield*/, this.runStreamedOnce(prompt, modelOptions)];
149
- case 6: return [2 /*return*/, _a.sent()];
150
- case 7: return [4 /*yield*/, this.runOnce(prompt, modelOptions)];
151
- case 8: return [2 /*return*/, _a.sent()];
152
- case 9:
152
+ case 7:
153
+ result_1 = _a.sent();
154
+ return [4 /*yield*/, this.clearModelFallbackPreference(model)];
155
+ case 8:
156
+ _a.sent();
157
+ return [2 /*return*/, result_1];
158
+ case 9: return [4 /*yield*/, this.runOnce(prompt, modelOptions)];
159
+ case 10:
160
+ result = _a.sent();
161
+ return [4 /*yield*/, this.clearModelFallbackPreference(model)];
162
+ case 11:
163
+ _a.sent();
164
+ return [2 /*return*/, result];
165
+ case 12:
153
166
  error_1 = _a.sent();
154
167
  lastError = error_1;
155
168
  message = error_1 instanceof Error ? error_1.message : String(error_1 || 'Unknown error');
156
169
  this.log("run model=".concat(model || this.config.model || 'default', " attempt ").concat(attempt + 1, " failed: ").concat(message));
157
- if (nextModel && this.isModelAvailabilityError(error_1)) {
158
- this.log("model unavailable; retrying with fallback model ".concat(nextModel));
159
- return [3 /*break*/, 12];
160
- }
170
+ if (!(nextModel && this.isModelAvailabilityError(error_1))) return [3 /*break*/, 14];
171
+ return [4 /*yield*/, this.rememberModelFallbackPreference(model, nextModel)];
172
+ case 13:
173
+ _a.sent();
174
+ this.log("model unavailable; retrying with fallback model ".concat(nextModel));
175
+ return [3 /*break*/, 17];
176
+ case 14:
161
177
  if (attempt >= maxRetries || !this.isRetryableError(error_1)) {
162
178
  throw lastError;
163
179
  }
164
180
  delay = retryDelayMs * Math.pow(2, attempt);
165
181
  return [4 /*yield*/, this.sleep(delay)];
166
- case 10:
182
+ case 15:
167
183
  _a.sent();
168
184
  attempt += 1;
169
- return [3 /*break*/, 11];
170
- case 11: return [3 /*break*/, 2];
171
- case 12:
185
+ return [3 /*break*/, 16];
186
+ case 16: return [3 /*break*/, 3];
187
+ case 17:
172
188
  modelIndex += 1;
173
- return [3 /*break*/, 1];
174
- case 13: throw lastError;
189
+ return [3 /*break*/, 2];
190
+ case 18: throw lastError;
175
191
  }
176
192
  });
177
193
  });
@@ -352,7 +368,7 @@ var CodexClient = /** @class */ (function () {
352
368
  };
353
369
  CodexClient.prototype.startThread = function (threadOptions_1, threadKey_1) {
354
370
  return __awaiter(this, arguments, void 0, function (threadOptions, threadKey, reuseThread) {
355
- var mergedOptions, cacheKey, existing, thread_1, thread;
371
+ var mergedOptions, effectiveModel, cacheKey, existing, thread_1, thread;
356
372
  if (reuseThread === void 0) { reuseThread = true; }
357
373
  return __generator(this, function (_a) {
358
374
  switch (_a.label) {
@@ -360,6 +376,7 @@ var CodexClient = /** @class */ (function () {
360
376
  case 1:
361
377
  _a.sent();
362
378
  mergedOptions = this.mergeThreadOptions(threadOptions);
379
+ effectiveModel = (mergedOptions === null || mergedOptions === void 0 ? void 0 : mergedOptions.model) || this.config.model || 'default';
363
380
  cacheKey = this.buildThreadCacheKey(threadKey, mergedOptions);
364
381
  if (reuseThread && cacheKey) {
365
382
  existing = this.threadCache.get(cacheKey);
@@ -368,7 +385,7 @@ var CodexClient = /** @class */ (function () {
368
385
  return [2 /*return*/, existing.thread];
369
386
  }
370
387
  }
371
- this.log("startThread (model=".concat(this.config.model || 'default', ")"));
388
+ this.log("startThread (model=".concat(effectiveModel, ")"));
372
389
  if (mergedOptions === null || mergedOptions === void 0 ? void 0 : mergedOptions.model) {
373
390
  this.log("startThread options: ".concat(this.formatThreadOptions(mergedOptions)));
374
391
  thread_1 = this.codex.startThread(mergedOptions);
@@ -688,6 +705,11 @@ var CodexClient = /** @class */ (function () {
688
705
  }
689
706
  sequence.push(normalized);
690
707
  };
708
+ var preferredFallback = this.getModelFallbackPreference(primary);
709
+ if (preferredFallback) {
710
+ push(preferredFallback);
711
+ this.log("sticky model fallback active: ".concat(primary, " -> ").concat(preferredFallback));
712
+ }
691
713
  push(primary);
692
714
  fallbackModels.forEach(push);
693
715
  if (!fallbackModels.length && primary.toLowerCase().includes('codex')) {
@@ -773,6 +795,195 @@ var CodexClient = /** @class */ (function () {
773
795
  CodexClient.prototype.normalizeOptionalString = function (value) {
774
796
  return typeof value === 'string' ? value.trim() : '';
775
797
  };
798
+ CodexClient.prototype.resolveModelUnavailableTtlMs = function () {
799
+ var raw = Number(process.env.AI_DASHBOARD_CODEX_MODEL_UNAVAILABLE_TTL_MS
800
+ || process.env.AI_ASSISTANT_CODEX_MODEL_UNAVAILABLE_TTL_MS);
801
+ if (Number.isFinite(raw) && raw >= CodexClient.MIN_MODEL_UNAVAILABLE_TTL_MS) {
802
+ return Math.floor(raw);
803
+ }
804
+ return CodexClient.DEFAULT_MODEL_UNAVAILABLE_TTL_MS;
805
+ };
806
+ CodexClient.prototype.ensureModelFallbackPreferencesLoaded = function () {
807
+ return __awaiter(this, void 0, void 0, function () {
808
+ var _this = this;
809
+ return __generator(this, function (_a) {
810
+ switch (_a.label) {
811
+ case 0:
812
+ if (CodexClient.modelFallbackPreferencesLoaded) {
813
+ this.pruneModelFallbackPreferences();
814
+ return [2 /*return*/];
815
+ }
816
+ if (!(CodexClient.modelFallbackPreferencesLoadPromise !== null)) return [3 /*break*/, 2];
817
+ return [4 /*yield*/, CodexClient.modelFallbackPreferencesLoadPromise];
818
+ case 1:
819
+ _a.sent();
820
+ this.pruneModelFallbackPreferences();
821
+ return [2 /*return*/];
822
+ case 2:
823
+ CodexClient.modelFallbackPreferencesLoadPromise = (function () { return __awaiter(_this, void 0, void 0, function () {
824
+ var raw, parsed, _a;
825
+ var _this = this;
826
+ return __generator(this, function (_b) {
827
+ switch (_b.label) {
828
+ case 0:
829
+ _b.trys.push([0, 2, 3, 4]);
830
+ return [4 /*yield*/, fs_1.promises.readFile(CodexClient.MODEL_UNAVAILABLE_FALLBACK_FILE, 'utf8')];
831
+ case 1:
832
+ raw = _b.sent();
833
+ parsed = JSON.parse(raw);
834
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
835
+ return [2 /*return*/];
836
+ }
837
+ Object.entries(parsed).forEach(function (_a) {
838
+ var _b = __read(_a, 2), model = _b[0], entry = _b[1];
839
+ var key = _this.normalizeOptionalString(model);
840
+ if (!key) {
841
+ return;
842
+ }
843
+ var fallbackModel = _this.normalizeOptionalString(entry === null || entry === void 0 ? void 0 : entry.fallbackModel);
844
+ var expiresAt = Number(entry === null || entry === void 0 ? void 0 : entry.expiresAt);
845
+ var updatedAt = Number(entry === null || entry === void 0 ? void 0 : entry.updatedAt);
846
+ if (!fallbackModel || !Number.isFinite(expiresAt) || expiresAt <= Date.now()) {
847
+ return;
848
+ }
849
+ CodexClient.modelFallbackPreferences.set(key, {
850
+ fallbackModel: fallbackModel,
851
+ expiresAt: Math.floor(expiresAt),
852
+ updatedAt: Number.isFinite(updatedAt) ? Math.floor(updatedAt) : Date.now()
853
+ });
854
+ });
855
+ return [3 /*break*/, 4];
856
+ case 2:
857
+ _a = _b.sent();
858
+ return [3 /*break*/, 4];
859
+ case 3:
860
+ CodexClient.modelFallbackPreferencesLoaded = true;
861
+ CodexClient.modelFallbackPreferencesLoadPromise = null;
862
+ return [7 /*endfinally*/];
863
+ case 4: return [2 /*return*/];
864
+ }
865
+ });
866
+ }); })();
867
+ return [4 /*yield*/, CodexClient.modelFallbackPreferencesLoadPromise];
868
+ case 3:
869
+ _a.sent();
870
+ this.pruneModelFallbackPreferences();
871
+ return [2 /*return*/];
872
+ }
873
+ });
874
+ });
875
+ };
876
+ CodexClient.prototype.pruneModelFallbackPreferences = function (now) {
877
+ if (now === void 0) { now = Date.now(); }
878
+ Array.from(CodexClient.modelFallbackPreferences.entries()).forEach(function (_a) {
879
+ var _b = __read(_a, 2), model = _b[0], entry = _b[1];
880
+ if (!entry || !Number.isFinite(entry.expiresAt) || entry.expiresAt <= now) {
881
+ CodexClient.modelFallbackPreferences.delete(model);
882
+ }
883
+ });
884
+ };
885
+ CodexClient.prototype.getModelFallbackPreference = function (model) {
886
+ var normalizedModel = this.normalizeOptionalString(model);
887
+ if (!normalizedModel) {
888
+ return '';
889
+ }
890
+ this.pruneModelFallbackPreferences();
891
+ var entry = CodexClient.modelFallbackPreferences.get(normalizedModel);
892
+ if (!entry || entry.expiresAt <= Date.now()) {
893
+ if (entry) {
894
+ CodexClient.modelFallbackPreferences.delete(normalizedModel);
895
+ }
896
+ return '';
897
+ }
898
+ return entry.fallbackModel;
899
+ };
900
+ CodexClient.prototype.rememberModelFallbackPreference = function (model, fallbackModel) {
901
+ return __awaiter(this, void 0, void 0, function () {
902
+ var normalizedModel, normalizedFallback, now;
903
+ return __generator(this, function (_a) {
904
+ switch (_a.label) {
905
+ case 0:
906
+ normalizedModel = this.normalizeOptionalString(model);
907
+ normalizedFallback = this.normalizeOptionalString(fallbackModel);
908
+ if (!normalizedModel || !normalizedFallback || normalizedModel === normalizedFallback) {
909
+ return [2 /*return*/];
910
+ }
911
+ now = Date.now();
912
+ CodexClient.modelFallbackPreferences.set(normalizedModel, {
913
+ fallbackModel: normalizedFallback,
914
+ expiresAt: now + this.modelUnavailableTtlMs,
915
+ updatedAt: now
916
+ });
917
+ this.pruneModelFallbackPreferences(now);
918
+ return [4 /*yield*/, this.persistModelFallbackPreferences()];
919
+ case 1:
920
+ _a.sent();
921
+ return [2 /*return*/];
922
+ }
923
+ });
924
+ });
925
+ };
926
+ CodexClient.prototype.clearModelFallbackPreference = function (model) {
927
+ return __awaiter(this, void 0, void 0, function () {
928
+ var normalizedModel;
929
+ return __generator(this, function (_a) {
930
+ switch (_a.label) {
931
+ case 0:
932
+ normalizedModel = this.normalizeOptionalString(model);
933
+ if (!normalizedModel) {
934
+ return [2 /*return*/];
935
+ }
936
+ if (!CodexClient.modelFallbackPreferences.has(normalizedModel)) {
937
+ return [2 /*return*/];
938
+ }
939
+ CodexClient.modelFallbackPreferences.delete(normalizedModel);
940
+ return [4 /*yield*/, this.persistModelFallbackPreferences()];
941
+ case 1:
942
+ _a.sent();
943
+ return [2 /*return*/];
944
+ }
945
+ });
946
+ });
947
+ };
948
+ CodexClient.prototype.persistModelFallbackPreferences = function () {
949
+ return __awaiter(this, void 0, void 0, function () {
950
+ var _a, serialized, _b;
951
+ return __generator(this, function (_c) {
952
+ switch (_c.label) {
953
+ case 0:
954
+ this.pruneModelFallbackPreferences();
955
+ if (!!CodexClient.modelFallbackPreferences.size) return [3 /*break*/, 5];
956
+ _c.label = 1;
957
+ case 1:
958
+ _c.trys.push([1, 3, , 4]);
959
+ return [4 /*yield*/, fs_1.promises.unlink(CodexClient.MODEL_UNAVAILABLE_FALLBACK_FILE)];
960
+ case 2:
961
+ _c.sent();
962
+ return [3 /*break*/, 4];
963
+ case 3:
964
+ _a = _c.sent();
965
+ return [3 /*break*/, 4];
966
+ case 4: return [2 /*return*/];
967
+ case 5:
968
+ serialized = {};
969
+ CodexClient.modelFallbackPreferences.forEach(function (entry, model) {
970
+ serialized[model] = entry;
971
+ });
972
+ _c.label = 6;
973
+ case 6:
974
+ _c.trys.push([6, 8, , 9]);
975
+ return [4 /*yield*/, fs_1.promises.writeFile(CodexClient.MODEL_UNAVAILABLE_FALLBACK_FILE, JSON.stringify(serialized), 'utf8')];
976
+ case 7:
977
+ _c.sent();
978
+ return [3 /*break*/, 9];
979
+ case 8:
980
+ _b = _c.sent();
981
+ return [3 /*break*/, 9];
982
+ case 9: return [2 /*return*/];
983
+ }
984
+ });
985
+ });
986
+ };
776
987
  CodexClient.prototype.sleep = function (ms) {
777
988
  return __awaiter(this, void 0, void 0, function () {
778
989
  return __generator(this, function (_a) {
@@ -1089,6 +1300,12 @@ var CodexClient = /** @class */ (function () {
1089
1300
  };
1090
1301
  CodexClient.PING_PROMPT = "Say 'pong'";
1091
1302
  CodexClient.DEFAULT_FALLBACK_MODEL = 'gpt-5.2-codex';
1303
+ CodexClient.MODEL_UNAVAILABLE_FALLBACK_FILE = path.join(os.tmpdir(), 'resolveio-codex-model-fallbacks.json');
1304
+ CodexClient.DEFAULT_MODEL_UNAVAILABLE_TTL_MS = 6 * 60 * 60 * 1000;
1305
+ CodexClient.MIN_MODEL_UNAVAILABLE_TTL_MS = 60 * 1000;
1306
+ CodexClient.modelFallbackPreferences = new Map();
1307
+ CodexClient.modelFallbackPreferencesLoaded = false;
1308
+ CodexClient.modelFallbackPreferencesLoadPromise = null;
1092
1309
  return CodexClient;
1093
1310
  }());
1094
1311
  exports.CodexClient = CodexClient;