ai-zero-token 2.0.5 → 2.0.7
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/CHANGELOG.md +24 -0
- package/README.md +20 -17
- package/README.zh-CN.md +20 -17
- package/admin-ui/dist/assets/StatCard-7TEzqn2i.js +1 -0
- package/admin-ui/dist/assets/accounts-D3tsDc3k.js +4 -0
- package/admin-ui/dist/assets/{docs-Dh0aFha_.js → docs-BO-aSEzh.js} +1 -1
- package/admin-ui/dist/assets/{image-bed-C1M7-0q1.js → image-bed-Dql7Vqd9.js} +1 -1
- package/admin-ui/dist/assets/index-C22_3Mxq.css +1 -0
- package/admin-ui/dist/assets/index-CCiBaGwU.js +10 -0
- package/admin-ui/dist/assets/{launch-pB7YlWFI.js → launch-DXLo-NIM.js} +1 -1
- package/admin-ui/dist/assets/{logs-B7McijSi.js → logs-Cwn8-rDu.js} +1 -1
- package/admin-ui/dist/assets/{network-detect-Bx3XmXPk.js → network-detect-vzWfL-Tz.js} +1 -1
- package/admin-ui/dist/assets/overview-B_yad8ge.js +1 -0
- package/admin-ui/dist/assets/{profiles-DMOjJORP.js → profiles-C5SmQvju.js} +1 -1
- package/admin-ui/dist/assets/settings-BdRWcKJb.js +5 -0
- package/admin-ui/dist/assets/{tester-BG-up8qP.js → tester-BKoMSoCz.js} +1 -1
- package/admin-ui/dist/assets/usage-B-qQxXzQ.js +1 -0
- package/admin-ui/dist/index.html +3 -3
- package/dist/cli/commands/help.js +1 -1
- package/dist/cli/commands/models.js +3 -2
- package/dist/core/context.js +4 -1
- package/dist/core/models/openai-codex-models.js +106 -1
- package/dist/core/providers/http-client.js +160 -11
- package/dist/core/providers/openai-codex/chat.js +2 -1
- package/dist/core/providers/openai-codex/chatgpt-web-image.js +1404 -0
- package/dist/core/services/auth-service.js +154 -10
- package/dist/core/services/chat-service.js +16 -18
- package/dist/core/services/config-service.js +9 -0
- package/dist/core/services/image-service.js +31 -1
- package/dist/core/services/model-service.js +22 -8
- package/dist/core/services/usage-service.js +349 -0
- package/dist/core/store/codex-auth-store.js +149 -15
- package/dist/core/store/settings-store.js +8 -2
- package/dist/core/store/state-paths.js +17 -1
- package/dist/server/app.js +1023 -69
- package/dist/server/index.js +1 -1
- package/docs/API_USAGE.md +34 -4
- package/docs/DESKTOP_RELEASE.md +12 -1
- package/package.json +1 -1
- package/admin-ui/dist/assets/accounts-ABMyXo4H.js +0 -4
- package/admin-ui/dist/assets/index--rNjdmzf.js +0 -10
- package/admin-ui/dist/assets/index-DjtN30PC.css +0 -1
- package/admin-ui/dist/assets/overview-CV0H2Nsq.js +0 -1
- package/admin-ui/dist/assets/settings-ynCIdUvZ.js +0 -7
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
refreshOpenAICodexToken
|
|
15
15
|
} from "../providers/openai-codex/oauth.js";
|
|
16
16
|
import { askOpenAICodex } from "../providers/openai-codex/chat.js";
|
|
17
|
+
import { isTransientHttpError } from "../providers/http-client.js";
|
|
17
18
|
import {
|
|
18
19
|
applyGatewayToCodexProviderConfig,
|
|
19
20
|
applyProfileToCodexAuth,
|
|
@@ -27,7 +28,7 @@ import {
|
|
|
27
28
|
importProfileFromJson,
|
|
28
29
|
importProfilesFromJson
|
|
29
30
|
} from "../store/profile-transfer.js";
|
|
30
|
-
const DEFAULT_QUOTA_SYNC_CONCURRENCY =
|
|
31
|
+
const DEFAULT_QUOTA_SYNC_CONCURRENCY = 3;
|
|
31
32
|
function getQuotaSyncConcurrency(configured) {
|
|
32
33
|
const raw = process.env.AZT_QUOTA_SYNC_CONCURRENCY;
|
|
33
34
|
const parsed = raw ? Number.parseInt(raw, 10) : configured ?? DEFAULT_QUOTA_SYNC_CONCURRENCY;
|
|
@@ -151,20 +152,99 @@ class AuthService {
|
|
|
151
152
|
const percents = this.getQuotaPercents(profile);
|
|
152
153
|
return percents.length > 0 && percents.some((value) => value >= 100);
|
|
153
154
|
}
|
|
154
|
-
|
|
155
|
+
isQuotaSnapshotExhausted(quota) {
|
|
156
|
+
if (!quota) {
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
return [quota.primaryUsedPercent, quota.secondaryUsedPercent].some(
|
|
160
|
+
(value) => typeof value === "number" && Number.isFinite(value) && value >= 100
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
isQuotaBlocked(profile) {
|
|
164
|
+
return this.isQuotaExhausted(profile) && !this.hasResetElapsedForExhaustedQuota(profile);
|
|
165
|
+
}
|
|
166
|
+
timestampToMillis(value) {
|
|
167
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
|
|
168
|
+
return void 0;
|
|
169
|
+
}
|
|
170
|
+
return value < 1e12 ? value * 1e3 : value;
|
|
171
|
+
}
|
|
172
|
+
getQuotaResetAt(profile, slot) {
|
|
173
|
+
const quota = profile.quota;
|
|
174
|
+
if (!quota) {
|
|
175
|
+
return void 0;
|
|
176
|
+
}
|
|
177
|
+
const direct = slot === "primary" ? quota.primaryResetAt : quota.secondaryResetAt;
|
|
178
|
+
const directMillis = this.timestampToMillis(direct);
|
|
179
|
+
if (directMillis) {
|
|
180
|
+
return directMillis;
|
|
181
|
+
}
|
|
182
|
+
const after = slot === "primary" ? quota.primaryResetAfterSeconds : quota.secondaryResetAfterSeconds;
|
|
183
|
+
if (typeof after === "number" && Number.isFinite(after) && after > 0) {
|
|
184
|
+
const capturedAt = this.timestampToMillis(quota.capturedAt) ?? Date.now();
|
|
185
|
+
return capturedAt + after * 1e3;
|
|
186
|
+
}
|
|
187
|
+
return void 0;
|
|
188
|
+
}
|
|
189
|
+
hasResetElapsedForExhaustedQuota(profile) {
|
|
190
|
+
const quota = profile.quota;
|
|
191
|
+
if (!quota) {
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
const exhaustedSlots = [];
|
|
195
|
+
if (typeof quota.primaryUsedPercent === "number" && quota.primaryUsedPercent >= 100) {
|
|
196
|
+
exhaustedSlots.push("primary");
|
|
197
|
+
}
|
|
198
|
+
if (typeof quota.secondaryUsedPercent === "number" && quota.secondaryUsedPercent >= 100) {
|
|
199
|
+
exhaustedSlots.push("secondary");
|
|
200
|
+
}
|
|
201
|
+
if (exhaustedSlots.length === 0) {
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
const now = Date.now();
|
|
205
|
+
return exhaustedSlots.every((slot) => {
|
|
206
|
+
const resetAt = this.getQuotaResetAt(profile, slot);
|
|
207
|
+
return typeof resetAt === "number" && resetAt <= now;
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
canEnterAutoSwitchPool(profile) {
|
|
155
211
|
if (profile.authStatus?.state === "token_invalidated" || profile.authStatus?.state === "auth_error") {
|
|
156
212
|
return false;
|
|
157
213
|
}
|
|
158
214
|
const percents = this.getQuotaPercents(profile);
|
|
159
|
-
|
|
215
|
+
if (percents.length === 0) {
|
|
216
|
+
return true;
|
|
217
|
+
}
|
|
218
|
+
return percents.every((value) => value < 100) || this.hasResetElapsedForExhaustedQuota(profile);
|
|
160
219
|
}
|
|
161
220
|
hasInvalidAuthStatus(profile) {
|
|
162
221
|
return profile.authStatus?.state === "token_invalidated" || profile.authStatus?.state === "auth_error";
|
|
163
222
|
}
|
|
223
|
+
shouldLeaveActiveProfile(profile) {
|
|
224
|
+
return this.hasInvalidAuthStatus(profile) || this.isQuotaBlocked(profile);
|
|
225
|
+
}
|
|
226
|
+
isRotationTrigger(error, quota) {
|
|
227
|
+
if (this.isQuotaSnapshotExhausted(quota)) {
|
|
228
|
+
return true;
|
|
229
|
+
}
|
|
230
|
+
if (isTransientHttpError(error)) {
|
|
231
|
+
return true;
|
|
232
|
+
}
|
|
233
|
+
const normalized = error instanceof Error ? error : new Error(String(error));
|
|
234
|
+
const details = normalized;
|
|
235
|
+
const status = typeof details.upstreamStatus === "number" ? details.upstreamStatus : void 0;
|
|
236
|
+
const marker = [
|
|
237
|
+
normalized.message,
|
|
238
|
+
details.upstreamErrorCode,
|
|
239
|
+
details.upstreamErrorType,
|
|
240
|
+
details.upstreamErrorMessage
|
|
241
|
+
].filter((value) => typeof value === "string").join(" ").toLowerCase();
|
|
242
|
+
return status === 429 || status === 401 || status === 403 || marker.includes("usage_limit_reached") || marker.includes("token_invalidated") || marker.includes("authentication token has been invalidated") || marker.includes("\u5237\u65B0 token \u5931\u8D25");
|
|
243
|
+
}
|
|
164
244
|
getQuotaUsageScore(profile) {
|
|
165
245
|
const percents = this.getQuotaPercents(profile);
|
|
166
246
|
if (percents.length === 0) {
|
|
167
|
-
return
|
|
247
|
+
return 50;
|
|
168
248
|
}
|
|
169
249
|
return Math.max(...percents);
|
|
170
250
|
}
|
|
@@ -175,12 +255,12 @@ class AuthService {
|
|
|
175
255
|
}
|
|
176
256
|
return updater(profile);
|
|
177
257
|
});
|
|
178
|
-
if (!options?.checkAutoSwitch || options.skipAutoSwitch || !updated || updated.provider !== provider || !this.
|
|
258
|
+
if (!options?.checkAutoSwitch || options.skipAutoSwitch || !updated || updated.provider !== provider || !this.shouldLeaveActiveProfile(updated)) {
|
|
179
259
|
return updated;
|
|
180
260
|
}
|
|
181
261
|
const activeProfile = await this.getActiveProfile(provider);
|
|
182
262
|
if (activeProfile?.profileId === updated.profileId) {
|
|
183
|
-
|
|
263
|
+
return this.maybeAutoSwitchProfile(updated, provider);
|
|
184
264
|
}
|
|
185
265
|
return updated;
|
|
186
266
|
}
|
|
@@ -212,7 +292,7 @@ class AuthService {
|
|
|
212
292
|
async maybeAutoSwitchProfile(profile, provider) {
|
|
213
293
|
const settings = await this.configService.getSettings();
|
|
214
294
|
const excludedProfileIds = new Set(settings.autoSwitch.excludedProfileIds);
|
|
215
|
-
if (!settings.autoSwitch.enabled || excludedProfileIds.has(profile.profileId) || !this.
|
|
295
|
+
if (!settings.autoSwitch.enabled || excludedProfileIds.has(profile.profileId) || !this.shouldLeaveActiveProfile(profile)) {
|
|
216
296
|
return profile;
|
|
217
297
|
}
|
|
218
298
|
const [profiles, codexStatus] = await Promise.all([
|
|
@@ -225,7 +305,7 @@ class AuthService {
|
|
|
225
305
|
profile: item,
|
|
226
306
|
index,
|
|
227
307
|
distance: currentIndex >= 0 ? (index - currentIndex + profiles.length) % profiles.length : index + 1
|
|
228
|
-
})).filter((item) => item.profile.provider === provider && item.profile.profileId !== profile.profileId).filter((item) => !excludedProfileIds.has(item.profile.profileId)).filter((item) => this.
|
|
308
|
+
})).filter((item) => item.profile.provider === provider && item.profile.profileId !== profile.profileId).filter((item) => !excludedProfileIds.has(item.profile.profileId)).filter((item) => this.canEnterAutoSwitchPool(item.profile)).sort((left, right) => {
|
|
229
309
|
const leftCodexConflict = codexAccountId && left.profile.accountId === codexAccountId ? 1 : 0;
|
|
230
310
|
const rightCodexConflict = codexAccountId && right.profile.accountId === codexAccountId ? 1 : 0;
|
|
231
311
|
const codexDiff = leftCodexConflict - rightCodexConflict;
|
|
@@ -255,8 +335,9 @@ class AuthService {
|
|
|
255
335
|
if (!activated) {
|
|
256
336
|
return profile;
|
|
257
337
|
}
|
|
258
|
-
console.info("[auth] auto switched active profile
|
|
338
|
+
console.info("[auth] auto switched active profile", {
|
|
259
339
|
provider,
|
|
340
|
+
reason: this.hasInvalidAuthStatus(profile) ? "auth_error" : "quota_exhausted",
|
|
260
341
|
fromProfileId: profile.profileId,
|
|
261
342
|
toProfileId: activated.profileId,
|
|
262
343
|
avoidedCodexAccount: Boolean(codexAccountId && activated.accountId !== codexAccountId)
|
|
@@ -401,6 +482,69 @@ class AuthService {
|
|
|
401
482
|
}
|
|
402
483
|
return this.refreshStoredProfile(profile, provider);
|
|
403
484
|
}
|
|
485
|
+
async requireUsableProfileById(profileId, provider = "openai-codex") {
|
|
486
|
+
const profiles = await listProfiles();
|
|
487
|
+
const profile = profiles.find((item) => item.provider === provider && item.profileId === profileId);
|
|
488
|
+
if (!profile) {
|
|
489
|
+
throw new Error(`\u6CA1\u6709\u627E\u5230\u8D26\u53F7: ${profileId}`);
|
|
490
|
+
}
|
|
491
|
+
if (Date.now() < profile.expires) {
|
|
492
|
+
return this.toManagedProfile(profile);
|
|
493
|
+
}
|
|
494
|
+
return this.refreshStoredProfile(profile, provider);
|
|
495
|
+
}
|
|
496
|
+
async withProfileRotation(provider, runner, options) {
|
|
497
|
+
const maxAttempts = Math.max(1, Math.min(8, options?.maxAttempts ?? 2));
|
|
498
|
+
const attemptedProfileIds = /* @__PURE__ */ new Set();
|
|
499
|
+
let lastError;
|
|
500
|
+
for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
|
|
501
|
+
let profile;
|
|
502
|
+
try {
|
|
503
|
+
profile = await this.requireUsableProfile(provider, {
|
|
504
|
+
skipAutoSwitch: options?.skipAutoSwitch
|
|
505
|
+
});
|
|
506
|
+
} catch (error) {
|
|
507
|
+
lastError = error;
|
|
508
|
+
if (options?.skipAutoSwitch || attempt >= maxAttempts - 1 || !this.isRotationTrigger(error)) {
|
|
509
|
+
throw error;
|
|
510
|
+
}
|
|
511
|
+
const activeProfile = await this.getActiveProfile(provider);
|
|
512
|
+
if (!activeProfile) {
|
|
513
|
+
throw error;
|
|
514
|
+
}
|
|
515
|
+
const switchedProfile = await this.recordProfileRequestFailure(activeProfile.profileId, error, void 0, provider);
|
|
516
|
+
if (!switchedProfile || switchedProfile.profileId === activeProfile.profileId) {
|
|
517
|
+
throw error;
|
|
518
|
+
}
|
|
519
|
+
continue;
|
|
520
|
+
}
|
|
521
|
+
if (attemptedProfileIds.has(profile.profileId)) {
|
|
522
|
+
break;
|
|
523
|
+
}
|
|
524
|
+
attemptedProfileIds.add(profile.profileId);
|
|
525
|
+
try {
|
|
526
|
+
const result = await runner(profile, attempt);
|
|
527
|
+
await this.recordProfileRequestSuccess(profile.profileId, result.quota, provider, {
|
|
528
|
+
skipAutoSwitch: options?.skipAutoSwitch
|
|
529
|
+
});
|
|
530
|
+
return {
|
|
531
|
+
profile,
|
|
532
|
+
result,
|
|
533
|
+
retryCount: attempt
|
|
534
|
+
};
|
|
535
|
+
} catch (error) {
|
|
536
|
+
lastError = error;
|
|
537
|
+
const quota = error.quota;
|
|
538
|
+
const switchedProfile = await this.recordProfileRequestFailure(profile.profileId, error, quota, provider, {
|
|
539
|
+
skipAutoSwitch: options?.skipAutoSwitch
|
|
540
|
+
});
|
|
541
|
+
if (options?.skipAutoSwitch || attempt >= maxAttempts - 1 || !this.isRotationTrigger(error, quota) || !switchedProfile || switchedProfile.profileId === profile.profileId || attemptedProfileIds.has(switchedProfile.profileId)) {
|
|
542
|
+
throw error;
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
throw lastError instanceof Error ? lastError : new Error(String(lastError ?? "\u6CA1\u6709\u53EF\u7528\u8D26\u53F7\u53EF\u8F6E\u6362\u3002"));
|
|
547
|
+
}
|
|
404
548
|
async requireFreshProfileWithIdToken(profileId, provider = "openai-codex") {
|
|
405
549
|
const profiles = await listProfiles();
|
|
406
550
|
const profile = profiles.find((item) => item.provider === provider && item.profileId === profileId);
|
|
@@ -538,7 +682,7 @@ class AuthService {
|
|
|
538
682
|
}),
|
|
539
683
|
{
|
|
540
684
|
skipAutoSwitch: options?.skipAutoSwitch,
|
|
541
|
-
checkAutoSwitch: Boolean(quota)
|
|
685
|
+
checkAutoSwitch: Boolean(quota || authStatus)
|
|
542
686
|
}
|
|
543
687
|
);
|
|
544
688
|
}
|
|
@@ -9,29 +9,27 @@ class ChatService {
|
|
|
9
9
|
const model = await this.deps.modelService.resolveModel(provider, request.model, {
|
|
10
10
|
allowUnknown: request.experimental?.allowUnknownModel
|
|
11
11
|
});
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
const rotation = await this.deps.authService.withProfileRotation(
|
|
13
|
+
provider,
|
|
14
|
+
(profile) => askOpenAICodex({
|
|
15
15
|
profile,
|
|
16
16
|
prompt: request.input,
|
|
17
17
|
model,
|
|
18
18
|
system: request.system,
|
|
19
19
|
bodyOverride: request.experimental?.codexBody
|
|
20
|
-
})
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
throw error;
|
|
34
|
-
}
|
|
20
|
+
})
|
|
21
|
+
);
|
|
22
|
+
const result = rotation.result;
|
|
23
|
+
return {
|
|
24
|
+
provider,
|
|
25
|
+
model,
|
|
26
|
+
profile: rotation.profile,
|
|
27
|
+
retryCount: rotation.retryCount,
|
|
28
|
+
text: result.text,
|
|
29
|
+
toolCalls: result.toolCalls,
|
|
30
|
+
raw: result.raw,
|
|
31
|
+
artifacts: result.artifacts
|
|
32
|
+
};
|
|
35
33
|
}
|
|
36
34
|
}
|
|
37
35
|
export {
|
|
@@ -161,6 +161,15 @@ class ConfigService {
|
|
|
161
161
|
}
|
|
162
162
|
};
|
|
163
163
|
}
|
|
164
|
+
if (params.image) {
|
|
165
|
+
next = {
|
|
166
|
+
...next,
|
|
167
|
+
image: {
|
|
168
|
+
...next.image,
|
|
169
|
+
freeAccountWebGenerationEnabled: params.image.freeAccountWebGenerationEnabled ?? next.image.freeAccountWebGenerationEnabled
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
}
|
|
164
173
|
if (params.server) {
|
|
165
174
|
next = {
|
|
166
175
|
...next,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { askOpenAICodex } from "../providers/openai-codex/chat.js";
|
|
3
|
+
import { generateChatGPTWebImage } from "../providers/openai-codex/chatgpt-web-image.js";
|
|
3
4
|
const SUPPORTED_IMAGE_MODELS = /* @__PURE__ */ new Set([
|
|
4
5
|
"gpt-image-1",
|
|
5
6
|
"gpt-image-1-mini",
|
|
@@ -248,6 +249,9 @@ function extractImageUsage(raw) {
|
|
|
248
249
|
total_tokens: imageGen.total_tokens
|
|
249
250
|
};
|
|
250
251
|
}
|
|
252
|
+
function isFreePlan(profile) {
|
|
253
|
+
return profile.quota?.planType?.toLowerCase() === "free";
|
|
254
|
+
}
|
|
251
255
|
class ImageService {
|
|
252
256
|
constructor(deps) {
|
|
253
257
|
this.deps = deps;
|
|
@@ -262,9 +266,12 @@ class ImageService {
|
|
|
262
266
|
return model;
|
|
263
267
|
}
|
|
264
268
|
async generate(request) {
|
|
265
|
-
const profile = await this.deps.authService.requireUsableProfile("openai-codex"
|
|
269
|
+
const profile = await this.deps.authService.requireUsableProfile("openai-codex", {
|
|
270
|
+
skipAutoSwitch: true
|
|
271
|
+
});
|
|
266
272
|
const orchestratorModel = IMAGE_ORCHESTRATOR_MODEL;
|
|
267
273
|
const requestedImageModel = this.resolveRequestedImageModel(request.model);
|
|
274
|
+
const settings = await this.deps.configService.getSettings();
|
|
268
275
|
const requestSummary = {
|
|
269
276
|
requestedImageModel,
|
|
270
277
|
orchestratorModel,
|
|
@@ -279,6 +286,29 @@ class ImageService {
|
|
|
279
286
|
inputImageCount: request.inputImages?.length ?? 0
|
|
280
287
|
};
|
|
281
288
|
console.info("[gateway:image] upstream request", requestSummary);
|
|
289
|
+
if (isFreePlan(profile) && settings.image.freeAccountWebGenerationEnabled) {
|
|
290
|
+
try {
|
|
291
|
+
console.info("[gateway:image] using ChatGPT web image route for Free profile", requestSummary);
|
|
292
|
+
const response = await generateChatGPTWebImage({
|
|
293
|
+
profile,
|
|
294
|
+
prompt: request.prompt,
|
|
295
|
+
model: requestedImageModel,
|
|
296
|
+
inputImages: request.inputImages,
|
|
297
|
+
size: request.size,
|
|
298
|
+
responseFormat: "b64_json"
|
|
299
|
+
});
|
|
300
|
+
await this.deps.authService.recordProfileRequestSuccess(profile.profileId, void 0, "openai-codex");
|
|
301
|
+
console.info("[gateway:image] ChatGPT web image response", {
|
|
302
|
+
...requestSummary,
|
|
303
|
+
imageCount: response.data.length,
|
|
304
|
+
firstImageBase64Length: response.data[0]?.b64_json.length ?? 0
|
|
305
|
+
});
|
|
306
|
+
return response;
|
|
307
|
+
} catch (error) {
|
|
308
|
+
await this.deps.authService.recordProfileRequestFailure(profile.profileId, error, void 0, "openai-codex");
|
|
309
|
+
throw error;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
282
312
|
const tool = {
|
|
283
313
|
type: "image_generation",
|
|
284
314
|
model: requestedImageModel
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
getCodexModelCatalog,
|
|
4
|
-
hasCodexModel
|
|
4
|
+
hasCodexModel,
|
|
5
|
+
refreshCodexModelCatalogFromNetwork
|
|
5
6
|
} from "../models/openai-codex-models.js";
|
|
6
7
|
class ModelService {
|
|
7
|
-
constructor(configService) {
|
|
8
|
+
constructor(configService, authService) {
|
|
8
9
|
this.configService = configService;
|
|
10
|
+
this.authService = authService;
|
|
9
11
|
}
|
|
10
12
|
async listModels(provider = "openai-codex") {
|
|
11
13
|
if (provider !== "openai-codex") {
|
|
@@ -30,16 +32,28 @@ class ModelService {
|
|
|
30
32
|
if (provider !== "openai-codex") {
|
|
31
33
|
throw new Error(`\u6682\u4E0D\u652F\u6301 provider: ${provider}`);
|
|
32
34
|
}
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
const profile = await this.authService.requireUsableProfile(provider, {
|
|
36
|
+
skipAutoSwitch: true
|
|
37
|
+
});
|
|
38
|
+
let result;
|
|
39
|
+
try {
|
|
40
|
+
result = await refreshCodexModelCatalogFromNetwork(profile);
|
|
41
|
+
await this.authService.recordProfileRequestSuccess(profile.profileId, void 0, provider, {
|
|
42
|
+
skipAutoSwitch: true
|
|
43
|
+
});
|
|
44
|
+
} catch (error) {
|
|
45
|
+
await this.authService.recordProfileRequestFailure(profile.profileId, error, void 0, provider, {
|
|
46
|
+
skipAutoSwitch: true
|
|
47
|
+
});
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
50
|
+
const defaultModel = await this.configService.getDefaultModel(provider);
|
|
37
51
|
return {
|
|
38
|
-
models: models.map((model) => ({
|
|
52
|
+
models: result.models.map((model) => ({
|
|
39
53
|
...model,
|
|
40
54
|
isDefault: model.id === defaultModel
|
|
41
55
|
})),
|
|
42
|
-
catalog
|
|
56
|
+
catalog: result.catalog
|
|
43
57
|
};
|
|
44
58
|
}
|
|
45
59
|
async getDefaultModel(provider = "openai-codex") {
|