aicodeswitch 5.2.10 → 5.2.12
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/bin/utils/managed-fields.js +2 -0
- package/dist/server/config-managed-fields.js +2 -0
- package/dist/server/conversions/pairs/responses-claude/response.js +5 -21
- package/dist/server/conversions/pairs/responses-claude/streaming.js +18 -20
- package/dist/server/conversions/pairs/responses-completions/response.js +2 -9
- package/dist/server/conversions/pairs/responses-completions/streaming.js +3 -10
- package/dist/server/conversions/utils/usage.js +28 -8
- package/dist/server/fs-database.js +3 -3
- package/dist/server/main.js +96 -4
- package/dist/server/performance-tracker.js +377 -0
- package/dist/server/proxy-server.js +82 -18
- package/dist/server/rules-status-service.js +21 -6
- package/dist/server/transformers/stream-timing-transform.js +63 -0
- package/dist/ui/assets/index-BFVjD9Y2.js +799 -0
- package/dist/ui/assets/index-Dm34-4zP.css +1 -0
- package/dist/ui/index.html +2 -2
- package/package.json +1 -1
- package/dist/ui/assets/index-MjMlew6J.css +0 -1
- package/dist/ui/assets/index-NlzYhf99.js +0 -799
|
@@ -41,6 +41,8 @@ exports.CODEX_CONFIG_MANAGED_FIELDS = [
|
|
|
41
41
|
{ path: ['enableRouteSelection'] },
|
|
42
42
|
{ path: ['model_providers', 'aicodeswitch'], isSection: true },
|
|
43
43
|
{ path: ['mcp_servers'], isSection: true, optional: true },
|
|
44
|
+
{ path: ['features'], isSection: true, optional: true },
|
|
45
|
+
{ path: ['memories'], isSection: true, optional: true },
|
|
44
46
|
];
|
|
45
47
|
/**
|
|
46
48
|
* Codex auth.json 管理字段定义
|
|
@@ -9,6 +9,7 @@ exports.claudeToResponsesResponse = claudeToResponsesResponse;
|
|
|
9
9
|
const id_js_1 = require("../../utils/id.js");
|
|
10
10
|
const stop_reasons_js_1 = require("../../utils/stop-reasons.js");
|
|
11
11
|
const mapper_js_1 = require("../../thinking/mapper.js");
|
|
12
|
+
const usage_js_1 = require("../../utils/usage.js");
|
|
12
13
|
/**
|
|
13
14
|
* Convert a Claude Messages response to an OpenAI Responses API response.
|
|
14
15
|
*/
|
|
@@ -45,26 +46,9 @@ function claudeToResponsesResponse(response) {
|
|
|
45
46
|
}
|
|
46
47
|
}
|
|
47
48
|
const { status, incomplete_details } = (0, stop_reasons_js_1.claudeToResponsesStatus)(response.stop_reason);
|
|
48
|
-
|
|
49
|
+
// 上游无 usage 时省略 usage 字段(不伪造 0)
|
|
50
|
+
const usage = (0, usage_js_1.toResponsesUsage)(response.usage);
|
|
49
51
|
const responseId = response.id || (0, id_js_1.generateResponseId)();
|
|
50
|
-
return Object.assign(Object.assign({ id: responseId, object: 'response', status,
|
|
51
|
-
output, model: response.model || '', created_at: Math.floor(Date.now() / 1000), usage }, (incomplete_details ? { incomplete_details } : {})), { metadata: {} });
|
|
52
|
-
}
|
|
53
|
-
// ---------------------------------------------------------------------------
|
|
54
|
-
// Helpers
|
|
55
|
-
// ---------------------------------------------------------------------------
|
|
56
|
-
/**
|
|
57
|
-
* Map Claude usage to Responses API usage.
|
|
58
|
-
*/
|
|
59
|
-
function claudeToResponsesUsage(usage) {
|
|
60
|
-
var _a, _b;
|
|
61
|
-
if (!usage)
|
|
62
|
-
return { input_tokens: 0, output_tokens: 0, total_tokens: 0 };
|
|
63
|
-
const input = (_a = usage.input_tokens) !== null && _a !== void 0 ? _a : 0;
|
|
64
|
-
const output = (_b = usage.output_tokens) !== null && _b !== void 0 ? _b : 0;
|
|
65
|
-
return {
|
|
66
|
-
input_tokens: input,
|
|
67
|
-
output_tokens: output,
|
|
68
|
-
total_tokens: input + output,
|
|
69
|
-
};
|
|
52
|
+
return Object.assign(Object.assign(Object.assign({ id: responseId, object: 'response', status,
|
|
53
|
+
output, model: response.model || '', created_at: Math.floor(Date.now() / 1000) }, (usage ? { usage } : {})), (incomplete_details ? { incomplete_details } : {})), { metadata: {} });
|
|
70
54
|
}
|
|
@@ -10,6 +10,7 @@ exports.ClaudeToResponsesConverter = void 0;
|
|
|
10
10
|
const id_js_1 = require("../../utils/id.js");
|
|
11
11
|
const stop_reasons_js_1 = require("../../utils/stop-reasons.js");
|
|
12
12
|
const streaming_helpers_js_1 = require("../../utils/streaming-helpers.js");
|
|
13
|
+
const usage_js_1 = require("../../utils/usage.js");
|
|
13
14
|
/**
|
|
14
15
|
* ClaudeToResponsesConverter: Claude Messages SSE → Responses API SSE
|
|
15
16
|
*/
|
|
@@ -49,7 +50,7 @@ class ClaudeToResponsesConverter {
|
|
|
49
50
|
enumerable: true,
|
|
50
51
|
configurable: true,
|
|
51
52
|
writable: true,
|
|
52
|
-
value:
|
|
53
|
+
value: null
|
|
53
54
|
});
|
|
54
55
|
Object.defineProperty(this, "output", {
|
|
55
56
|
enumerable: true,
|
|
@@ -89,7 +90,7 @@ class ClaudeToResponsesConverter {
|
|
|
89
90
|
});
|
|
90
91
|
}
|
|
91
92
|
convertEvent(event) {
|
|
92
|
-
var _a, _b, _c, _d, _e;
|
|
93
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
|
|
93
94
|
if (!event.data)
|
|
94
95
|
return [];
|
|
95
96
|
const events = [];
|
|
@@ -99,6 +100,14 @@ class ClaudeToResponsesConverter {
|
|
|
99
100
|
case 'message_start': {
|
|
100
101
|
this.model = ((_a = data.message) === null || _a === void 0 ? void 0 : _a.model) || '';
|
|
101
102
|
this.responseId = ((_b = data.message) === null || _b === void 0 ? void 0 : _b.id) || this.responseId;
|
|
103
|
+
// Claude 的 prompt token 数在 message_start 的 message.usage.input_tokens
|
|
104
|
+
const startUsage = (_c = data.message) === null || _c === void 0 ? void 0 : _c.usage;
|
|
105
|
+
if (startUsage) {
|
|
106
|
+
this.usage = {
|
|
107
|
+
input_tokens: (_d = startUsage.input_tokens) !== null && _d !== void 0 ? _d : 0,
|
|
108
|
+
output_tokens: (_e = startUsage.output_tokens) !== null && _e !== void 0 ? _e : 0,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
102
111
|
events.push(this.makeSSE('response.created', {
|
|
103
112
|
id: this.responseId,
|
|
104
113
|
object: 'response',
|
|
@@ -236,11 +245,11 @@ class ClaudeToResponsesConverter {
|
|
|
236
245
|
break;
|
|
237
246
|
}
|
|
238
247
|
case 'message_delta': {
|
|
239
|
-
this.pendingStopReason = ((
|
|
248
|
+
this.pendingStopReason = ((_f = data.delta) === null || _f === void 0 ? void 0 : _f.stop_reason) || null;
|
|
240
249
|
if (data.usage) {
|
|
241
250
|
this.usage = {
|
|
242
|
-
input_tokens: this.usage.input_tokens,
|
|
243
|
-
output_tokens: (
|
|
251
|
+
input_tokens: (_j = (_g = data.usage.input_tokens) !== null && _g !== void 0 ? _g : (_h = this.usage) === null || _h === void 0 ? void 0 : _h.input_tokens) !== null && _j !== void 0 ? _j : 0,
|
|
252
|
+
output_tokens: (_o = (_l = (_k = data.usage.output_tokens) !== null && _k !== void 0 ? _k : data.usage.tokens) !== null && _l !== void 0 ? _l : (_m = this.usage) === null || _m === void 0 ? void 0 : _m.output_tokens) !== null && _o !== void 0 ? _o : 0,
|
|
244
253
|
};
|
|
245
254
|
}
|
|
246
255
|
break;
|
|
@@ -253,7 +262,7 @@ class ClaudeToResponsesConverter {
|
|
|
253
262
|
break;
|
|
254
263
|
}
|
|
255
264
|
}
|
|
256
|
-
catch (
|
|
265
|
+
catch (_p) {
|
|
257
266
|
// Ignore parse errors
|
|
258
267
|
}
|
|
259
268
|
return events;
|
|
@@ -315,20 +324,9 @@ class ClaudeToResponsesConverter {
|
|
|
315
324
|
this.closeText(events);
|
|
316
325
|
this.closeThinking(events);
|
|
317
326
|
const { status, incomplete_details } = (0, stop_reasons_js_1.claudeToResponsesStatus)(this.pendingStopReason);
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
status,
|
|
322
|
-
output: this.output,
|
|
323
|
-
model: this.model,
|
|
324
|
-
created_at: Math.floor(Date.now() / 1000),
|
|
325
|
-
usage: {
|
|
326
|
-
input_tokens: this.usage.input_tokens,
|
|
327
|
-
output_tokens: this.usage.output_tokens,
|
|
328
|
-
total_tokens: this.usage.input_tokens + this.usage.output_tokens,
|
|
329
|
-
},
|
|
330
|
-
metadata: {},
|
|
331
|
-
};
|
|
327
|
+
// 上游无 usage 时省略 usage 字段(不伪造 0,避免 Codex missing field input_tokens)
|
|
328
|
+
const responsesUsage = (0, usage_js_1.toResponsesUsage)(this.usage);
|
|
329
|
+
const responseObj = Object.assign({ id: this.responseId, object: 'response', status, output: this.output, model: this.model, created_at: Math.floor(Date.now() / 1000), metadata: {} }, (responsesUsage ? { usage: responsesUsage } : {}));
|
|
332
330
|
if (incomplete_details) {
|
|
333
331
|
responseObj.incomplete_details = incomplete_details;
|
|
334
332
|
}
|
|
@@ -80,15 +80,8 @@ function completionsToResponsesResponse(response) {
|
|
|
80
80
|
const finishReason = (0, stop_reasons_js_1.completionsToResponsesFinishReason)(choice.finish_reason);
|
|
81
81
|
const status = finishReason === 'incomplete' ? 'incomplete' : 'completed';
|
|
82
82
|
const usage = (0, usage_js_1.completionsToResponsesUsage)(response.usage);
|
|
83
|
-
const result = {
|
|
84
|
-
|
|
85
|
-
object: 'response',
|
|
86
|
-
status,
|
|
87
|
-
output,
|
|
88
|
-
model: response.model,
|
|
89
|
-
usage,
|
|
90
|
-
created_at: response.created || Math.floor(Date.now() / 1000),
|
|
91
|
-
};
|
|
83
|
+
const result = Object.assign({ id: ((_m = response.id) === null || _m === void 0 ? void 0 : _m.startsWith('resp_')) ? response.id : (0, id_js_1.generateResponseId)(), object: 'response', status,
|
|
84
|
+
output, model: response.model, created_at: response.created || Math.floor(Date.now() / 1000) }, (usage ? { usage } : {}));
|
|
92
85
|
if (status === 'incomplete') {
|
|
93
86
|
result.incomplete_details = { reason: 'max_output_tokens' };
|
|
94
87
|
}
|
|
@@ -320,16 +320,9 @@ class CompletionsToResponsesConverter {
|
|
|
320
320
|
}
|
|
321
321
|
// Build full response object
|
|
322
322
|
const status = this.finishReason === 'length' ? 'incomplete' : 'completed';
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
status,
|
|
327
|
-
output: this.output,
|
|
328
|
-
model: this.model,
|
|
329
|
-
usage: this.usage ? (0, usage_js_1.completionsToResponsesUsage)(this.usage) : {},
|
|
330
|
-
created_at: Math.floor(Date.now() / 1000),
|
|
331
|
-
metadata: {},
|
|
332
|
-
};
|
|
323
|
+
// 上游无 usage 时省略 usage 字段(不伪造 0,避免 Codex missing field input_tokens)
|
|
324
|
+
const responsesUsage = (0, usage_js_1.completionsToResponsesUsage)(this.usage);
|
|
325
|
+
const responseObj = Object.assign({ id: this.responseId, object: 'response', status, output: this.output, model: this.model, created_at: Math.floor(Date.now() / 1000), metadata: {} }, (responsesUsage ? { usage: responsesUsage } : {}));
|
|
333
326
|
if (status === 'incomplete') {
|
|
334
327
|
responseObj.incomplete_details = { reason: 'max_output_tokens' };
|
|
335
328
|
}
|
|
@@ -8,6 +8,7 @@ exports.claudeToCompletionsUsage = claudeToCompletionsUsage;
|
|
|
8
8
|
exports.geminiToClaudeUsage = geminiToClaudeUsage;
|
|
9
9
|
exports.claudeToGeminiUsage = claudeToGeminiUsage;
|
|
10
10
|
exports.responsesToClaudeUsage = responsesToClaudeUsage;
|
|
11
|
+
exports.toResponsesUsage = toResponsesUsage;
|
|
11
12
|
exports.completionsToResponsesUsage = completionsToResponsesUsage;
|
|
12
13
|
/** Map OpenAI Chat usage to Claude usage */
|
|
13
14
|
function completionsToClaudeUsage(usage) {
|
|
@@ -69,14 +70,33 @@ function responsesToClaudeUsage(usage) {
|
|
|
69
70
|
cache_creation_input_tokens: usage.cache_creation_input_tokens,
|
|
70
71
|
};
|
|
71
72
|
}
|
|
72
|
-
/**
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
73
|
+
/**
|
|
74
|
+
* 构造标准 Responses API usage 对象(转换层兼容入口)。
|
|
75
|
+
*
|
|
76
|
+
* 真实值优先:input_tokens ?? prompt_tokens、output_tokens ?? completion_tokens、total_tokens。
|
|
77
|
+
* 覆盖上游 chat completions / claude / gemini 三种格式的字段命名归一。
|
|
78
|
+
*
|
|
79
|
+
* 仅当上游确实提供了任意 token 字段时返回归一化对象;否则返回 null(调用方应省略 usage 字段,
|
|
80
|
+
* 不伪造 0)。这是避免 Codex `ResponseCompleted: missing field input_tokens` 的关键——
|
|
81
|
+
* 既不吐空 `{}`,也不吐伪造的 `{0,0,0}`。
|
|
82
|
+
*/
|
|
83
|
+
function toResponsesUsage(usage) {
|
|
84
|
+
var _a, _b;
|
|
85
|
+
if (!usage || typeof usage !== 'object')
|
|
86
|
+
return null;
|
|
87
|
+
const input_tokens = (_a = usage.input_tokens) !== null && _a !== void 0 ? _a : usage.prompt_tokens;
|
|
88
|
+
const output_tokens = (_b = usage.output_tokens) !== null && _b !== void 0 ? _b : usage.completion_tokens;
|
|
89
|
+
const total_tokens = usage.total_tokens;
|
|
90
|
+
// 上游没返回任何 token 字段 → 不伪造,返回 null
|
|
91
|
+
if (input_tokens == null && output_tokens == null && total_tokens == null)
|
|
92
|
+
return null;
|
|
77
93
|
return {
|
|
78
|
-
input_tokens:
|
|
79
|
-
output_tokens:
|
|
80
|
-
total_tokens:
|
|
94
|
+
input_tokens: input_tokens !== null && input_tokens !== void 0 ? input_tokens : 0,
|
|
95
|
+
output_tokens: output_tokens !== null && output_tokens !== void 0 ? output_tokens : 0,
|
|
96
|
+
total_tokens: total_tokens !== null && total_tokens !== void 0 ? total_tokens : ((input_tokens !== null && input_tokens !== void 0 ? input_tokens : 0) + (output_tokens !== null && output_tokens !== void 0 ? output_tokens : 0)),
|
|
81
97
|
};
|
|
82
98
|
}
|
|
99
|
+
/** Map OpenAI Chat usage to Responses API usage(薄封装,保持现有调用方不变) */
|
|
100
|
+
function completionsToResponsesUsage(usage) {
|
|
101
|
+
return toResponsesUsage(usage);
|
|
102
|
+
}
|
|
@@ -2806,9 +2806,9 @@ class FileSystemDatabaseManager {
|
|
|
2806
2806
|
serviceStats.avgResponseTime =
|
|
2807
2807
|
(serviceStats.avgResponseTime * (serviceStats.totalRequests - 1) + responseTime) / serviceStats.totalRequests;
|
|
2808
2808
|
}
|
|
2809
|
-
// 更新 byModel
|
|
2810
|
-
if (log.
|
|
2811
|
-
const modelName = log.
|
|
2809
|
+
// 更新 byModel(按实际转发的模型统计,优先 targetModel 而非客户端提交的 requestModel)
|
|
2810
|
+
if (log.targetModel || log.requestModel) {
|
|
2811
|
+
const modelName = log.targetModel || log.requestModel || 'Unknown';
|
|
2812
2812
|
let modelStats = this.statistics.byModel.find(s => s.modelName === modelName);
|
|
2813
2813
|
if (!modelStats) {
|
|
2814
2814
|
modelStats = { modelName, totalRequests: 0, totalTokens: 0, avgResponseTime: 0 };
|
package/dist/server/main.js
CHANGED
|
@@ -24,6 +24,7 @@ const proxy_server_1 = require("./proxy-server");
|
|
|
24
24
|
const index_1 = require("./access-keys/index");
|
|
25
25
|
const manager_1 = require("./access-keys/manager");
|
|
26
26
|
const policy_manager_1 = require("./access-keys/policy-manager");
|
|
27
|
+
const performance_tracker_1 = require("./performance-tracker");
|
|
27
28
|
const os_1 = __importDefault(require("os"));
|
|
28
29
|
const auth_1 = require("./auth");
|
|
29
30
|
const version_check_1 = require("./version-check");
|
|
@@ -383,7 +384,7 @@ const DEFAULT_CODEX_REASONING_EFFORT = 'high';
|
|
|
383
384
|
const isCodexReasoningEffort = (value) => {
|
|
384
385
|
return typeof value === 'string' && VALID_CODEX_REASONING_EFFORTS.includes(value);
|
|
385
386
|
};
|
|
386
|
-
const writeCodexConfig = (_dbManager_1, ...args_1) => __awaiter(void 0, [_dbManager_1, ...args_1], void 0, function* (_dbManager, modelReasoningEffort = DEFAULT_CODEX_REASONING_EFFORT, codexDefaultModel, options = {}) {
|
|
387
|
+
const writeCodexConfig = (_dbManager_1, ...args_1) => __awaiter(void 0, [_dbManager_1, ...args_1], void 0, function* (_dbManager, modelReasoningEffort = DEFAULT_CODEX_REASONING_EFFORT, codexDefaultModel, enableMemories, options = {}) {
|
|
387
388
|
var _a;
|
|
388
389
|
try {
|
|
389
390
|
const homeDir = os_1.default.homedir();
|
|
@@ -450,6 +451,17 @@ const writeCodexConfig = (_dbManager_1, ...args_1) => __awaiter(void 0, [_dbMana
|
|
|
450
451
|
}
|
|
451
452
|
}
|
|
452
453
|
};
|
|
454
|
+
// 记忆功能配置
|
|
455
|
+
if (enableMemories) {
|
|
456
|
+
proxyConfig.features = {
|
|
457
|
+
memories: true,
|
|
458
|
+
};
|
|
459
|
+
proxyConfig.memories = {
|
|
460
|
+
generate_memories: true,
|
|
461
|
+
use_memories: true,
|
|
462
|
+
disable_on_external_context: true,
|
|
463
|
+
};
|
|
464
|
+
}
|
|
453
465
|
// 使用智能合并
|
|
454
466
|
const mergedConfig = (0, config_merge_1.mergeTomlConfig)(proxyConfig, currentConfig, config_managed_fields_1.CODEX_CONFIG_MANAGED_FIELDS);
|
|
455
467
|
// 原子性写入合并后的配置
|
|
@@ -681,7 +693,7 @@ const syncConfigsOnServerStartup = (dbManager) => __awaiter(void 0, void 0, void
|
|
|
681
693
|
const modelReasoningEffort = isCodexReasoningEffort(config.codexModelReasoningEffort)
|
|
682
694
|
? config.codexModelReasoningEffort
|
|
683
695
|
: DEFAULT_CODEX_REASONING_EFFORT;
|
|
684
|
-
const codexWritten = yield writeCodexConfig(dbManager, modelReasoningEffort, config.codexDefaultModel);
|
|
696
|
+
const codexWritten = yield writeCodexConfig(dbManager, modelReasoningEffort, config.codexDefaultModel, config.codexEnableMemories);
|
|
685
697
|
console.log(`[Startup Config Sync] Codex config ${codexWritten ? 'written' : 'skipped'}`);
|
|
686
698
|
});
|
|
687
699
|
const syncConfigsOnGlobalConfigUpdate = (dbManager) => __awaiter(void 0, void 0, void 0, function* () {
|
|
@@ -694,7 +706,7 @@ const syncConfigsOnGlobalConfigUpdate = (dbManager) => __awaiter(void 0, void 0,
|
|
|
694
706
|
const modelReasoningEffort = isCodexReasoningEffort(config.codexModelReasoningEffort)
|
|
695
707
|
? config.codexModelReasoningEffort
|
|
696
708
|
: DEFAULT_CODEX_REASONING_EFFORT;
|
|
697
|
-
const codexUpdated = yield writeCodexConfig(dbManager, modelReasoningEffort, config.codexDefaultModel, { allowOverwriteRefresh: true });
|
|
709
|
+
const codexUpdated = yield writeCodexConfig(dbManager, modelReasoningEffort, config.codexDefaultModel, config.codexEnableMemories, { allowOverwriteRefresh: true });
|
|
698
710
|
console.log(`[Config Update Sync] Codex config ${codexUpdated ? 'written' : 'skipped'}`);
|
|
699
711
|
});
|
|
700
712
|
const getCentralSkillsDir = () => {
|
|
@@ -2038,7 +2050,11 @@ ${instruction}
|
|
|
2038
2050
|
: isCodexReasoningEffort(appConfig.codexModelReasoningEffort)
|
|
2039
2051
|
? appConfig.codexModelReasoningEffort
|
|
2040
2052
|
: DEFAULT_CODEX_REASONING_EFFORT;
|
|
2041
|
-
const
|
|
2053
|
+
const requestedEnableMemories = req.body.enableMemories;
|
|
2054
|
+
const enableMemories = requestedEnableMemories !== undefined
|
|
2055
|
+
? !!requestedEnableMemories
|
|
2056
|
+
: !!appConfig.codexEnableMemories;
|
|
2057
|
+
const result = yield writeCodexConfig(dbManager, modelReasoningEffort, appConfig.codexDefaultModel, enableMemories);
|
|
2042
2058
|
applyWriteLocalRecords(proxyServer);
|
|
2043
2059
|
res.json(result);
|
|
2044
2060
|
})));
|
|
@@ -3073,6 +3089,64 @@ ${instruction}
|
|
|
3073
3089
|
}
|
|
3074
3090
|
res.json(alerts);
|
|
3075
3091
|
})));
|
|
3092
|
+
// ============ 服务性能统计(全局,与 AUTH 无关) ============
|
|
3093
|
+
const requirePerfTracker = (res) => {
|
|
3094
|
+
const tracker = proxyServer.getPerformanceTracker();
|
|
3095
|
+
if (!tracker) {
|
|
3096
|
+
res.status(503).json({ error: 'Performance tracker not initialized' });
|
|
3097
|
+
}
|
|
3098
|
+
return tracker;
|
|
3099
|
+
};
|
|
3100
|
+
// 全部 API 服务平铺一览(含所属供应商)
|
|
3101
|
+
app.get('/api/performance/services-overview', asyncHandler((_req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
3102
|
+
const tracker = requirePerfTracker(res);
|
|
3103
|
+
if (!tracker)
|
|
3104
|
+
return;
|
|
3105
|
+
res.json(tracker.getServicesOverview());
|
|
3106
|
+
})));
|
|
3107
|
+
// 全部供应商一览
|
|
3108
|
+
app.get('/api/performance/vendors', asyncHandler((_req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
3109
|
+
const tracker = requirePerfTracker(res);
|
|
3110
|
+
if (!tracker)
|
|
3111
|
+
return;
|
|
3112
|
+
res.json(tracker.getVendorsOverview());
|
|
3113
|
+
})));
|
|
3114
|
+
// 某供应商详情(rollup + 其下服务)
|
|
3115
|
+
app.get('/api/performance/vendors/:vendorId', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
3116
|
+
const tracker = requirePerfTracker(res);
|
|
3117
|
+
if (!tracker)
|
|
3118
|
+
return;
|
|
3119
|
+
const detail = tracker.getVendorDetail(req.params.vendorId);
|
|
3120
|
+
if (!detail) {
|
|
3121
|
+
res.status(404).json({ error: 'Vendor not found' });
|
|
3122
|
+
return;
|
|
3123
|
+
}
|
|
3124
|
+
res.json(detail);
|
|
3125
|
+
})));
|
|
3126
|
+
// 某服务详情(rollup + 其下模型)
|
|
3127
|
+
app.get('/api/performance/services/:serviceId', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
3128
|
+
const tracker = requirePerfTracker(res);
|
|
3129
|
+
if (!tracker)
|
|
3130
|
+
return;
|
|
3131
|
+
const detail = tracker.getServiceDetail(req.params.serviceId);
|
|
3132
|
+
if (!detail) {
|
|
3133
|
+
res.status(404).json({ error: 'Service not found' });
|
|
3134
|
+
return;
|
|
3135
|
+
}
|
|
3136
|
+
res.json(detail);
|
|
3137
|
+
})));
|
|
3138
|
+
// 单模型详情(派生 + 小时走势 + 极值)
|
|
3139
|
+
app.get('/api/performance/services/:serviceId/models/:model', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
3140
|
+
const tracker = requirePerfTracker(res);
|
|
3141
|
+
if (!tracker)
|
|
3142
|
+
return;
|
|
3143
|
+
const detail = tracker.getModelDetail(req.params.serviceId, decodeURIComponent(req.params.model));
|
|
3144
|
+
if (!detail) {
|
|
3145
|
+
res.status(404).json({ error: 'Model not found' });
|
|
3146
|
+
return;
|
|
3147
|
+
}
|
|
3148
|
+
res.json(detail);
|
|
3149
|
+
})));
|
|
3076
3150
|
// 写入MCP配置到Claude Code或Codex的全局配置文件
|
|
3077
3151
|
const writeMCPConfig = (targetType) => __awaiter(void 0, void 0, void 0, function* () {
|
|
3078
3152
|
try {
|
|
@@ -3320,6 +3394,16 @@ const start = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
|
3320
3394
|
catch (error) {
|
|
3321
3395
|
console.error('[Server] AccessKey module initialization failed:', error);
|
|
3322
3396
|
}
|
|
3397
|
+
// Initialize Service Performance Tracker (全局统计,与 AUTH 无关)
|
|
3398
|
+
const performanceTracker = new performance_tracker_1.ServicePerformanceTracker(dataDir);
|
|
3399
|
+
try {
|
|
3400
|
+
yield performanceTracker.initialize();
|
|
3401
|
+
performanceTracker.startAutoFlush();
|
|
3402
|
+
proxyServer.setPerformanceTracker(performanceTracker);
|
|
3403
|
+
}
|
|
3404
|
+
catch (error) {
|
|
3405
|
+
console.error('[Server] Performance tracker initialization failed:', error);
|
|
3406
|
+
}
|
|
3323
3407
|
// 恢复已写入本地的 AccessKey(在代理配置写入之后、AccessKey 模块初始化之后)
|
|
3324
3408
|
try {
|
|
3325
3409
|
applyWriteLocalRecords(proxyServer);
|
|
@@ -3424,6 +3508,14 @@ const start = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
|
3424
3508
|
catch (error) {
|
|
3425
3509
|
console.error('[Shutdown ...] AccessKey module shutdown failed:', error);
|
|
3426
3510
|
}
|
|
3511
|
+
// Flush 服务性能统计(全局桶)后停止定时刷盘
|
|
3512
|
+
try {
|
|
3513
|
+
performanceTracker.stopAutoFlush();
|
|
3514
|
+
yield performanceTracker.flush();
|
|
3515
|
+
}
|
|
3516
|
+
catch (error) {
|
|
3517
|
+
console.error('[Shutdown ...] Performance tracker flush failed:', error);
|
|
3518
|
+
}
|
|
3427
3519
|
dbManager.close();
|
|
3428
3520
|
// 清理规则状态广播器(关闭 SSE 连接)
|
|
3429
3521
|
rules_status_service_1.rulesStatusBroadcaster.destroy();
|