@jsonstudio/rcc 0.89.1086 → 0.89.1136
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/dist/build-info.js +2 -2
- package/dist/cli.js +39 -1
- package/dist/cli.js.map +1 -1
- package/dist/client/gemini/gemini-protocol-client.js +5 -0
- package/dist/client/gemini/gemini-protocol-client.js.map +1 -1
- package/dist/commands/provider-update.js +355 -5
- package/dist/commands/provider-update.js.map +1 -1
- package/dist/docs/daemon-admin-ui.html +604 -91
- package/dist/index.js +33 -1
- package/dist/index.js.map +1 -1
- package/dist/manager/modules/quota/index.d.ts +37 -1
- package/dist/manager/modules/quota/index.js +378 -18
- package/dist/manager/modules/quota/index.js.map +1 -1
- package/dist/manager/quota/provider-quota-center.d.ts +3 -0
- package/dist/manager/quota/provider-quota-center.js +88 -24
- package/dist/manager/quota/provider-quota-center.js.map +1 -1
- package/dist/manager/quota/provider-quota-store.js +5 -2
- package/dist/manager/quota/provider-quota-store.js.map +1 -1
- package/dist/manager/types.d.ts +5 -0
- package/dist/providers/core/config/service-profiles.js +1 -1
- package/dist/providers/core/config/service-profiles.js.map +1 -1
- package/dist/providers/core/runtime/gemini-cli-http-provider.js +5 -0
- package/dist/providers/core/runtime/gemini-cli-http-provider.js.map +1 -1
- package/dist/providers/core/runtime/http-transport-provider.js +26 -38
- package/dist/providers/core/runtime/http-transport-provider.js.map +1 -1
- package/dist/providers/core/utils/http-client.js +10 -1
- package/dist/providers/core/utils/http-client.js.map +1 -1
- package/dist/server/handlers/handler-utils.d.ts +1 -1
- package/dist/server/handlers/handler-utils.js +82 -4
- package/dist/server/handlers/handler-utils.js.map +1 -1
- package/dist/server/handlers/responses-handler.js +26 -3
- package/dist/server/handlers/responses-handler.js.map +1 -1
- package/dist/server/handlers/sse-dispatcher.js +1 -4
- package/dist/server/handlers/sse-dispatcher.js.map +1 -1
- package/dist/server/runtime/http-server/colored-logger.d.ts +1 -1
- package/dist/server/runtime/http-server/colored-logger.js +22 -10
- package/dist/server/runtime/http-server/colored-logger.js.map +1 -1
- package/dist/server/runtime/http-server/daemon-admin/credentials-handler.d.ts +1 -1
- package/dist/server/runtime/http-server/daemon-admin/credentials-handler.js +10 -14
- package/dist/server/runtime/http-server/daemon-admin/credentials-handler.js.map +1 -1
- package/dist/server/runtime/http-server/daemon-admin/providers-handler.js +108 -115
- package/dist/server/runtime/http-server/daemon-admin/providers-handler.js.map +1 -1
- package/dist/server/runtime/http-server/daemon-admin/quota-handler.js +132 -7
- package/dist/server/runtime/http-server/daemon-admin/quota-handler.js.map +1 -1
- package/dist/server/runtime/http-server/daemon-admin/restart-handler.d.ts +3 -0
- package/dist/server/runtime/http-server/daemon-admin/restart-handler.js +22 -0
- package/dist/server/runtime/http-server/daemon-admin/restart-handler.js.map +1 -0
- package/dist/server/runtime/http-server/daemon-admin/stats-handler.d.ts +3 -0
- package/dist/server/runtime/http-server/daemon-admin/stats-handler.js +56 -0
- package/dist/server/runtime/http-server/daemon-admin/stats-handler.js.map +1 -0
- package/dist/server/runtime/http-server/daemon-admin/status-handler.js +100 -4
- package/dist/server/runtime/http-server/daemon-admin/status-handler.js.map +1 -1
- package/dist/server/runtime/http-server/daemon-admin-routes.d.ts +24 -0
- package/dist/server/runtime/http-server/daemon-admin-routes.js +25 -0
- package/dist/server/runtime/http-server/daemon-admin-routes.js.map +1 -1
- package/dist/server/runtime/http-server/executor-provider.js +74 -0
- package/dist/server/runtime/http-server/executor-provider.js.map +1 -1
- package/dist/server/runtime/http-server/index.d.ts +7 -1
- package/dist/server/runtime/http-server/index.js +171 -14
- package/dist/server/runtime/http-server/index.js.map +1 -1
- package/dist/server/runtime/http-server/middleware.d.ts +2 -1
- package/dist/server/runtime/http-server/middleware.js +7 -6
- package/dist/server/runtime/http-server/middleware.js.map +1 -1
- package/dist/server/runtime/http-server/provider-utils.d.ts +1 -1
- package/dist/server/runtime/http-server/provider-utils.js +19 -2
- package/dist/server/runtime/http-server/provider-utils.js.map +1 -1
- package/dist/server/runtime/http-server/request-executor.js +9 -10
- package/dist/server/runtime/http-server/request-executor.js.map +1 -1
- package/dist/server/runtime/http-server/routes.d.ts +10 -0
- package/dist/server/runtime/http-server/routes.js +14 -1
- package/dist/server/runtime/http-server/routes.js.map +1 -1
- package/dist/server/runtime/http-server/stats-manager.d.ts +7 -0
- package/dist/server/runtime/http-server/stats-manager.js +22 -3
- package/dist/server/runtime/http-server/stats-manager.js.map +1 -1
- package/dist/server/runtime/http-server/types.d.ts +10 -0
- package/dist/server/utils/http-error-mapper.js +85 -7
- package/dist/server/utils/http-error-mapper.js.map +1 -1
- package/dist/server/utils/request-id-manager.js +9 -5
- package/dist/server/utils/request-id-manager.js.map +1 -1
- package/dist/server/utils/sse-request-parser.js +2 -1
- package/dist/server/utils/sse-request-parser.js.map +1 -1
- package/dist/server/utils/utf8-chunk-buffer.d.ts +15 -30
- package/dist/server/utils/utf8-chunk-buffer.js +78 -88
- package/dist/server/utils/utf8-chunk-buffer.js.map +1 -1
- package/dist/server/utils/warmup-storm-tracker.js +1 -1
- package/dist/server/utils/warmup-storm-tracker.js.map +1 -1
- package/dist/token-daemon/token-daemon.js +7 -1
- package/dist/token-daemon/token-daemon.js.map +1 -1
- package/dist/tools/provider-update/fetch-models.js +8 -5
- package/dist/tools/provider-update/fetch-models.js.map +1 -1
- package/dist/tools/provider-update/probe-context.d.ts +24 -0
- package/dist/tools/provider-update/probe-context.js +199 -0
- package/dist/tools/provider-update/probe-context.js.map +1 -0
- package/dist/tools/provider-update/types.d.ts +1 -0
- package/package.json +6 -4
- package/scripts/scan-apply-patch-samples.mjs +362 -0
- package/scripts/scan-exec-command-samples.mjs +269 -0
- package/scripts/scan-tool-shape-samples.mjs +291 -0
- package/scripts/tools/sync-apply-patch-regressions.mjs +86 -0
- package/scripts/verify-apply-patch-regressions.mjs +119 -0
- package/scripts/verify-tool-arguments.mjs +1 -2
|
@@ -2,10 +2,24 @@ import { registerStatusRoutes } from './daemon-admin/status-handler.js';
|
|
|
2
2
|
import { registerCredentialRoutes } from './daemon-admin/credentials-handler.js';
|
|
3
3
|
import { registerQuotaRoutes } from './daemon-admin/quota-handler.js';
|
|
4
4
|
import { registerProviderRoutes } from './daemon-admin/providers-handler.js';
|
|
5
|
+
import { registerRestartRoutes } from './daemon-admin/restart-handler.js';
|
|
6
|
+
import { registerStatsRoutes } from './daemon-admin/stats-handler.js';
|
|
7
|
+
import { extractApiKeyFromRequest } from './middleware.js';
|
|
5
8
|
export function isLocalRequest(req) {
|
|
6
9
|
const ip = req.socket?.remoteAddress || '';
|
|
7
10
|
return ip === '127.0.0.1' || ip === '::1' || ip === '::ffff:127.0.0.1';
|
|
8
11
|
}
|
|
12
|
+
export function isDaemonAdminRequestAllowed(req, expectedApiKey) {
|
|
13
|
+
if (isLocalRequest(req)) {
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
const expected = typeof expectedApiKey === 'string' ? expectedApiKey.trim() : '';
|
|
17
|
+
if (!expected) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
const provided = extractApiKeyFromRequest(req);
|
|
21
|
+
return Boolean(provided && provided === expected);
|
|
22
|
+
}
|
|
9
23
|
export function rejectNonLocal(req, res) {
|
|
10
24
|
if (isLocalRequest(req)) {
|
|
11
25
|
return false;
|
|
@@ -13,15 +27,26 @@ export function rejectNonLocal(req, res) {
|
|
|
13
27
|
res.status(403).json({ error: { message: 'forbidden', code: 'forbidden' } });
|
|
14
28
|
return true;
|
|
15
29
|
}
|
|
30
|
+
export function rejectNonLocalOrUnauthorizedAdmin(req, res, expectedApiKey) {
|
|
31
|
+
if (isDaemonAdminRequestAllowed(req, expectedApiKey)) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
res.status(403).json({ error: { message: 'forbidden', code: 'forbidden' } });
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
16
37
|
export function registerDaemonAdminRoutes(options) {
|
|
17
38
|
const { app } = options;
|
|
18
39
|
// Daemon / manager 状态
|
|
19
40
|
registerStatusRoutes(app, options);
|
|
41
|
+
// Token usage / provider stats
|
|
42
|
+
registerStatsRoutes(app, options);
|
|
20
43
|
// Credentials / token 视图
|
|
21
44
|
registerCredentialRoutes(app, options);
|
|
22
45
|
// Quota / 429 冷却视图
|
|
23
46
|
registerQuotaRoutes(app, options);
|
|
24
47
|
// Providers 运行时 + Config V2 视图
|
|
25
48
|
registerProviderRoutes(app, options);
|
|
49
|
+
// Reload / restart runtime (reload config from disk)
|
|
50
|
+
registerRestartRoutes(app, options);
|
|
26
51
|
}
|
|
27
52
|
//# sourceMappingURL=daemon-admin-routes.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"daemon-admin-routes.js","sourceRoot":"","sources":["../../../../src/server/runtime/http-server/daemon-admin-routes.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,EAAE,wBAAwB,EAAE,MAAM,uCAAuC,CAAC;AACjF,OAAO,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAC;
|
|
1
|
+
{"version":3,"file":"daemon-admin-routes.js","sourceRoot":"","sources":["../../../../src/server/runtime/http-server/daemon-admin-routes.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,EAAE,wBAAwB,EAAE,MAAM,uCAAuC,CAAC;AACjF,OAAO,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAC;AAC7E,OAAO,EAAE,qBAAqB,EAAE,MAAM,mCAAmC,CAAC;AAC1E,OAAO,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAC;AAqC3D,MAAM,UAAU,cAAc,CAAC,GAAY;IACzC,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,aAAa,IAAI,EAAE,CAAC;IAC3C,OAAO,EAAE,KAAK,WAAW,IAAI,EAAE,KAAK,KAAK,IAAI,EAAE,KAAK,kBAAkB,CAAC;AACzE,CAAC;AAED,MAAM,UAAU,2BAA2B,CAAC,GAAY,EAAE,cAAuB;IAC/E,IAAI,cAAc,CAAC,GAAG,CAAC,EAAE,CAAC;QACxB,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,QAAQ,GAAG,OAAO,cAAc,KAAK,QAAQ,CAAC,CAAC,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACjF,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,QAAQ,GAAG,wBAAwB,CAAC,GAAG,CAAC,CAAC;IAC/C,OAAO,OAAO,CAAC,QAAQ,IAAI,QAAQ,KAAK,QAAQ,CAAC,CAAC;AACpD,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,GAAY,EAAE,GAAa;IACxD,IAAI,cAAc,CAAC,GAAG,CAAC,EAAE,CAAC;QACxB,OAAO,KAAK,CAAC;IACf,CAAC;IACD,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,CAAC,CAAC;IAC7E,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,iCAAiC,CAC/C,GAAY,EACZ,GAAa,EACb,cAAuB;IAEvB,IAAI,2BAA2B,CAAC,GAAG,EAAE,cAAc,CAAC,EAAE,CAAC;QACrD,OAAO,KAAK,CAAC;IACf,CAAC;IACD,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,CAAC,CAAC;IAC7E,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,yBAAyB,CAAC,OAAgC;IACxE,MAAM,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC;IAExB,sBAAsB;IACtB,oBAAoB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAEnC,+BAA+B;IAC/B,mBAAmB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAElC,yBAAyB;IACzB,wBAAwB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAEvC,mBAAmB;IACnB,mBAAmB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAElC,+BAA+B;IAC/B,sBAAsB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAErC,qDAAqD;IACrD,qBAAqB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;AACtC,CAAC"}
|
|
@@ -23,6 +23,13 @@ export function shouldRetryProviderError(error) {
|
|
|
23
23
|
if (!error || typeof error !== 'object') {
|
|
24
24
|
return false;
|
|
25
25
|
}
|
|
26
|
+
// Deterministic context overflow: retrying the *same* model will keep failing,
|
|
27
|
+
// but a different model/provider in the same route pool might accept it.
|
|
28
|
+
// This matches the virtual-router strategy: if a target is over its context window,
|
|
29
|
+
// switch to the next candidate before giving up.
|
|
30
|
+
if (isPromptTooLongError(error)) {
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
26
33
|
// virtualRouterSeriesCooldown 表示 provider 已经把 alias 从池子里拉黑,需要切换到下一位。
|
|
27
34
|
// 这类错误仍然属于「可恢复」,因为虚拟路由会根据 cooldown 信息选择新的目标。
|
|
28
35
|
if (hasVirtualRouterSeriesCooldown(error)) {
|
|
@@ -41,6 +48,55 @@ export function shouldRetryProviderError(error) {
|
|
|
41
48
|
}
|
|
42
49
|
return false;
|
|
43
50
|
}
|
|
51
|
+
function isPromptTooLongError(error) {
|
|
52
|
+
const status = extractErrorStatusCode(error);
|
|
53
|
+
// Most upstreams return 400 for context overflow; keep this narrow to avoid retries on generic 400s.
|
|
54
|
+
// Some error shims only expose the message (or nest status inside response.data.error.status),
|
|
55
|
+
// so allow missing status when the message is a strong match.
|
|
56
|
+
if (status !== undefined && status !== 400) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
const messages = [];
|
|
60
|
+
const rawMessage = error.message;
|
|
61
|
+
if (typeof rawMessage === 'string' && rawMessage.trim()) {
|
|
62
|
+
messages.push(rawMessage);
|
|
63
|
+
}
|
|
64
|
+
const upstreamMessage = error.upstreamMessage;
|
|
65
|
+
if (typeof upstreamMessage === 'string' && upstreamMessage.trim()) {
|
|
66
|
+
messages.push(upstreamMessage);
|
|
67
|
+
}
|
|
68
|
+
const details = error.details;
|
|
69
|
+
if (details && typeof details === 'object') {
|
|
70
|
+
const msg = details.upstreamMessage;
|
|
71
|
+
if (typeof msg === 'string' && msg.trim()) {
|
|
72
|
+
messages.push(msg);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const response = error.response;
|
|
76
|
+
if (response && typeof response === 'object') {
|
|
77
|
+
const data = response.data;
|
|
78
|
+
if (data && typeof data === 'object') {
|
|
79
|
+
const err = data.error;
|
|
80
|
+
if (err && typeof err === 'object') {
|
|
81
|
+
const msg = err.message;
|
|
82
|
+
if (typeof msg === 'string' && msg.trim()) {
|
|
83
|
+
messages.push(msg);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const combined = messages.join(' | ').toLowerCase();
|
|
89
|
+
if (!combined) {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
return (combined.includes('prompt is too long') ||
|
|
93
|
+
combined.includes('maximum context') ||
|
|
94
|
+
combined.includes('max context') ||
|
|
95
|
+
combined.includes('context length') ||
|
|
96
|
+
combined.includes('context_window_exceeded') ||
|
|
97
|
+
combined.includes('token limit') ||
|
|
98
|
+
combined.includes('too many tokens'));
|
|
99
|
+
}
|
|
44
100
|
export function extractErrorStatusCode(error) {
|
|
45
101
|
if (!error || typeof error !== 'object') {
|
|
46
102
|
return undefined;
|
|
@@ -60,6 +116,14 @@ export function extractErrorStatusCode(error) {
|
|
|
60
116
|
if (typeof nestedStatus === 'number') {
|
|
61
117
|
statusCandidates.push(nestedStatus);
|
|
62
118
|
}
|
|
119
|
+
const upstreamStatus = detailStatus.upstreamStatus;
|
|
120
|
+
if (typeof upstreamStatus === 'number') {
|
|
121
|
+
statusCandidates.push(upstreamStatus);
|
|
122
|
+
}
|
|
123
|
+
const upstreamStatusSnake = detailStatus.upstream_status;
|
|
124
|
+
if (typeof upstreamStatusSnake === 'number') {
|
|
125
|
+
statusCandidates.push(upstreamStatusSnake);
|
|
126
|
+
}
|
|
63
127
|
}
|
|
64
128
|
const response = error.response;
|
|
65
129
|
if (response && typeof response === 'object') {
|
|
@@ -71,6 +135,16 @@ export function extractErrorStatusCode(error) {
|
|
|
71
135
|
if (typeof respStatusCode === 'number') {
|
|
72
136
|
statusCandidates.push(respStatusCode);
|
|
73
137
|
}
|
|
138
|
+
const data = response.data;
|
|
139
|
+
if (data && typeof data === 'object') {
|
|
140
|
+
const errNode = data.error;
|
|
141
|
+
if (errNode && typeof errNode === 'object') {
|
|
142
|
+
const nested = errNode.status;
|
|
143
|
+
if (typeof nested === 'number') {
|
|
144
|
+
statusCandidates.push(nested);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
74
148
|
}
|
|
75
149
|
const explicit = statusCandidates.find((candidate) => typeof candidate === 'number');
|
|
76
150
|
if (typeof explicit === 'number') {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"executor-provider.js","sourceRoot":"","sources":["../../../../src/server/runtime/http-server/executor-provider.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,8BAA8B,CAAC,KAAc;IAC3D,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACxC,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,OAAO,GAAI,KAA+B,CAAC,OAAO,CAAC;IACzD,IAAI,CAAC,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QAC5C,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,YAAY,GAAI,OAAqD,CAAC,2BAA2B,CAAC;IACxG,IAAI,CAAC,YAAY,IAAI,OAAO,YAAY,KAAK,QAAQ,EAAE,CAAC;QACtD,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,MAAM,GAAG,YAId,CAAC;IACF,MAAM,UAAU,GAAG,OAAO,MAAM,CAAC,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC;IACzF,IAAI,CAAC,UAAU,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,UAAU,IAAI,CAAC,EAAE,CAAC;QACnE,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,MAAM,GAAG,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IAClG,MAAM,YAAY,GAAG,MAAM,KAAK,mBAAmB,IAAI,OAAO,MAAM,CAAC,eAAe,KAAK,QAAQ,CAAC;IAClG,OAAO,YAAY,CAAC;AACtB,CAAC;AAED,MAAM,UAAU,wBAAwB,CAAC,KAAc;IACrD,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACxC,OAAO,KAAK,CAAC;IACf,CAAC;IACD,qEAAqE;IACrE,6CAA6C;IAC7C,IAAI,8BAA8B,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1C,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,aAAa,GAAG,KAAsB,CAAC;IAC7C,IAAI,aAAa,CAAC,SAAS,KAAK,IAAI,EAAE,CAAC;QACrC,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,MAAM,GAAG,sBAAsB,CAAC,KAAK,CAAC,CAAC;IAC7C,IAAI,MAAM,KAAK,GAAG,IAAI,MAAM,KAAK,GAAG,IAAI,MAAM,KAAK,GAAG,EAAE,CAAC;QACvD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,IAAI,GAAG,EAAE,CAAC;QAChD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,KAAc;IACnD,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACxC,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,gBAAgB,GAA8B,EAAE,CAAC;IACvD,MAAM,YAAY,GAAI,KAAkC,CAAC,UAAU,CAAC;IACpE,IAAI,OAAO,YAAY,KAAK,QAAQ,EAAE,CAAC;QACrC,gBAAgB,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IACtC,CAAC;IACD,MAAM,eAAe,GAAI,KAA8B,CAAC,MAAM,CAAC;IAC/D,IAAI,OAAO,eAAe,KAAK,QAAQ,EAAE,CAAC;QACxC,gBAAgB,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IACzC,CAAC;IACD,MAAM,YAAY,GAAI,KAA+B,CAAC,OAAO,CAAC;IAC9D,IAAI,YAAY,IAAI,OAAO,YAAY,KAAK,QAAQ,EAAE,CAAC;QACrD,MAAM,YAAY,GAAI,YAAqC,CAAC,MAAM,CAAC;QACnE,IAAI,OAAO,YAAY,KAAK,QAAQ,EAAE,CAAC;YACrC,gBAAgB,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACtC,CAAC;IACH,CAAC;IACD,MAAM,QAAQ,GAAI,KAAgC,CAAC,QAAQ,CAAC;IAC5D,IAAI,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC7C,MAAM,UAAU,GAAI,QAAiC,CAAC,MAAM,CAAC;QAC7D,IAAI,OAAO,UAAU,KAAK,QAAQ,EAAE,CAAC;YACnC,gBAAgB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACpC,CAAC;QACD,MAAM,cAAc,GAAI,QAAqC,CAAC,UAAU,CAAC;QACzE,IAAI,OAAO,cAAc,KAAK,QAAQ,EAAE,CAAC;YACvC,gBAAgB,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QACxC,CAAC;IACH,CAAC;IACD,MAAM,QAAQ,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC,SAAS,EAAuB,EAAE,CAAC,OAAO,SAAS,KAAK,QAAQ,CAAC,CAAC;IAC1G,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACjC,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,MAAM,OAAO,GAAI,KAA+B,CAAC,OAAO,CAAC;IACzD,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QAChC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;QAC/C,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAC7C,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC1B,OAAO,MAAM,CAAC;YAChB,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,KAAc;IAChD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;QAC5C,OAAO,KAAK,CAAC,OAAO,CAAC;IACvB,CAAC;IACD,IAAI,OAAQ,KAA+B,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;QACjE,OAAQ,KAA6B,CAAC,OAAO,CAAC;IAChD,CAAC;IACD,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC"}
|
|
1
|
+
{"version":3,"file":"executor-provider.js","sourceRoot":"","sources":["../../../../src/server/runtime/http-server/executor-provider.ts"],"names":[],"mappings":"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"}
|
|
@@ -18,6 +18,7 @@ import type { ServerConfigV2, ServerStatusV2 } from './types.js';
|
|
|
18
18
|
export declare class RouteCodexHttpServer {
|
|
19
19
|
private app;
|
|
20
20
|
private server?;
|
|
21
|
+
private activeSockets;
|
|
21
22
|
private config;
|
|
22
23
|
private errorHandling;
|
|
23
24
|
private _isInitialized;
|
|
@@ -39,12 +40,15 @@ export declare class RouteCodexHttpServer {
|
|
|
39
40
|
private readonly coloredLogger;
|
|
40
41
|
private managerDaemon;
|
|
41
42
|
private readonly stats;
|
|
43
|
+
private restartChain;
|
|
42
44
|
constructor(config: ServerConfigV2);
|
|
43
45
|
private resolveVirtualRouterInput;
|
|
44
46
|
private getModuleDependencies;
|
|
45
47
|
/**
|
|
46
48
|
* Register Daemon Admin UI route.
|
|
47
|
-
* Serves docs/daemon-admin-ui.html as a static page
|
|
49
|
+
* Serves docs/daemon-admin-ui.html as a static page.
|
|
50
|
+
* - If `httpserver.apikey` is not configured: localhost-only.
|
|
51
|
+
* - If `httpserver.apikey` is configured: allow remote UI access (API calls still require apikey).
|
|
48
52
|
*/
|
|
49
53
|
private registerDaemonAdminUiRoute;
|
|
50
54
|
private getErrorHandlingShim;
|
|
@@ -63,10 +67,12 @@ export declare class RouteCodexHttpServer {
|
|
|
63
67
|
private bootstrapVirtualRouter;
|
|
64
68
|
private ensureHubPipelineCtor;
|
|
65
69
|
private isPipelineReady;
|
|
70
|
+
private isQuotaRoutingEnabled;
|
|
66
71
|
/**
|
|
67
72
|
* 初始化服务器
|
|
68
73
|
*/
|
|
69
74
|
initialize(): Promise<void>;
|
|
75
|
+
private restartRuntimeFromDisk;
|
|
70
76
|
/**
|
|
71
77
|
* 启动服务器
|
|
72
78
|
*/
|
|
@@ -33,6 +33,7 @@ import { HealthManagerModule } from '../../../manager/modules/health/index.js';
|
|
|
33
33
|
import { RoutingStateManagerModule } from '../../../manager/modules/routing/index.js';
|
|
34
34
|
import { TokenManagerModule } from '../../../manager/modules/token/index.js';
|
|
35
35
|
import { StatsManager } from './stats-manager.js';
|
|
36
|
+
import { loadRouteCodexConfig } from '../../../config/routecodex-config-loader.js';
|
|
36
37
|
/**
|
|
37
38
|
* RouteCodex Server V2
|
|
38
39
|
*
|
|
@@ -41,6 +42,7 @@ import { StatsManager } from './stats-manager.js';
|
|
|
41
42
|
export class RouteCodexHttpServer {
|
|
42
43
|
app;
|
|
43
44
|
server;
|
|
45
|
+
activeSockets = new Set();
|
|
44
46
|
config;
|
|
45
47
|
errorHandling;
|
|
46
48
|
_isInitialized = false;
|
|
@@ -63,6 +65,7 @@ export class RouteCodexHttpServer {
|
|
|
63
65
|
coloredLogger = createServerColoredLogger();
|
|
64
66
|
managerDaemon = null;
|
|
65
67
|
stats = new StatsManager();
|
|
68
|
+
restartChain = Promise.resolve();
|
|
66
69
|
constructor(config) {
|
|
67
70
|
this.config = config;
|
|
68
71
|
this.app = express();
|
|
@@ -107,14 +110,18 @@ export class RouteCodexHttpServer {
|
|
|
107
110
|
}
|
|
108
111
|
/**
|
|
109
112
|
* Register Daemon Admin UI route.
|
|
110
|
-
* Serves docs/daemon-admin-ui.html as a static page
|
|
113
|
+
* Serves docs/daemon-admin-ui.html as a static page.
|
|
114
|
+
* - If `httpserver.apikey` is not configured: localhost-only.
|
|
115
|
+
* - If `httpserver.apikey` is configured: allow remote UI access (API calls still require apikey).
|
|
111
116
|
*/
|
|
112
117
|
registerDaemonAdminUiRoute() {
|
|
113
118
|
this.app.get('/daemon/admin', async (req, res) => {
|
|
114
119
|
try {
|
|
115
120
|
const ip = req.socket?.remoteAddress || '';
|
|
116
|
-
const
|
|
117
|
-
|
|
121
|
+
const isLocal = ip === '127.0.0.1' || ip === '::1' || ip === '::ffff:127.0.0.1';
|
|
122
|
+
const expectedKey = typeof this.config?.server?.apikey === 'string' ? this.config.server.apikey.trim() : '';
|
|
123
|
+
const hasConfiguredKey = Boolean(expectedKey);
|
|
124
|
+
if (!isLocal && !hasConfiguredKey) {
|
|
118
125
|
res.status(403).json({ error: { message: 'forbidden', code: 'forbidden' } });
|
|
119
126
|
return;
|
|
120
127
|
}
|
|
@@ -131,6 +138,10 @@ export class RouteCodexHttpServer {
|
|
|
131
138
|
html = await fs.readFile(fallback, 'utf8');
|
|
132
139
|
}
|
|
133
140
|
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
|
141
|
+
// Avoid stale admin UI in browsers / proxies after upgrades.
|
|
142
|
+
res.setHeader('Cache-Control', 'no-store, max-age=0');
|
|
143
|
+
res.setHeader('Pragma', 'no-cache');
|
|
144
|
+
res.setHeader('X-RouteCodex-Version', String(process.env.ROUTECODEX_VERSION || 'dev'));
|
|
134
145
|
res.send(html);
|
|
135
146
|
}
|
|
136
147
|
catch (error) {
|
|
@@ -345,6 +356,13 @@ export class RouteCodexHttpServer {
|
|
|
345
356
|
isPipelineReady() {
|
|
346
357
|
return Boolean(this.hubPipeline);
|
|
347
358
|
}
|
|
359
|
+
isQuotaRoutingEnabled() {
|
|
360
|
+
const flag = this.config.server.quotaRoutingEnabled;
|
|
361
|
+
if (typeof flag === 'boolean') {
|
|
362
|
+
return flag;
|
|
363
|
+
}
|
|
364
|
+
return true;
|
|
365
|
+
}
|
|
348
366
|
/**
|
|
349
367
|
* 初始化服务器
|
|
350
368
|
*/
|
|
@@ -357,7 +375,7 @@ export class RouteCodexHttpServer {
|
|
|
357
375
|
// 初始化 ManagerDaemon 骨架(当前模块为占位实现,不改变行为)
|
|
358
376
|
if (!this.managerDaemon) {
|
|
359
377
|
const serverId = `${this.config.server.host}:${this.config.server.port}`;
|
|
360
|
-
const daemon = new ManagerDaemon({ serverId });
|
|
378
|
+
const daemon = new ManagerDaemon({ serverId, quotaRoutingEnabled: this.isQuotaRoutingEnabled() });
|
|
361
379
|
daemon.registerModule(new TokenManagerModule());
|
|
362
380
|
daemon.registerModule(new RoutingStateManagerModule());
|
|
363
381
|
daemon.registerModule(new HealthManagerModule());
|
|
@@ -385,6 +403,7 @@ export class RouteCodexHttpServer {
|
|
|
385
403
|
buildHandlerContext: () => this.buildHandlerContext(),
|
|
386
404
|
getPipelineReady: () => this.isPipelineReady(),
|
|
387
405
|
handleError: (error, context) => this.handleError(error, context),
|
|
406
|
+
restartRuntimeFromDisk: async () => await this.restartRuntimeFromDisk(),
|
|
388
407
|
getHealthSnapshot: () => {
|
|
389
408
|
const healthModule = this.managerDaemon?.getModule('health');
|
|
390
409
|
return healthModule?.getCurrentSnapshot() ?? null;
|
|
@@ -400,6 +419,10 @@ export class RouteCodexHttpServer {
|
|
|
400
419
|
},
|
|
401
420
|
getManagerDaemon: () => this.managerDaemon,
|
|
402
421
|
getVirtualRouterArtifacts: () => this.currentRouterArtifacts,
|
|
422
|
+
getStatsSnapshot: () => ({
|
|
423
|
+
session: this.stats.snapshot(Math.round(process.uptime() * 1000)),
|
|
424
|
+
historical: this.stats.snapshotHistorical()
|
|
425
|
+
}),
|
|
403
426
|
getServerId: () => `${this.config.server.host}:${this.config.server.port}`
|
|
404
427
|
});
|
|
405
428
|
this._isInitialized = true;
|
|
@@ -410,6 +433,38 @@ export class RouteCodexHttpServer {
|
|
|
410
433
|
throw error;
|
|
411
434
|
}
|
|
412
435
|
}
|
|
436
|
+
async restartRuntimeFromDisk() {
|
|
437
|
+
// Serialize restarts to avoid racing provider disposals / hub updates.
|
|
438
|
+
const run = async () => {
|
|
439
|
+
const loaded = await loadRouteCodexConfig(this.config?.configPath);
|
|
440
|
+
const userConfig = asRecord(loaded.userConfig) ?? {};
|
|
441
|
+
const httpServerNode = asRecord(userConfig.httpserver) ??
|
|
442
|
+
asRecord(asRecord(userConfig.modules)?.httpserver)?.config ??
|
|
443
|
+
null;
|
|
444
|
+
const nextApiKey = httpServerNode ? (typeof httpServerNode.apikey === 'string' ? String(httpServerNode.apikey).trim() : '') : '';
|
|
445
|
+
const nextHost = httpServerNode ? (typeof httpServerNode.host === 'string' ? String(httpServerNode.host).trim() : '') : '';
|
|
446
|
+
const nextPort = httpServerNode ? (typeof httpServerNode.port === 'number' ? Number(httpServerNode.port) : NaN) : NaN;
|
|
447
|
+
const warnings = [];
|
|
448
|
+
// Best-effort: allow rotating apikey at runtime by mutating config object.
|
|
449
|
+
if (typeof nextApiKey === 'string' && nextApiKey !== String(this.config.server.apikey || '')) {
|
|
450
|
+
this.config.server.apikey = nextApiKey || undefined;
|
|
451
|
+
}
|
|
452
|
+
// host/port changes require rebind; record warnings but do not attempt to re-listen.
|
|
453
|
+
if (nextHost && nextHost !== this.config.server.host) {
|
|
454
|
+
warnings.push(`httpserver.host changed to "${nextHost}" but live server keeps "${this.config.server.host}" until process restart`);
|
|
455
|
+
}
|
|
456
|
+
if (Number.isFinite(nextPort) && nextPort > 0 && nextPort !== this.config.server.port) {
|
|
457
|
+
warnings.push(`httpserver.port changed to ${nextPort} but live server keeps ${this.config.server.port} until process restart`);
|
|
458
|
+
}
|
|
459
|
+
// Keep the server's configPath aligned with what was loaded.
|
|
460
|
+
this.config.configPath = loaded.configPath;
|
|
461
|
+
await this.reloadRuntime(loaded.userConfig, { providerProfiles: loaded.providerProfiles });
|
|
462
|
+
return { reloadedAt: Date.now(), configPath: loaded.configPath, ...(warnings.length ? { warnings } : {}) };
|
|
463
|
+
};
|
|
464
|
+
const slot = this.restartChain.then(run);
|
|
465
|
+
this.restartChain = slot.then(() => undefined, () => undefined);
|
|
466
|
+
return await slot;
|
|
467
|
+
}
|
|
413
468
|
/**
|
|
414
469
|
* 启动服务器
|
|
415
470
|
*/
|
|
@@ -423,6 +478,22 @@ export class RouteCodexHttpServer {
|
|
|
423
478
|
console.log(`[RouteCodexHttpServer] Server started on ${this.config.server.host}:${this.config.server.port}`);
|
|
424
479
|
resolve();
|
|
425
480
|
});
|
|
481
|
+
// In test runners (Jest), prevent the listen handle from keeping the process alive
|
|
482
|
+
// in case some keep-alive sockets linger.
|
|
483
|
+
if (process.env.JEST_WORKER_ID || process.env.NODE_ENV === 'test') {
|
|
484
|
+
try {
|
|
485
|
+
this.server.unref?.();
|
|
486
|
+
}
|
|
487
|
+
catch {
|
|
488
|
+
// ignore
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
this.server.on('connection', (socket) => {
|
|
492
|
+
this.activeSockets.add(socket);
|
|
493
|
+
socket.on('close', () => {
|
|
494
|
+
this.activeSockets.delete(socket);
|
|
495
|
+
});
|
|
496
|
+
});
|
|
426
497
|
this.server.on('error', async (error) => {
|
|
427
498
|
await this.handleError(error, 'server_start');
|
|
428
499
|
reject(error);
|
|
@@ -434,6 +505,24 @@ export class RouteCodexHttpServer {
|
|
|
434
505
|
*/
|
|
435
506
|
async stop() {
|
|
436
507
|
if (this.server) {
|
|
508
|
+
// Best-effort: close any open keep-alive sockets so server.close can finish.
|
|
509
|
+
for (const socket of this.activeSockets) {
|
|
510
|
+
try {
|
|
511
|
+
socket.destroy();
|
|
512
|
+
}
|
|
513
|
+
catch {
|
|
514
|
+
// ignore
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
this.activeSockets.clear();
|
|
518
|
+
try {
|
|
519
|
+
const srv = this.server;
|
|
520
|
+
srv.closeIdleConnections?.();
|
|
521
|
+
srv.closeAllConnections?.();
|
|
522
|
+
}
|
|
523
|
+
catch {
|
|
524
|
+
// ignore
|
|
525
|
+
}
|
|
437
526
|
return new Promise(resolve => {
|
|
438
527
|
this.server?.close(async () => {
|
|
439
528
|
this._isRunning = false;
|
|
@@ -450,6 +539,13 @@ export class RouteCodexHttpServer {
|
|
|
450
539
|
catch {
|
|
451
540
|
// ignore manager shutdown failures
|
|
452
541
|
}
|
|
542
|
+
try {
|
|
543
|
+
this.server?.removeAllListeners();
|
|
544
|
+
}
|
|
545
|
+
catch {
|
|
546
|
+
// ignore
|
|
547
|
+
}
|
|
548
|
+
this.server = undefined;
|
|
453
549
|
await this.errorHandling.destroy();
|
|
454
550
|
try {
|
|
455
551
|
const uptimeMs = Math.round(process.uptime() * 1000);
|
|
@@ -551,15 +647,24 @@ export class RouteCodexHttpServer {
|
|
|
551
647
|
hubConfig.routingStateStore = routingStateStore;
|
|
552
648
|
}
|
|
553
649
|
const quotaModule = this.managerDaemon?.getModule('provider-quota');
|
|
554
|
-
|
|
555
|
-
const quotaEnabled = quotaFlagRaw === '1' || quotaFlagRaw === 'true';
|
|
556
|
-
if (quotaEnabled && quotaModule && typeof quotaModule.getQuotaView === 'function') {
|
|
650
|
+
if (this.isQuotaRoutingEnabled() && quotaModule && typeof quotaModule.getQuotaView === 'function') {
|
|
557
651
|
hubConfig.quotaView = quotaModule.getQuotaView();
|
|
558
652
|
}
|
|
559
653
|
if (!this.hubPipeline) {
|
|
560
654
|
this.hubPipeline = new hubCtor(hubConfig);
|
|
561
655
|
}
|
|
562
656
|
else {
|
|
657
|
+
const existing = this.hubPipeline;
|
|
658
|
+
try {
|
|
659
|
+
existing.updateRuntimeDeps?.({
|
|
660
|
+
...(healthStore ? { healthStore } : {}),
|
|
661
|
+
...(routingStateStore ? { routingStateStore } : {}),
|
|
662
|
+
...('quotaView' in hubConfig ? { quotaView: hubConfig.quotaView } : {})
|
|
663
|
+
});
|
|
664
|
+
}
|
|
665
|
+
catch {
|
|
666
|
+
// best-effort: runtime deps updates must never block reload
|
|
667
|
+
}
|
|
563
668
|
this.hubPipeline.updateVirtualRouterConfig(bootstrapArtifacts.config);
|
|
564
669
|
}
|
|
565
670
|
await this.initializeProviderRuntimes(bootstrapArtifacts);
|
|
@@ -577,17 +682,51 @@ export class RouteCodexHttpServer {
|
|
|
577
682
|
}
|
|
578
683
|
await this.disposeProviders();
|
|
579
684
|
this.providerKeyToRuntimeKey.clear();
|
|
685
|
+
const quotaModule = this.managerDaemon?.getModule('provider-quota');
|
|
686
|
+
// Multiple providerKeys may share the same runtimeKey (single provider handle with multiple models).
|
|
687
|
+
// Quota is tracked by providerKey, so we need a stable way to derive authType for every key,
|
|
688
|
+
// even when the handle has already been created for this runtimeKey.
|
|
689
|
+
const runtimeKeyAuthType = new Map();
|
|
580
690
|
for (const [providerKey, runtime] of Object.entries(runtimeMap)) {
|
|
581
691
|
if (!runtime) {
|
|
582
692
|
continue;
|
|
583
693
|
}
|
|
584
694
|
const runtimeKey = runtime.runtimeKey || providerKey;
|
|
695
|
+
const authTypeFromRuntime = runtime && runtime.auth && typeof runtime.auth.type === 'string'
|
|
696
|
+
? String(runtime.auth.type).trim()
|
|
697
|
+
: null;
|
|
585
698
|
if (!this.providerHandles.has(runtimeKey)) {
|
|
586
699
|
const resolvedRuntime = await this.materializeRuntimeProfile(runtime);
|
|
587
700
|
const patchedRuntime = this.applyProviderProfileOverrides(resolvedRuntime);
|
|
701
|
+
try {
|
|
702
|
+
const authTypeFromPatched = patchedRuntime && patchedRuntime.auth && typeof patchedRuntime.auth.type === 'string'
|
|
703
|
+
? String(patchedRuntime.auth.type).trim()
|
|
704
|
+
: null;
|
|
705
|
+
if (authTypeFromPatched) {
|
|
706
|
+
runtimeKeyAuthType.set(runtimeKey, authTypeFromPatched);
|
|
707
|
+
}
|
|
708
|
+
else if (authTypeFromRuntime) {
|
|
709
|
+
runtimeKeyAuthType.set(runtimeKey, authTypeFromRuntime);
|
|
710
|
+
}
|
|
711
|
+
else if (!runtimeKeyAuthType.has(runtimeKey)) {
|
|
712
|
+
runtimeKeyAuthType.set(runtimeKey, null);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
catch {
|
|
716
|
+
// ignore authType derivation failures
|
|
717
|
+
}
|
|
588
718
|
const handle = await this.createProviderHandle(runtimeKey, patchedRuntime);
|
|
589
719
|
this.providerHandles.set(runtimeKey, handle);
|
|
590
720
|
}
|
|
721
|
+
// Register static quota metadata for every providerKey (not just per runtimeKey).
|
|
722
|
+
// Use the runtimeKey authType when available so shared runtimeKey models inherit correct policy.
|
|
723
|
+
try {
|
|
724
|
+
const authType = runtimeKeyAuthType.get(runtimeKey) ?? authTypeFromRuntime;
|
|
725
|
+
quotaModule?.registerProviderStaticConfig?.(providerKey, { authType: authType ?? null });
|
|
726
|
+
}
|
|
727
|
+
catch {
|
|
728
|
+
// best-effort: quota static config registration must never block runtime init
|
|
729
|
+
}
|
|
591
730
|
this.providerKeyToRuntimeKey.set(providerKey, runtimeKey);
|
|
592
731
|
}
|
|
593
732
|
}
|
|
@@ -756,16 +895,16 @@ export class RouteCodexHttpServer {
|
|
|
756
895
|
// snapshot failure should not block request path
|
|
757
896
|
}
|
|
758
897
|
const pipelineLabel = 'hub';
|
|
759
|
-
|
|
760
|
-
|
|
898
|
+
const iterationMetadata = initialMetadata;
|
|
899
|
+
const _followupTriggered = false;
|
|
761
900
|
// Provider 级别不再在单个 HTTP 请求内执行重复尝试,
|
|
762
901
|
// 429/配额/熔断逻辑统一交由 llmswitch-core VirtualRouter 处理。
|
|
763
|
-
const
|
|
764
|
-
let
|
|
902
|
+
const _maxAttempts = 1;
|
|
903
|
+
let _attempt = 0;
|
|
765
904
|
const originalBodySnapshot = this.cloneRequestPayload(input.body);
|
|
766
905
|
const excludedProviderKeys = new Set();
|
|
767
906
|
while (true) {
|
|
768
|
-
|
|
907
|
+
_attempt += 1;
|
|
769
908
|
// 每次尝试前重置请求 body,避免上一轮 HubPipeline 的就地改写导致
|
|
770
909
|
// 第二轮出现 ChatEnvelopeValidationError(messages_missing) 之类的问题。
|
|
771
910
|
if (originalBodySnapshot && typeof originalBodySnapshot === 'object') {
|
|
@@ -907,7 +1046,7 @@ export class RouteCodexHttpServer {
|
|
|
907
1046
|
});
|
|
908
1047
|
const usage = this.extractUsageFromResult(converted, mergedMetadata);
|
|
909
1048
|
const quotaModule = this.managerDaemon?.getModule('provider-quota');
|
|
910
|
-
if (quotaModule) {
|
|
1049
|
+
if (this.isQuotaRoutingEnabled() && quotaModule) {
|
|
911
1050
|
const totalTokens = typeof usage?.total_tokens === 'number' && Number.isFinite(usage.total_tokens)
|
|
912
1051
|
? Math.max(0, usage.total_tokens)
|
|
913
1052
|
: Math.max(0, (typeof usage?.prompt_tokens === 'number' && Number.isFinite(usage.prompt_tokens) ? usage.prompt_tokens : 0) +
|
|
@@ -929,6 +1068,24 @@ export class RouteCodexHttpServer {
|
|
|
929
1068
|
}
|
|
930
1069
|
}
|
|
931
1070
|
this.stats.recordCompletion(input.requestId, { usage, error: false });
|
|
1071
|
+
// 回传 session_id 和 conversation_id 到响应头(如果存在)
|
|
1072
|
+
const sessionId = typeof mergedMetadata.sessionId === "string" && mergedMetadata.sessionId.trim()
|
|
1073
|
+
? mergedMetadata.sessionId.trim()
|
|
1074
|
+
: undefined;
|
|
1075
|
+
const conversationId = typeof mergedMetadata.conversationId === "string" && mergedMetadata.conversationId.trim()
|
|
1076
|
+
? mergedMetadata.conversationId.trim()
|
|
1077
|
+
: undefined;
|
|
1078
|
+
if (sessionId || conversationId) {
|
|
1079
|
+
if (!converted.headers) {
|
|
1080
|
+
converted.headers = {};
|
|
1081
|
+
}
|
|
1082
|
+
if (sessionId && !converted.headers["session_id"]) {
|
|
1083
|
+
converted.headers["session_id"] = sessionId;
|
|
1084
|
+
}
|
|
1085
|
+
if (conversationId && !converted.headers["conversation_id"]) {
|
|
1086
|
+
converted.headers["conversation_id"] = conversationId;
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
932
1089
|
return converted;
|
|
933
1090
|
}
|
|
934
1091
|
catch (error) {
|
|
@@ -941,7 +1098,7 @@ export class RouteCodexHttpServer {
|
|
|
941
1098
|
providerLabel
|
|
942
1099
|
});
|
|
943
1100
|
const quotaModule = this.managerDaemon?.getModule('provider-quota');
|
|
944
|
-
if (quotaModule) {
|
|
1101
|
+
if (this.isQuotaRoutingEnabled() && quotaModule) {
|
|
945
1102
|
try {
|
|
946
1103
|
quotaModule.recordProviderUsage({ providerKey: target.providerKey, requestedTokens: 0 });
|
|
947
1104
|
}
|