@jsonstudio/rcc 0.89.333 → 0.89.524
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 +3 -3
- package/dist/build-info.js.map +1 -1
- package/dist/cli.js +62 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/token-daemon.d.ts +2 -0
- package/dist/commands/token-daemon.js +183 -0
- package/dist/commands/token-daemon.js.map +1 -0
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/dist/modules/llmswitch/bridge.d.ts +1 -1
- package/dist/modules/llmswitch/bridge.js +3 -2
- package/dist/modules/llmswitch/bridge.js.map +1 -1
- package/dist/modules/pipeline/utils/colored-logger.js +3 -1
- package/dist/modules/pipeline/utils/colored-logger.js.map +1 -1
- package/dist/providers/auth/gemini-cli-userinfo-helper.js +12 -2
- package/dist/providers/auth/gemini-cli-userinfo-helper.js.map +1 -1
- package/dist/providers/auth/oauth-lifecycle.js +261 -22
- package/dist/providers/auth/oauth-lifecycle.js.map +1 -1
- package/dist/providers/core/config/oauth-flows.d.ts +23 -0
- package/dist/providers/core/config/oauth-flows.js +92 -5
- package/dist/providers/core/config/oauth-flows.js.map +1 -1
- package/dist/providers/core/config/provider-oauth-configs.js +9 -3
- package/dist/providers/core/config/provider-oauth-configs.js.map +1 -1
- package/dist/providers/core/config/service-profiles.js +18 -10
- package/dist/providers/core/config/service-profiles.js.map +1 -1
- package/dist/providers/core/runtime/gemini-cli-http-provider.js +87 -20
- package/dist/providers/core/runtime/gemini-cli-http-provider.js.map +1 -1
- package/dist/providers/core/runtime/http-request-executor.d.ts +1 -0
- package/dist/providers/core/runtime/http-request-executor.js +41 -1
- package/dist/providers/core/runtime/http-request-executor.js.map +1 -1
- package/dist/providers/core/runtime/http-transport-provider.d.ts +2 -0
- package/dist/providers/core/runtime/http-transport-provider.js +37 -2
- package/dist/providers/core/runtime/http-transport-provider.js.map +1 -1
- package/dist/providers/core/runtime/responses-provider.js +8 -3
- package/dist/providers/core/runtime/responses-provider.js.map +1 -1
- package/dist/providers/core/runtime/vision-debug-utils.d.ts +13 -0
- package/dist/providers/core/runtime/vision-debug-utils.js +114 -0
- package/dist/providers/core/runtime/vision-debug-utils.js.map +1 -0
- package/dist/providers/core/strategies/oauth-auth-code-flow.js +75 -26
- package/dist/providers/core/strategies/oauth-auth-code-flow.js.map +1 -1
- package/dist/providers/core/utils/http-client.js +2 -1
- package/dist/providers/core/utils/http-client.js.map +1 -1
- package/dist/providers/core/utils/snapshot-writer.d.ts +1 -1
- package/dist/providers/core/utils/snapshot-writer.js.map +1 -1
- package/dist/server/handlers/sse-dispatcher.js +22 -2
- package/dist/server/handlers/sse-dispatcher.js.map +1 -1
- package/dist/server/runtime/http-server/index.d.ts +9 -0
- package/dist/server/runtime/http-server/index.js +512 -144
- package/dist/server/runtime/http-server/index.js.map +1 -1
- package/dist/server/runtime/http-server/request-executor.d.ts +10 -0
- package/dist/server/runtime/http-server/request-executor.js +553 -159
- package/dist/server/runtime/http-server/request-executor.js.map +1 -1
- package/dist/server/runtime/http-server/routes.d.ts +5 -0
- package/dist/server/runtime/http-server/routes.js +69 -0
- package/dist/server/runtime/http-server/routes.js.map +1 -1
- package/dist/server/runtime/http-server/runtime-manager.js +18 -0
- package/dist/server/runtime/http-server/runtime-manager.js.map +1 -1
- package/dist/server/utils/utf8-chunk-buffer.d.ts +43 -0
- package/dist/server/utils/utf8-chunk-buffer.js +132 -0
- package/dist/server/utils/utf8-chunk-buffer.js.map +1 -0
- package/dist/token-daemon/index.d.ts +7 -0
- package/dist/token-daemon/index.js +242 -0
- package/dist/token-daemon/index.js.map +1 -0
- package/dist/token-daemon/server-utils.d.ts +33 -0
- package/dist/token-daemon/server-utils.js +155 -0
- package/dist/token-daemon/server-utils.js.map +1 -0
- package/dist/token-daemon/token-daemon.d.ts +20 -0
- package/dist/token-daemon/token-daemon.js +144 -0
- package/dist/token-daemon/token-daemon.js.map +1 -0
- package/dist/token-daemon/token-types.d.ts +44 -0
- package/dist/token-daemon/token-types.js +18 -0
- package/dist/token-daemon/token-types.js.map +1 -0
- package/dist/token-daemon/token-utils.d.ts +17 -0
- package/dist/token-daemon/token-utils.js +153 -0
- package/dist/token-daemon/token-utils.js.map +1 -0
- package/dist/tools/semantic-replay.js +7 -6
- package/dist/tools/semantic-replay.js.map +1 -1
- package/dist/utils/error-handler-registry.d.ts +36 -0
- package/dist/utils/error-handler-registry.js +93 -7
- package/dist/utils/error-handler-registry.js.map +1 -1
- package/node_modules/@jsonstudio/llms/README.md +2 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/codecs/gemini-openai-codec.js +137 -5
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/gemini-web-search.d.ts +17 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/gemini-web-search.js +68 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/glm-image-content.d.ts +2 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/glm-image-content.js +83 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/glm-vision-prompt.d.ts +11 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/glm-vision-prompt.js +177 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/glm-web-search.d.ts +2 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/glm-web-search.js +63 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/universal-shape-filter.js +11 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/profiles/chat-gemini.json +17 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/profiles/chat-glm.json +190 -181
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/profiles/chat-iflow.json +195 -195
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/profiles/chat-lmstudio.json +43 -43
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/profiles/chat-qwen.json +20 -20
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/profiles/responses-c4m.json +42 -42
- package/node_modules/@jsonstudio/llms/dist/conversion/config/sample-config.json +1 -1
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/compat/compat-pipeline-executor.js +24 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/compat/compat-types.d.ts +8 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/hub-pipeline.js +39 -4
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/target-utils.js +6 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/process/chat-process.js +213 -1
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/response/provider-response.d.ts +34 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/response/provider-response.js +84 -24
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/response/server-side-tools.d.ts +26 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/response/server-side-tools.js +383 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/semantic-mappers/gemini-mapper.js +241 -14
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/semantic-mappers/responses-mapper.js +17 -1
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/standardized-bridge.js +14 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/types/standardized.d.ts +1 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/responses/responses-openai-bridge.js +82 -3
- package/node_modules/@jsonstudio/llms/dist/conversion/shared/anthropic-message-utils.js +92 -3
- package/node_modules/@jsonstudio/llms/dist/conversion/shared/bridge-message-utils.js +137 -10
- package/node_modules/@jsonstudio/llms/dist/conversion/shared/responses-output-builder.js +43 -2
- package/node_modules/@jsonstudio/llms/dist/conversion/shared/snapshot-utils.js +17 -47
- package/node_modules/@jsonstudio/llms/dist/conversion/shared/tool-filter-pipeline.js +1 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/shared/tool-mapping.js +25 -2
- package/node_modules/@jsonstudio/llms/dist/index.d.ts +1 -0
- package/node_modules/@jsonstudio/llms/dist/index.js +1 -0
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/bootstrap.js +308 -43
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/classifier.js +11 -17
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/context-advisor.d.ts +0 -2
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/context-advisor.js +0 -12
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/engine.d.ts +17 -2
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/engine.js +332 -95
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/features.js +1 -1
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/message-utils.js +36 -24
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/provider-registry.js +2 -1
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/token-counter.js +14 -3
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/types.d.ts +66 -2
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/types.js +2 -1
- package/node_modules/@jsonstudio/llms/dist/servertool/engine.d.ts +27 -0
- package/node_modules/@jsonstudio/llms/dist/servertool/engine.js +60 -0
- package/node_modules/@jsonstudio/llms/dist/servertool/flow-types.d.ts +40 -0
- package/node_modules/@jsonstudio/llms/dist/servertool/flow-types.js +1 -0
- package/node_modules/@jsonstudio/llms/dist/servertool/handlers/vision.d.ts +1 -0
- package/node_modules/@jsonstudio/llms/dist/servertool/handlers/vision.js +194 -0
- package/node_modules/@jsonstudio/llms/dist/servertool/handlers/web-search.d.ts +1 -0
- package/node_modules/@jsonstudio/llms/dist/servertool/handlers/web-search.js +638 -0
- package/node_modules/@jsonstudio/llms/dist/servertool/orchestration-types.d.ts +33 -0
- package/node_modules/@jsonstudio/llms/dist/servertool/orchestration-types.js +1 -0
- package/node_modules/@jsonstudio/llms/dist/servertool/registry.d.ts +18 -0
- package/node_modules/@jsonstudio/llms/dist/servertool/registry.js +27 -0
- package/node_modules/@jsonstudio/llms/dist/servertool/server-side-tools.d.ts +8 -0
- package/node_modules/@jsonstudio/llms/dist/servertool/server-side-tools.js +208 -0
- package/node_modules/@jsonstudio/llms/dist/servertool/types.d.ts +88 -0
- package/node_modules/@jsonstudio/llms/dist/servertool/types.js +1 -0
- package/node_modules/@jsonstudio/llms/dist/servertool/vision-tool.d.ts +2 -0
- package/node_modules/@jsonstudio/llms/dist/servertool/vision-tool.js +185 -0
- package/node_modules/@jsonstudio/llms/dist/sse/json-to-sse/event-generators/responses.js +15 -3
- package/node_modules/@jsonstudio/llms/dist/sse/sse-to-json/builders/response-builder.js +6 -3
- package/node_modules/@jsonstudio/llms/dist/sse/sse-to-json/gemini-sse-to-json-converter.js +27 -1
- package/node_modules/@jsonstudio/llms/dist/sse/types/gemini-types.d.ts +20 -1
- package/node_modules/@jsonstudio/llms/dist/sse/types/responses-types.js +1 -1
- package/node_modules/@jsonstudio/llms/dist/telemetry/stats-center.d.ts +73 -0
- package/node_modules/@jsonstudio/llms/dist/telemetry/stats-center.js +280 -0
- package/node_modules/@jsonstudio/llms/package.json +1 -1
- package/package.json +2 -2
- package/scripts/pack-mode.mjs +2 -1
- package/scripts/publish-rcc.mjs +20 -4
- package/scripts/tests/virtual-router-health.mjs +141 -6
- package/dist/tools/replay-request.d.ts +0 -0
- package/dist/tools/replay-request.js +0 -2
- package/dist/tools/replay-request.js.map +0 -1
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
export interface VirtualRouterHitEvent {
|
|
2
|
+
requestId: string;
|
|
3
|
+
timestamp: number;
|
|
4
|
+
entryEndpoint: string;
|
|
5
|
+
routeName: string;
|
|
6
|
+
pool: string;
|
|
7
|
+
providerKey: string;
|
|
8
|
+
runtimeKey?: string;
|
|
9
|
+
providerType?: string;
|
|
10
|
+
modelId?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface ProviderUsageEvent {
|
|
13
|
+
requestId: string;
|
|
14
|
+
timestamp: number;
|
|
15
|
+
providerKey: string;
|
|
16
|
+
runtimeKey?: string;
|
|
17
|
+
providerType: string;
|
|
18
|
+
modelId?: string;
|
|
19
|
+
routeName?: string;
|
|
20
|
+
entryEndpoint?: string;
|
|
21
|
+
success: boolean;
|
|
22
|
+
latencyMs: number;
|
|
23
|
+
promptTokens?: number;
|
|
24
|
+
completionTokens?: number;
|
|
25
|
+
totalTokens?: number;
|
|
26
|
+
}
|
|
27
|
+
export interface RouterStatsBucket {
|
|
28
|
+
requestCount: number;
|
|
29
|
+
poolHitCount: Record<string, number>;
|
|
30
|
+
routeHitCount: Record<string, number>;
|
|
31
|
+
providerHitCount: Record<string, number>;
|
|
32
|
+
}
|
|
33
|
+
export interface RouterStatsSnapshot {
|
|
34
|
+
global: RouterStatsBucket;
|
|
35
|
+
byEntryEndpoint: Record<string, RouterStatsBucket>;
|
|
36
|
+
}
|
|
37
|
+
export interface ProviderStatsBucket {
|
|
38
|
+
requestCount: number;
|
|
39
|
+
successCount: number;
|
|
40
|
+
errorCount: number;
|
|
41
|
+
latencySumMs: number;
|
|
42
|
+
minLatencyMs: number;
|
|
43
|
+
maxLatencyMs: number;
|
|
44
|
+
usage: {
|
|
45
|
+
promptTokens: number;
|
|
46
|
+
completionTokens: number;
|
|
47
|
+
totalTokens: number;
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
export interface ProviderStatsSnapshot {
|
|
51
|
+
global: ProviderStatsBucket;
|
|
52
|
+
byProviderKey: Record<string, ProviderStatsBucket>;
|
|
53
|
+
byRoute: Record<string, ProviderStatsBucket>;
|
|
54
|
+
byEntryEndpoint: Record<string, ProviderStatsBucket>;
|
|
55
|
+
}
|
|
56
|
+
export interface StatsSnapshot {
|
|
57
|
+
router: RouterStatsSnapshot;
|
|
58
|
+
providers: ProviderStatsSnapshot;
|
|
59
|
+
}
|
|
60
|
+
export interface StatsCenterOptions {
|
|
61
|
+
enable?: boolean;
|
|
62
|
+
autoPrintOnExit?: boolean;
|
|
63
|
+
persistPath?: string | null;
|
|
64
|
+
}
|
|
65
|
+
export interface StatsCenter {
|
|
66
|
+
recordVirtualRouterHit(ev: VirtualRouterHitEvent): void;
|
|
67
|
+
recordProviderUsage(ev: ProviderUsageEvent): void;
|
|
68
|
+
getSnapshot(): StatsSnapshot;
|
|
69
|
+
flushToDisk(): Promise<void>;
|
|
70
|
+
reset(): void;
|
|
71
|
+
}
|
|
72
|
+
export declare function initStatsCenter(options?: StatsCenterOptions): StatsCenter;
|
|
73
|
+
export declare function getStatsCenter(): StatsCenter;
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import os from 'node:os';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import fs from 'node:fs/promises';
|
|
4
|
+
function createEmptyRouterBucket() {
|
|
5
|
+
return {
|
|
6
|
+
requestCount: 0,
|
|
7
|
+
poolHitCount: {},
|
|
8
|
+
routeHitCount: {},
|
|
9
|
+
providerHitCount: {}
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
function createEmptyProviderBucket() {
|
|
13
|
+
return {
|
|
14
|
+
requestCount: 0,
|
|
15
|
+
successCount: 0,
|
|
16
|
+
errorCount: 0,
|
|
17
|
+
latencySumMs: 0,
|
|
18
|
+
minLatencyMs: Number.POSITIVE_INFINITY,
|
|
19
|
+
maxLatencyMs: 0,
|
|
20
|
+
usage: {
|
|
21
|
+
promptTokens: 0,
|
|
22
|
+
completionTokens: 0,
|
|
23
|
+
totalTokens: 0
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function createEmptySnapshot() {
|
|
28
|
+
return {
|
|
29
|
+
router: {
|
|
30
|
+
global: createEmptyRouterBucket(),
|
|
31
|
+
byEntryEndpoint: {}
|
|
32
|
+
},
|
|
33
|
+
providers: {
|
|
34
|
+
global: createEmptyProviderBucket(),
|
|
35
|
+
byProviderKey: {},
|
|
36
|
+
byRoute: {},
|
|
37
|
+
byEntryEndpoint: {}
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
class NoopStatsCenter {
|
|
42
|
+
recordVirtualRouterHit() { }
|
|
43
|
+
recordProviderUsage() { }
|
|
44
|
+
getSnapshot() { return createEmptySnapshot(); }
|
|
45
|
+
async flushToDisk() { }
|
|
46
|
+
reset() { }
|
|
47
|
+
}
|
|
48
|
+
class DefaultStatsCenter {
|
|
49
|
+
snapshot = createEmptySnapshot();
|
|
50
|
+
dirty = false;
|
|
51
|
+
flushInFlight = false;
|
|
52
|
+
persistPath;
|
|
53
|
+
constructor(persistPath) {
|
|
54
|
+
if (persistPath === null) {
|
|
55
|
+
this.persistPath = null;
|
|
56
|
+
}
|
|
57
|
+
else if (typeof persistPath === 'string' && persistPath.trim().length) {
|
|
58
|
+
this.persistPath = persistPath.trim();
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
const base = path.join(os.homedir(), '.routecodex', 'stats');
|
|
62
|
+
this.persistPath = path.join(base, 'stats.json');
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
recordVirtualRouterHit(ev) {
|
|
66
|
+
if (!ev || !ev.routeName || !ev.providerKey) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const snap = this.snapshot;
|
|
70
|
+
this.applyRouterHitToBucket(snap.router.global, ev);
|
|
71
|
+
const entryKey = ev.entryEndpoint || 'unknown';
|
|
72
|
+
if (!snap.router.byEntryEndpoint[entryKey]) {
|
|
73
|
+
snap.router.byEntryEndpoint[entryKey] = createEmptyRouterBucket();
|
|
74
|
+
}
|
|
75
|
+
this.applyRouterHitToBucket(snap.router.byEntryEndpoint[entryKey], ev);
|
|
76
|
+
this.dirty = true;
|
|
77
|
+
}
|
|
78
|
+
recordProviderUsage(ev) {
|
|
79
|
+
if (!ev || !ev.providerKey || !ev.providerType) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const snap = this.snapshot;
|
|
83
|
+
this.applyProviderUsageToBucket(snap.providers.global, ev);
|
|
84
|
+
const providerKey = ev.providerKey;
|
|
85
|
+
if (!snap.providers.byProviderKey[providerKey]) {
|
|
86
|
+
snap.providers.byProviderKey[providerKey] = createEmptyProviderBucket();
|
|
87
|
+
}
|
|
88
|
+
this.applyProviderUsageToBucket(snap.providers.byProviderKey[providerKey], ev);
|
|
89
|
+
const routeKey = ev.routeName || 'unknown';
|
|
90
|
+
if (!snap.providers.byRoute[routeKey]) {
|
|
91
|
+
snap.providers.byRoute[routeKey] = createEmptyProviderBucket();
|
|
92
|
+
}
|
|
93
|
+
this.applyProviderUsageToBucket(snap.providers.byRoute[routeKey], ev);
|
|
94
|
+
const entryKey = ev.entryEndpoint || 'unknown';
|
|
95
|
+
if (!snap.providers.byEntryEndpoint[entryKey]) {
|
|
96
|
+
snap.providers.byEntryEndpoint[entryKey] = createEmptyProviderBucket();
|
|
97
|
+
}
|
|
98
|
+
this.applyProviderUsageToBucket(snap.providers.byEntryEndpoint[entryKey], ev);
|
|
99
|
+
this.dirty = true;
|
|
100
|
+
}
|
|
101
|
+
getSnapshot() {
|
|
102
|
+
return this.snapshot;
|
|
103
|
+
}
|
|
104
|
+
reset() {
|
|
105
|
+
this.snapshot = createEmptySnapshot();
|
|
106
|
+
this.dirty = false;
|
|
107
|
+
}
|
|
108
|
+
async flushToDisk() {
|
|
109
|
+
if (!this.persistPath || !this.dirty || this.flushInFlight) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
this.flushInFlight = true;
|
|
113
|
+
try {
|
|
114
|
+
const dir = path.dirname(this.persistPath);
|
|
115
|
+
await fs.mkdir(dir, { recursive: true });
|
|
116
|
+
const payload = JSON.stringify(this.snapshot, null, 2);
|
|
117
|
+
await fs.writeFile(this.persistPath, payload, 'utf-8');
|
|
118
|
+
this.dirty = false;
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
// ignore persistence errors
|
|
122
|
+
}
|
|
123
|
+
finally {
|
|
124
|
+
this.flushInFlight = false;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
applyRouterHitToBucket(bucket, ev) {
|
|
128
|
+
bucket.requestCount += 1;
|
|
129
|
+
if (ev.pool) {
|
|
130
|
+
bucket.poolHitCount[ev.pool] = (bucket.poolHitCount[ev.pool] || 0) + 1;
|
|
131
|
+
}
|
|
132
|
+
if (ev.routeName) {
|
|
133
|
+
bucket.routeHitCount[ev.routeName] = (bucket.routeHitCount[ev.routeName] || 0) + 1;
|
|
134
|
+
}
|
|
135
|
+
if (ev.providerKey) {
|
|
136
|
+
bucket.providerHitCount[ev.providerKey] = (bucket.providerHitCount[ev.providerKey] || 0) + 1;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
applyProviderUsageToBucket(bucket, ev) {
|
|
140
|
+
bucket.requestCount += 1;
|
|
141
|
+
if (ev.success) {
|
|
142
|
+
bucket.successCount += 1;
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
bucket.errorCount += 1;
|
|
146
|
+
}
|
|
147
|
+
if (Number.isFinite(ev.latencyMs) && ev.latencyMs >= 0) {
|
|
148
|
+
bucket.latencySumMs += ev.latencyMs;
|
|
149
|
+
if (ev.latencyMs < bucket.minLatencyMs) {
|
|
150
|
+
bucket.minLatencyMs = ev.latencyMs;
|
|
151
|
+
}
|
|
152
|
+
if (ev.latencyMs > bucket.maxLatencyMs) {
|
|
153
|
+
bucket.maxLatencyMs = ev.latencyMs;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (typeof ev.promptTokens === 'number' && Number.isFinite(ev.promptTokens)) {
|
|
157
|
+
bucket.usage.promptTokens += Math.max(0, ev.promptTokens);
|
|
158
|
+
}
|
|
159
|
+
if (typeof ev.completionTokens === 'number' && Number.isFinite(ev.completionTokens)) {
|
|
160
|
+
bucket.usage.completionTokens += Math.max(0, ev.completionTokens);
|
|
161
|
+
}
|
|
162
|
+
if (typeof ev.totalTokens === 'number' && Number.isFinite(ev.totalTokens)) {
|
|
163
|
+
bucket.usage.totalTokens += Math.max(0, ev.totalTokens);
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
const derivedTotal = (typeof ev.promptTokens === 'number' ? Math.max(0, ev.promptTokens) : 0) +
|
|
167
|
+
(typeof ev.completionTokens === 'number' ? Math.max(0, ev.completionTokens) : 0);
|
|
168
|
+
bucket.usage.totalTokens += derivedTotal;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
let instance = null;
|
|
173
|
+
function resolveEnableFlag(defaultValue) {
|
|
174
|
+
const raw = process.env.ROUTECODEX_STATS;
|
|
175
|
+
if (!raw)
|
|
176
|
+
return defaultValue;
|
|
177
|
+
const normalized = raw.trim().toLowerCase();
|
|
178
|
+
if (['1', 'true', 'yes', 'on'].includes(normalized))
|
|
179
|
+
return true;
|
|
180
|
+
if (['0', 'false', 'no', 'off'].includes(normalized))
|
|
181
|
+
return false;
|
|
182
|
+
return defaultValue;
|
|
183
|
+
}
|
|
184
|
+
function printStatsToConsole(snapshot) {
|
|
185
|
+
const router = snapshot.router;
|
|
186
|
+
const providers = snapshot.providers;
|
|
187
|
+
const totalRequests = router.global.requestCount;
|
|
188
|
+
const poolEntries = Object.entries(router.global.poolHitCount);
|
|
189
|
+
const providerEntries = Object.entries(router.global.providerHitCount);
|
|
190
|
+
// Router summary
|
|
191
|
+
// eslint-disable-next-line no-console
|
|
192
|
+
console.log('[stats] Virtual Router:');
|
|
193
|
+
// eslint-disable-next-line no-console
|
|
194
|
+
console.log(` total requests: ${totalRequests}`);
|
|
195
|
+
if (poolEntries.length) {
|
|
196
|
+
// eslint-disable-next-line no-console
|
|
197
|
+
console.log(' pools:');
|
|
198
|
+
for (const [pool, count] of poolEntries) {
|
|
199
|
+
const ratio = totalRequests > 0 ? (count / totalRequests) * 100 : 0;
|
|
200
|
+
// eslint-disable-next-line no-console
|
|
201
|
+
console.log(` ${pool}: ${count} (${ratio.toFixed(2)}%)`);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
if (providerEntries.length) {
|
|
205
|
+
// eslint-disable-next-line no-console
|
|
206
|
+
console.log(' top providers:');
|
|
207
|
+
const sorted = providerEntries.sort((a, b) => b[1] - a[1]).slice(0, 5);
|
|
208
|
+
for (const [providerKey, count] of sorted) {
|
|
209
|
+
// eslint-disable-next-line no-console
|
|
210
|
+
console.log(` ${providerKey}: ${count}`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
const globalProvider = providers.global;
|
|
214
|
+
const totalProviderRequests = globalProvider.requestCount;
|
|
215
|
+
const avgLatency = globalProvider.successCount > 0 ? globalProvider.latencySumMs / globalProvider.successCount : 0;
|
|
216
|
+
// Provider summary
|
|
217
|
+
// eslint-disable-next-line no-console
|
|
218
|
+
console.log('\n[stats] Providers:');
|
|
219
|
+
// eslint-disable-next-line no-console
|
|
220
|
+
console.log(` total requests : ${totalProviderRequests} (success=${globalProvider.successCount}, error=${globalProvider.errorCount})`);
|
|
221
|
+
// eslint-disable-next-line no-console
|
|
222
|
+
console.log(` avg latency : ${avgLatency.toFixed(1)} ms`);
|
|
223
|
+
// eslint-disable-next-line no-console
|
|
224
|
+
console.log(` total tokens : prompt=${globalProvider.usage.promptTokens} completion=${globalProvider.usage.completionTokens} total=${globalProvider.usage.totalTokens}`);
|
|
225
|
+
}
|
|
226
|
+
export function initStatsCenter(options) {
|
|
227
|
+
if (instance) {
|
|
228
|
+
return instance;
|
|
229
|
+
}
|
|
230
|
+
const enabled = resolveEnableFlag(options?.enable ?? true);
|
|
231
|
+
if (!enabled) {
|
|
232
|
+
instance = new NoopStatsCenter();
|
|
233
|
+
return instance;
|
|
234
|
+
}
|
|
235
|
+
const center = new DefaultStatsCenter(options?.persistPath);
|
|
236
|
+
instance = center;
|
|
237
|
+
const autoPrint = options?.autoPrintOnExit !== false;
|
|
238
|
+
if (autoPrint && typeof process !== 'undefined' && typeof process.on === 'function') {
|
|
239
|
+
const handler = async () => {
|
|
240
|
+
try {
|
|
241
|
+
await center.flushToDisk();
|
|
242
|
+
}
|
|
243
|
+
catch {
|
|
244
|
+
// ignore
|
|
245
|
+
}
|
|
246
|
+
try {
|
|
247
|
+
const snapshot = center.getSnapshot();
|
|
248
|
+
printStatsToConsole(snapshot);
|
|
249
|
+
}
|
|
250
|
+
catch {
|
|
251
|
+
// ignore
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
try {
|
|
255
|
+
process.once('beforeExit', handler);
|
|
256
|
+
}
|
|
257
|
+
catch {
|
|
258
|
+
// ignore
|
|
259
|
+
}
|
|
260
|
+
try {
|
|
261
|
+
process.once('SIGINT', handler);
|
|
262
|
+
}
|
|
263
|
+
catch {
|
|
264
|
+
// ignore
|
|
265
|
+
}
|
|
266
|
+
try {
|
|
267
|
+
process.once('SIGTERM', handler);
|
|
268
|
+
}
|
|
269
|
+
catch {
|
|
270
|
+
// ignore
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return instance;
|
|
274
|
+
}
|
|
275
|
+
export function getStatsCenter() {
|
|
276
|
+
if (!instance) {
|
|
277
|
+
return initStatsCenter();
|
|
278
|
+
}
|
|
279
|
+
return instance;
|
|
280
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsonstudio/rcc",
|
|
3
|
-
"version": "0.89.
|
|
3
|
+
"version": "0.89.524",
|
|
4
4
|
"description": "Multi-provider OpenAI proxy server with anthropic/responses/chat support (dev)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -126,7 +126,7 @@
|
|
|
126
126
|
},
|
|
127
127
|
"dependencies": {
|
|
128
128
|
"@anthropic-ai/sdk": "^0.65.0",
|
|
129
|
-
"@jsonstudio/llms": "^0.6.
|
|
129
|
+
"@jsonstudio/llms": "^0.6.375",
|
|
130
130
|
"@lmstudio/sdk": "^1.5.0",
|
|
131
131
|
"@radix-ui/react-switch": "^1.2.6",
|
|
132
132
|
"@types/socket.io": "^3.0.1",
|
package/scripts/pack-mode.mjs
CHANGED
|
@@ -59,11 +59,12 @@ try {
|
|
|
59
59
|
if (args.name === 'routecodex' || args.name === 'rcc') {
|
|
60
60
|
mutated.bundledDependencies = [];
|
|
61
61
|
mutated.bundleDependencies = [];
|
|
62
|
+
const llmsVersion = original.dependencies?.['@jsonstudio/llms'] || '^0.6.230';
|
|
62
63
|
mutated.dependencies = {
|
|
63
64
|
...(original.dependencies || {}),
|
|
64
65
|
"ajv": original.dependencies?.ajv || "^8.17.1",
|
|
65
66
|
"zod": original.dependencies?.zod || "^3.23.8",
|
|
66
|
-
"@jsonstudio/llms":
|
|
67
|
+
"@jsonstudio/llms": llmsVersion
|
|
67
68
|
};
|
|
68
69
|
}
|
|
69
70
|
fs.writeFileSync(pkgPath, JSON.stringify(mutated, null, 2));
|
package/scripts/publish-rcc.mjs
CHANGED
|
@@ -7,10 +7,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
7
7
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
8
8
|
const PROJECT_ROOT = path.resolve(__dirname, '..');
|
|
9
9
|
const PACK_SCRIPT = path.join(PROJECT_ROOT, 'scripts', 'pack-mode.mjs');
|
|
10
|
-
const
|
|
11
|
-
const version = packageJson.version;
|
|
12
|
-
const tarballName = `jsonstudio-rcc-${version}.tgz`;
|
|
13
|
-
const tarballPath = path.join(PROJECT_ROOT, tarballName);
|
|
10
|
+
const pkgPath = path.join(PROJECT_ROOT, 'package.json');
|
|
14
11
|
|
|
15
12
|
function run(command, args, options = {}) {
|
|
16
13
|
const res = spawnSync(command, args, { stdio: 'inherit', ...options });
|
|
@@ -20,11 +17,30 @@ function run(command, args, options = {}) {
|
|
|
20
17
|
}
|
|
21
18
|
|
|
22
19
|
try {
|
|
20
|
+
// 1) 使用 release 模式构建 dist(依赖 npm 上的 @jsonstudio/llms)
|
|
21
|
+
run('npm', ['run', 'build:min'], {
|
|
22
|
+
cwd: PROJECT_ROOT,
|
|
23
|
+
env: { ...process.env, BUILD_MODE: 'release' }
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// 2) 通过 pack-mode 生成 rcc tarball(内部会临时切换 package.json.name/bin 并确保 llms 为 release 包)
|
|
23
27
|
run(process.execPath, [PACK_SCRIPT, '--name', '@jsonstudio/rcc', '--bin', 'rcc'], { cwd: PROJECT_ROOT });
|
|
28
|
+
|
|
29
|
+
// 构建过程中版本号可能被 bump(gen-build-info 会 auto-bump),因此需要在 pack 之后重新读取版本号
|
|
30
|
+
const updatedPkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
31
|
+
const version = updatedPkg.version;
|
|
32
|
+
const tarballName = `jsonstudio-rcc-${version}.tgz`;
|
|
33
|
+
const tarballPath = path.join(PROJECT_ROOT, tarballName);
|
|
34
|
+
|
|
24
35
|
if (!fs.existsSync(tarballPath)) {
|
|
25
36
|
throw new Error(`tarball not found: ${tarballPath}`);
|
|
26
37
|
}
|
|
38
|
+
|
|
39
|
+
// 3) 发布 npm 包
|
|
27
40
|
run('npm', ['publish', tarballName], { cwd: PROJECT_ROOT });
|
|
41
|
+
|
|
42
|
+
// 4) pack-mode 会在内部检测 dev 链接并调用 ensure-llmswitch-mode 恢复 dev 模式,
|
|
43
|
+
// 因此此处不再额外修改 BUILD_MODE 或重新 link。后续本地如需 dev build,可单独运行 `npm run build:dev`。
|
|
28
44
|
} catch (err) {
|
|
29
45
|
console.error('[publish-rcc] failed:', err.message);
|
|
30
46
|
process.exit(1);
|
|
@@ -13,7 +13,7 @@ import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
|
13
13
|
|
|
14
14
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
15
15
|
const ROOT = path.resolve(__dirname, '../..');
|
|
16
|
-
const CORE_DIST = path.join(ROOT, 'sharedmodule', 'llmswitch-core', 'dist', '
|
|
16
|
+
const CORE_DIST = path.join(ROOT, 'sharedmodule', 'llmswitch-core', 'dist', 'router', 'virtual-router');
|
|
17
17
|
|
|
18
18
|
async function loadCoreModule(rel) {
|
|
19
19
|
const file = path.join(CORE_DIST, rel);
|
|
@@ -21,16 +21,19 @@ async function loadCoreModule(rel) {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
const { VirtualRouterEngine } = await loadCoreModule('engine.js');
|
|
24
|
+
const { bootstrapVirtualRouterConfig } = await loadCoreModule('bootstrap.js');
|
|
24
25
|
const { providerErrorCenter } = await loadCoreModule('error-center.js');
|
|
25
26
|
const { VirtualRouterError } = await loadCoreModule('types.js');
|
|
26
27
|
|
|
27
28
|
function parseArgs(argv) {
|
|
28
|
-
const args = { out: null };
|
|
29
|
+
const args = { out: null, config: null };
|
|
29
30
|
const list = [...argv];
|
|
30
31
|
while (list.length) {
|
|
31
32
|
const cur = list.shift();
|
|
32
33
|
if (cur === '--out' || cur === '--output') {
|
|
33
34
|
args.out = list.shift() || null;
|
|
35
|
+
} else if (cur === '--config') {
|
|
36
|
+
args.config = list.shift() || null;
|
|
34
37
|
} else if (cur === '--help' || cur === '-h') {
|
|
35
38
|
args.help = true;
|
|
36
39
|
}
|
|
@@ -57,9 +60,18 @@ function createRouterConfig() {
|
|
|
57
60
|
'charlie.sim-model': buildProviderProfile('charlie.sim-model', 'https://charlie.local/v1')
|
|
58
61
|
};
|
|
59
62
|
const routing = {
|
|
60
|
-
default:
|
|
61
|
-
|
|
62
|
-
|
|
63
|
+
default: [
|
|
64
|
+
{ id: 'default-primary', priority: 200, targets: ['alpha.sim-model', 'bravo.sim-model'] },
|
|
65
|
+
{ id: 'default-backup', backup: true, priority: 100, targets: ['charlie.sim-model'] }
|
|
66
|
+
],
|
|
67
|
+
coding: [
|
|
68
|
+
{ id: 'coding-primary', priority: 200, targets: ['bravo.sim-model', 'charlie.sim-model'] },
|
|
69
|
+
{ id: 'coding-backup', backup: true, priority: 100, targets: ['alpha.sim-model'] }
|
|
70
|
+
],
|
|
71
|
+
thinking: [
|
|
72
|
+
{ id: 'thinking-primary', priority: 200, targets: ['charlie.sim-model'] },
|
|
73
|
+
{ id: 'thinking-backup', backup: true, priority: 100, targets: ['bravo.sim-model'] }
|
|
74
|
+
]
|
|
63
75
|
};
|
|
64
76
|
return {
|
|
65
77
|
routing,
|
|
@@ -227,16 +239,128 @@ async function scenarioScheduler(sim) {
|
|
|
227
239
|
sim.runRoute('thinking');
|
|
228
240
|
}
|
|
229
241
|
|
|
242
|
+
async function scenarioPriorityPools(sim) {
|
|
243
|
+
const first = sim.runRoute('thinking');
|
|
244
|
+
if (first !== 'charlie.sim-model') {
|
|
245
|
+
throw new Error(`expected primary thinking pool hit charlie.sim-model, got ${first}`);
|
|
246
|
+
}
|
|
247
|
+
sim.engine.handleProviderFailure({
|
|
248
|
+
providerKey: 'charlie.sim-model',
|
|
249
|
+
reason: 'priority-test',
|
|
250
|
+
fatal: true,
|
|
251
|
+
affectsHealth: true,
|
|
252
|
+
cooldownOverrideMs: 60_000
|
|
253
|
+
});
|
|
254
|
+
const second = sim.runRoute('thinking');
|
|
255
|
+
if (second !== 'bravo.sim-model') {
|
|
256
|
+
throw new Error(`expected thinking backup pool hit bravo.sim-model, got ${second}`);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
230
260
|
async function scenarioRoutingDirectives(sim) {
|
|
231
261
|
sim.runRoute('baseline', '普通请求');
|
|
232
262
|
sim.runRoute('forced-thinking', '请仔细分析这个问题 <**thinking**>');
|
|
233
263
|
sim.runRoute('forced-provider', '请强制使用这个provider <**charlie.sim-model**> 来回答');
|
|
234
264
|
}
|
|
235
265
|
|
|
266
|
+
async function scenarioRealConfig(configPath) {
|
|
267
|
+
const resolvedPath = path.resolve(configPath);
|
|
268
|
+
console.log(`\n=== Scenario: real-config (${resolvedPath}) ===`);
|
|
269
|
+
const source = JSON.parse(await fs.readFile(resolvedPath, 'utf-8'));
|
|
270
|
+
const section = source.virtualrouter && typeof source.virtualrouter === 'object' ? source.virtualrouter : source;
|
|
271
|
+
const { config } = bootstrapVirtualRouterConfig(section);
|
|
272
|
+
const engine = new VirtualRouterEngine();
|
|
273
|
+
engine.initialize(config);
|
|
274
|
+
const thinkingPools = config.routing.thinking ?? [];
|
|
275
|
+
const primaryPools = thinkingPools.filter((tier) => !tier.backup);
|
|
276
|
+
const backupPools = thinkingPools.filter((tier) => tier.backup);
|
|
277
|
+
|
|
278
|
+
const runThinking = (label) => {
|
|
279
|
+
const reqId = `real-config-${label}-${Date.now()}`;
|
|
280
|
+
const request = {
|
|
281
|
+
model: 'gpt-5.2-codex',
|
|
282
|
+
messages: [
|
|
283
|
+
{ role: 'system', content: 'diagnostic' },
|
|
284
|
+
{ role: 'user', content: `深入思考:${label}` }
|
|
285
|
+
]
|
|
286
|
+
};
|
|
287
|
+
const metadata = {
|
|
288
|
+
requestId: reqId,
|
|
289
|
+
entryEndpoint: '/v1/responses',
|
|
290
|
+
processMode: 'chat',
|
|
291
|
+
stream: false,
|
|
292
|
+
direction: 'request',
|
|
293
|
+
providerProtocol: 'openai-responses'
|
|
294
|
+
};
|
|
295
|
+
const result = engine.route(request, metadata);
|
|
296
|
+
console.log(
|
|
297
|
+
JSON.stringify(
|
|
298
|
+
{
|
|
299
|
+
stage: 'real-config-route',
|
|
300
|
+
label,
|
|
301
|
+
providerKey: result.decision.providerKey,
|
|
302
|
+
route: result.decision.routeName,
|
|
303
|
+
poolId: result.decision.poolId
|
|
304
|
+
},
|
|
305
|
+
null,
|
|
306
|
+
2
|
|
307
|
+
)
|
|
308
|
+
);
|
|
309
|
+
return result.decision.providerKey;
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
const markUnavailable = (targets, reason) => {
|
|
313
|
+
for (const key of targets || []) {
|
|
314
|
+
engine.handleProviderFailure({
|
|
315
|
+
providerKey: key,
|
|
316
|
+
fatal: true,
|
|
317
|
+
affectsHealth: true,
|
|
318
|
+
reason,
|
|
319
|
+
cooldownOverrideMs: 60_000
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
// Primary hit
|
|
325
|
+
const firstProvider = runThinking('primary-hit');
|
|
326
|
+
const primaryTargets = primaryPools.flatMap((pool) => pool.targets ?? []);
|
|
327
|
+
const backupTargets = backupPools.flatMap((pool) => pool.targets ?? []);
|
|
328
|
+
if (primaryTargets.length === 0) {
|
|
329
|
+
console.warn('[real-config] No explicit primary thinking pool configured.');
|
|
330
|
+
} else if (!primaryTargets.includes(firstProvider)) {
|
|
331
|
+
console.warn('[real-config] First provider is not part of primary tier:', firstProvider);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Drain primary
|
|
335
|
+
markUnavailable(primaryTargets, 'primary-exhausted');
|
|
336
|
+
const secondProvider = runThinking('backup-hit');
|
|
337
|
+
if (backupTargets.length && !backupTargets.includes(secondProvider)) {
|
|
338
|
+
throw new Error(
|
|
339
|
+
`[real-config] Expected backup pool provider after draining primary, got ${secondProvider}`
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Drain backup -> should fall to default
|
|
344
|
+
markUnavailable(backupTargets, 'backup-exhausted');
|
|
345
|
+
const thirdProvider = runThinking('default-fallback');
|
|
346
|
+
const usedRoute = engine.getStatus().routes;
|
|
347
|
+
console.log(
|
|
348
|
+
JSON.stringify(
|
|
349
|
+
{
|
|
350
|
+
stage: 'real-config-summary',
|
|
351
|
+
thirdProvider,
|
|
352
|
+
routes: usedRoute
|
|
353
|
+
},
|
|
354
|
+
null,
|
|
355
|
+
2
|
|
356
|
+
)
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
|
|
236
360
|
async function main() {
|
|
237
361
|
const args = parseArgs(process.argv.slice(2));
|
|
238
362
|
if (args.help) {
|
|
239
|
-
console.log('Usage: node scripts/tests/virtual-router-health.mjs [--out summary.json]');
|
|
363
|
+
console.log('Usage: node scripts/tests/virtual-router-health.mjs [--out summary.json] [--config ~/.routecodex/config.json]');
|
|
240
364
|
return;
|
|
241
365
|
}
|
|
242
366
|
|
|
@@ -247,6 +371,7 @@ async function main() {
|
|
|
247
371
|
['upstream', scenarioUpstream],
|
|
248
372
|
['timeout', scenarioTimeout],
|
|
249
373
|
['scheduler', scenarioScheduler],
|
|
374
|
+
['priority-pools', scenarioPriorityPools],
|
|
250
375
|
['routing-directives', scenarioRoutingDirectives]
|
|
251
376
|
];
|
|
252
377
|
|
|
@@ -260,6 +385,16 @@ async function main() {
|
|
|
260
385
|
});
|
|
261
386
|
}
|
|
262
387
|
|
|
388
|
+
if (args.config) {
|
|
389
|
+
try {
|
|
390
|
+
await scenarioRealConfig(args.config);
|
|
391
|
+
summary.push({ name: 'real-config', status: 'ok', lastHealth: null });
|
|
392
|
+
} catch (error) {
|
|
393
|
+
console.error('[real-config] failed:', error);
|
|
394
|
+
summary.push({ name: 'real-config', status: 'failed', error: error?.message || String(error), lastHealth: null });
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
263
398
|
if (args.out) {
|
|
264
399
|
const outFile = path.resolve(process.cwd(), args.out);
|
|
265
400
|
await fs.mkdir(path.dirname(outFile), { recursive: true });
|
|
File without changes
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"replay-request.js","sourceRoot":"","sources":["../../src/tools/replay-request.ts"],"names":[],"mappings":""}
|