@tonyclaw/agent-inspector 2.0.11 → 2.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.output/nitro.json +1 -1
- package/.output/public/assets/{CompareDrawer-C9OWdxHM.js → CompareDrawer-CrMyE23D.js} +1 -1
- package/.output/public/assets/ProxyViewerContainer-BZuo0px3.js +114 -0
- package/.output/public/assets/{ReplayDialog-CwE6I1Pe.js → ReplayDialog-TQGhDdTa.js} +1 -1
- package/.output/public/assets/RequestAnatomy-D6kQYtfa.js +1 -0
- package/.output/public/assets/ResponseView-C6ybWuB8.js +1 -0
- package/.output/public/assets/{StreamingChunkSequence-CLcLZ7-W.js → StreamingChunkSequence-Bvs59_-I.js} +1 -1
- package/.output/public/assets/_sessionId-BNG_mnaH.js +1 -0
- package/.output/public/assets/index-BUxIwHhC.js +1 -0
- package/.output/public/assets/index-D_nZj9Vt.css +1 -0
- package/.output/public/assets/{main-D1Xanzu7.js → main-DvFSJlOd.js} +2 -2
- package/.output/server/_libs/lucide-react.mjs +51 -39
- package/.output/server/{_sessionId-D3IyPpws.mjs → _sessionId-DHYJxPb7.mjs} +2 -2
- package/.output/server/_ssr/{CompareDrawer-BIMThqxy.mjs → CompareDrawer-meimDBeA.mjs} +3 -3
- package/.output/server/_ssr/{ProxyViewerContainer-BiBdKWtj.mjs → ProxyViewerContainer-Co-WKq6u.mjs} +1610 -183
- package/.output/server/_ssr/{ReplayDialog-D_wiDyM2.mjs → ReplayDialog-cclcGyR9.mjs} +4 -4
- package/.output/server/_ssr/RequestAnatomy-to_rezzu.mjs +882 -0
- package/.output/server/_ssr/{ResponseView-Drk5ghmL.mjs → ResponseView-CZZI4IZd.mjs} +3 -3
- package/.output/server/_ssr/{StreamingChunkSequence-F889rmG0.mjs → StreamingChunkSequence-BTF4xSYX.mjs} +2 -2
- package/.output/server/_ssr/{index-CaPniq9z.mjs → index-D2fpJFP5.mjs} +2 -2
- package/.output/server/_ssr/index.mjs +2 -2
- package/.output/server/_ssr/{router-C7S8FRth.mjs → router-RDBAF-Iu.mjs} +85 -22
- package/.output/server/{_tanstack-start-manifest_v-CVTkFOdT.mjs → _tanstack-start-manifest_v-DB9sseoH.mjs} +1 -1
- package/.output/server/index.mjs +58 -58
- package/package.json +1 -1
- package/src/components/ProxyViewer.tsx +454 -52
- package/src/components/providers/ProviderCard.tsx +45 -2
- package/src/components/providers/ProviderForm.tsx +245 -80
- package/src/components/providers/ProvidersPanel.tsx +7 -1
- package/src/components/proxy-viewer/ConversationGroup.tsx +18 -1
- package/src/components/proxy-viewer/LogEntry.tsx +25 -0
- package/src/components/proxy-viewer/TurnGroup.tsx +15 -0
- package/src/components/proxy-viewer/anatomy/RequestAnatomy.tsx +366 -99
- package/src/components/proxy-viewer/anatomy/contextIntelligence.ts +22 -4
- package/src/components/proxy-viewer/anatomy/sessionContextSummary.ts +196 -0
- package/src/components/proxy-viewer/logFocus.ts +33 -0
- package/src/lib/providerModelMetadata.ts +64 -12
- package/src/lib/utils.ts +7 -0
- package/src/proxy/providers.ts +25 -0
- package/src/routes/api/providers.ts +3 -0
- package/.output/public/assets/ProxyViewerContainer-CNzay4u8.js +0 -114
- package/.output/public/assets/RequestAnatomy-DEehC3yz.js +0 -1
- package/.output/public/assets/ResponseView-CE5UsuUq.js +0 -1
- package/.output/public/assets/_sessionId-DE__tPTo.js +0 -1
- package/.output/public/assets/index-BFO4Jmw5.js +0 -1
- package/.output/public/assets/index-CDzZ7l_S.css +0 -1
- package/.output/server/_ssr/RequestAnatomy-5UiMTESr.mjs +0 -1383
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import type { ProviderConfig } from "../../../lib/providerContract";
|
|
2
|
+
import type { CapturedLog } from "../../../proxy/schemas";
|
|
3
|
+
import { getLogFormatAdapter, resolveLogFormat } from "../log-formats";
|
|
4
|
+
import {
|
|
5
|
+
analyzeContextIntelligence,
|
|
6
|
+
type ContextHealth,
|
|
7
|
+
type ContextRiskLevel,
|
|
8
|
+
type ContextWindow,
|
|
9
|
+
type RoleUsage,
|
|
10
|
+
} from "./contextIntelligence";
|
|
11
|
+
import type { AnatomySegment } from "./types";
|
|
12
|
+
|
|
13
|
+
export type SessionContextSnapshot = {
|
|
14
|
+
logId: number;
|
|
15
|
+
model: string;
|
|
16
|
+
timestamp: string;
|
|
17
|
+
contextWindow: ContextWindow;
|
|
18
|
+
estimatedInputTokens: number;
|
|
19
|
+
windowUsedTokens: number | null;
|
|
20
|
+
usagePercent: number | null;
|
|
21
|
+
riskLevel: ContextRiskLevel;
|
|
22
|
+
health: ContextHealth;
|
|
23
|
+
diagnostics: string[];
|
|
24
|
+
roleSegments: AnatomySegment[];
|
|
25
|
+
roleTotalTokens: number;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export type SessionModelContextSummary = {
|
|
29
|
+
model: string;
|
|
30
|
+
requestCount: number;
|
|
31
|
+
first: SessionContextSnapshot;
|
|
32
|
+
latest: SessionContextSnapshot;
|
|
33
|
+
peak: SessionContextSnapshot;
|
|
34
|
+
trendPercent: number | null;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export type SessionContextSummary = {
|
|
38
|
+
models: SessionModelContextSummary[];
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
type SummaryAccumulator = {
|
|
42
|
+
model: string;
|
|
43
|
+
requestCount: number;
|
|
44
|
+
first: SessionContextSnapshot;
|
|
45
|
+
latest: SessionContextSnapshot;
|
|
46
|
+
peak: SessionContextSnapshot;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
function parseRequestBody(rawBody: string | null): unknown | null {
|
|
50
|
+
if (rawBody === null) return null;
|
|
51
|
+
try {
|
|
52
|
+
const parsed: unknown = JSON.parse(rawBody);
|
|
53
|
+
return parsed;
|
|
54
|
+
} catch {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function roleUsageSegment(usage: RoleUsage): AnatomySegment {
|
|
60
|
+
return {
|
|
61
|
+
role: usage.role,
|
|
62
|
+
label: usage.label,
|
|
63
|
+
size: usage.tokens,
|
|
64
|
+
characters: usage.tokens * 4,
|
|
65
|
+
text: "",
|
|
66
|
+
path: `role:${usage.role}`,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function snapshotFromLog(
|
|
71
|
+
log: CapturedLog,
|
|
72
|
+
providers: readonly ProviderConfig[],
|
|
73
|
+
): SessionContextSnapshot | null {
|
|
74
|
+
const parsed = parseRequestBody(log.rawRequestBody);
|
|
75
|
+
if (parsed === null) return null;
|
|
76
|
+
|
|
77
|
+
const adapter = getLogFormatAdapter(resolveLogFormat(log));
|
|
78
|
+
const segments = adapter.anatomySegments(parsed);
|
|
79
|
+
if (segments === null) return null;
|
|
80
|
+
|
|
81
|
+
const intelligence = analyzeContextIntelligence({
|
|
82
|
+
segments,
|
|
83
|
+
inputTokens: log.inputTokens ?? null,
|
|
84
|
+
parsed,
|
|
85
|
+
model: log.model,
|
|
86
|
+
providers,
|
|
87
|
+
});
|
|
88
|
+
const model = intelligence.model ?? log.model ?? "Unknown model";
|
|
89
|
+
const roleSegments = intelligence.roleUsages.map(roleUsageSegment);
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
logId: log.id,
|
|
93
|
+
model,
|
|
94
|
+
timestamp: log.timestamp,
|
|
95
|
+
contextWindow: intelligence.contextWindow,
|
|
96
|
+
estimatedInputTokens: intelligence.estimatedInputTokens,
|
|
97
|
+
windowUsedTokens: intelligence.windowUsedTokens,
|
|
98
|
+
usagePercent: intelligence.usagePercent,
|
|
99
|
+
riskLevel: intelligence.riskLevel,
|
|
100
|
+
health: intelligence.health,
|
|
101
|
+
diagnostics: intelligence.diagnostics.map((diagnostic) => diagnostic.title),
|
|
102
|
+
roleSegments,
|
|
103
|
+
roleTotalTokens: roleSegments.reduce((sum, segment) => sum + segment.size, 0),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function isHigherPeak(candidate: SessionContextSnapshot, current: SessionContextSnapshot): boolean {
|
|
108
|
+
if (candidate.usagePercent !== null && current.usagePercent !== null) {
|
|
109
|
+
return candidate.usagePercent > current.usagePercent;
|
|
110
|
+
}
|
|
111
|
+
if (candidate.usagePercent !== null && current.usagePercent === null) return true;
|
|
112
|
+
if (candidate.usagePercent === null && current.usagePercent !== null) return false;
|
|
113
|
+
return candidate.estimatedInputTokens > current.estimatedInputTokens;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function riskRank(risk: ContextRiskLevel): number {
|
|
117
|
+
switch (risk) {
|
|
118
|
+
case "danger":
|
|
119
|
+
return 4;
|
|
120
|
+
case "watch":
|
|
121
|
+
return 3;
|
|
122
|
+
case "unknown":
|
|
123
|
+
return 2;
|
|
124
|
+
case "ok":
|
|
125
|
+
return 1;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function compareModelSummary(
|
|
130
|
+
left: SessionModelContextSummary,
|
|
131
|
+
right: SessionModelContextSummary,
|
|
132
|
+
): number {
|
|
133
|
+
const riskDelta = riskRank(right.latest.riskLevel) - riskRank(left.latest.riskLevel);
|
|
134
|
+
if (riskDelta !== 0) return riskDelta;
|
|
135
|
+
|
|
136
|
+
const leftPeak = left.peak.usagePercent ?? -1;
|
|
137
|
+
const rightPeak = right.peak.usagePercent ?? -1;
|
|
138
|
+
const peakDelta = rightPeak - leftPeak;
|
|
139
|
+
if (peakDelta !== 0) return peakDelta;
|
|
140
|
+
|
|
141
|
+
return right.latest.logId - left.latest.logId;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function trendPercent(
|
|
145
|
+
first: SessionContextSnapshot,
|
|
146
|
+
latest: SessionContextSnapshot,
|
|
147
|
+
): number | null {
|
|
148
|
+
if (first.usagePercent === null || latest.usagePercent === null) return null;
|
|
149
|
+
return (latest.usagePercent - first.usagePercent) * 100;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function buildSessionContextSummary(
|
|
153
|
+
logs: readonly CapturedLog[],
|
|
154
|
+
providers: readonly ProviderConfig[] = [],
|
|
155
|
+
): SessionContextSummary {
|
|
156
|
+
const sortedLogs = [...logs].sort((left, right) => left.id - right.id);
|
|
157
|
+
const byModel = new Map<string, SummaryAccumulator>();
|
|
158
|
+
|
|
159
|
+
for (const log of sortedLogs) {
|
|
160
|
+
const snapshot = snapshotFromLog(log, providers);
|
|
161
|
+
if (snapshot === null) continue;
|
|
162
|
+
|
|
163
|
+
const existing = byModel.get(snapshot.model);
|
|
164
|
+
if (existing === undefined) {
|
|
165
|
+
byModel.set(snapshot.model, {
|
|
166
|
+
model: snapshot.model,
|
|
167
|
+
requestCount: 1,
|
|
168
|
+
first: snapshot,
|
|
169
|
+
latest: snapshot,
|
|
170
|
+
peak: snapshot,
|
|
171
|
+
});
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
byModel.set(snapshot.model, {
|
|
176
|
+
model: existing.model,
|
|
177
|
+
requestCount: existing.requestCount + 1,
|
|
178
|
+
first: existing.first,
|
|
179
|
+
latest: snapshot,
|
|
180
|
+
peak: isHigherPeak(snapshot, existing.peak) ? snapshot : existing.peak,
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const models = [...byModel.values()]
|
|
185
|
+
.map((entry) => ({
|
|
186
|
+
model: entry.model,
|
|
187
|
+
requestCount: entry.requestCount,
|
|
188
|
+
first: entry.first,
|
|
189
|
+
latest: entry.latest,
|
|
190
|
+
peak: entry.peak,
|
|
191
|
+
trendPercent: trendPercent(entry.first, entry.latest),
|
|
192
|
+
}))
|
|
193
|
+
.sort(compareModelSummary);
|
|
194
|
+
|
|
195
|
+
return { models };
|
|
196
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { safeGetOwnProperty } from "../../lib/objectUtils";
|
|
2
|
+
|
|
3
|
+
export const LOG_FOCUS_REQUEST_EVENT = "agent-inspector:focus-log";
|
|
4
|
+
|
|
5
|
+
export type LogFocusTab = "anatomy" | "request";
|
|
6
|
+
|
|
7
|
+
export type LogFocusRequest = {
|
|
8
|
+
logId: number;
|
|
9
|
+
tab: LogFocusTab;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export function dispatchLogFocusRequest(request: LogFocusRequest): void {
|
|
13
|
+
window.dispatchEvent(new CustomEvent(LOG_FOCUS_REQUEST_EVENT, { detail: request }));
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function readLogFocusRequest(event: Event): LogFocusRequest | null {
|
|
17
|
+
if (typeof CustomEvent === "undefined" || !(event instanceof CustomEvent)) return null;
|
|
18
|
+
|
|
19
|
+
const detail: unknown = event.detail;
|
|
20
|
+
const logId = safeGetOwnProperty(detail, "logId");
|
|
21
|
+
if (typeof logId !== "number" || !Number.isInteger(logId) || logId < 0) return null;
|
|
22
|
+
|
|
23
|
+
const tab = safeGetOwnProperty(detail, "tab");
|
|
24
|
+
switch (tab) {
|
|
25
|
+
case "anatomy":
|
|
26
|
+
case "request":
|
|
27
|
+
return { logId, tab };
|
|
28
|
+
case undefined:
|
|
29
|
+
return { logId, tab: "request" };
|
|
30
|
+
default:
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -41,10 +41,19 @@ export type ProviderModelRegistryApplyResult = {
|
|
|
41
41
|
};
|
|
42
42
|
|
|
43
43
|
export const BUILTIN_MODEL_METADATA_SOURCE_URL = "agent-inspector://builtin/model-metadata";
|
|
44
|
+
export const HF_DEEPSEEK_V4_PRO_URL = "https://huggingface.co/deepseek-ai/DeepSeek-V4-Pro";
|
|
45
|
+
export const HF_DEEPSEEK_V4_FLASH_URL = "https://huggingface.co/deepseek-ai/DeepSeek-V4-Flash";
|
|
46
|
+
export const HF_MINIMAX_M3_URL = "https://huggingface.co/MiniMaxAI/MiniMax-M3";
|
|
47
|
+
export const HF_MINIMAX_M2_7_URL = "https://huggingface.co/MiniMaxAI/MiniMax-M2.7";
|
|
48
|
+
export const HF_MINIMAX_M2_5_URL = "https://huggingface.co/MiniMaxAI/MiniMax-M2.5";
|
|
49
|
+
export const HF_MINIMAX_M2_1_URL = "https://huggingface.co/MiniMaxAI/MiniMax-M2.1";
|
|
50
|
+
export const HF_GLM_5_URL = "https://huggingface.co/zai-org/GLM-5";
|
|
51
|
+
export const HF_GLM_5_1_URL = "https://huggingface.co/zai-org/GLM-5.1";
|
|
52
|
+
export const HF_GLM_5_2_URL = "https://huggingface.co/zai-org/GLM-5.2";
|
|
44
53
|
|
|
45
54
|
export const BUILTIN_PROVIDER_MODEL_REGISTRY: ProviderModelRegistry = {
|
|
46
55
|
version: 1,
|
|
47
|
-
updatedAt: "2026-06-
|
|
56
|
+
updatedAt: "2026-06-24T00:00:00.000Z",
|
|
48
57
|
providers: [
|
|
49
58
|
{
|
|
50
59
|
name: "DeepSeek",
|
|
@@ -54,12 +63,14 @@ export const BUILTIN_PROVIDER_MODEL_REGISTRY: ProviderModelRegistry = {
|
|
|
54
63
|
{
|
|
55
64
|
id: "deepseek-v4-pro",
|
|
56
65
|
aliases: ["DeepSeek-V4-Pro"],
|
|
57
|
-
contextWindow:
|
|
66
|
+
contextWindow: 1_048_576,
|
|
67
|
+
sourceUrl: HF_DEEPSEEK_V4_PRO_URL,
|
|
58
68
|
},
|
|
59
69
|
{
|
|
60
70
|
id: "deepseek-v4-flash",
|
|
61
71
|
aliases: ["DeepSeek-V4-Flash"],
|
|
62
|
-
contextWindow:
|
|
72
|
+
contextWindow: 1_048_576,
|
|
73
|
+
sourceUrl: HF_DEEPSEEK_V4_FLASH_URL,
|
|
63
74
|
},
|
|
64
75
|
],
|
|
65
76
|
},
|
|
@@ -71,37 +82,69 @@ export const BUILTIN_PROVIDER_MODEL_REGISTRY: ProviderModelRegistry = {
|
|
|
71
82
|
{
|
|
72
83
|
id: "MiniMax M3",
|
|
73
84
|
aliases: ["MiniMax-M3", "minimax-m3"],
|
|
74
|
-
contextWindow:
|
|
85
|
+
contextWindow: 1_048_576,
|
|
86
|
+
sourceUrl: HF_MINIMAX_M3_URL,
|
|
75
87
|
},
|
|
76
88
|
{
|
|
77
89
|
id: "MiniMax M2.7",
|
|
78
90
|
aliases: ["MiniMax-M2.7", "minimax-m2.7"],
|
|
79
91
|
contextWindow: 204_800,
|
|
92
|
+
sourceUrl: HF_MINIMAX_M2_7_URL,
|
|
80
93
|
},
|
|
81
94
|
{
|
|
82
95
|
id: "MiniMax M2.7-highspeed",
|
|
83
96
|
aliases: ["MiniMax-M2.7-highspeed", "minimax-m2.7-highspeed"],
|
|
84
97
|
contextWindow: 204_800,
|
|
98
|
+
sourceUrl: HF_MINIMAX_M2_7_URL,
|
|
85
99
|
},
|
|
86
100
|
{
|
|
87
101
|
id: "MiniMax M2.5",
|
|
88
102
|
aliases: ["MiniMax-M2.5", "minimax-m2.5"],
|
|
89
|
-
contextWindow:
|
|
103
|
+
contextWindow: 196_608,
|
|
104
|
+
sourceUrl: HF_MINIMAX_M2_5_URL,
|
|
90
105
|
},
|
|
91
106
|
{
|
|
92
107
|
id: "MiniMax M2.5-highspeed",
|
|
93
108
|
aliases: ["MiniMax-M2.5-highspeed", "minimax-m2.5-highspeed"],
|
|
94
|
-
contextWindow:
|
|
109
|
+
contextWindow: 196_608,
|
|
110
|
+
sourceUrl: HF_MINIMAX_M2_5_URL,
|
|
95
111
|
},
|
|
96
112
|
{
|
|
97
113
|
id: "MiniMax M2.1",
|
|
98
114
|
aliases: ["MiniMax-M2.1", "minimax-m2.1"],
|
|
99
|
-
contextWindow:
|
|
115
|
+
contextWindow: 196_608,
|
|
116
|
+
sourceUrl: HF_MINIMAX_M2_1_URL,
|
|
100
117
|
},
|
|
101
118
|
{
|
|
102
119
|
id: "MiniMax M2.1-highspeed",
|
|
103
120
|
aliases: ["MiniMax-M2.1-highspeed", "minimax-m2.1-highspeed"],
|
|
104
|
-
contextWindow:
|
|
121
|
+
contextWindow: 196_608,
|
|
122
|
+
sourceUrl: HF_MINIMAX_M2_1_URL,
|
|
123
|
+
},
|
|
124
|
+
],
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
name: "GLM",
|
|
128
|
+
aliases: ["glm", "zai", "zhipu", "bigmodel"],
|
|
129
|
+
baseUrls: [],
|
|
130
|
+
models: [
|
|
131
|
+
{
|
|
132
|
+
id: "glm-5",
|
|
133
|
+
aliases: ["GLM-5"],
|
|
134
|
+
contextWindow: 202_752,
|
|
135
|
+
sourceUrl: HF_GLM_5_URL,
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
id: "glm-5.1",
|
|
139
|
+
aliases: ["GLM-5.1"],
|
|
140
|
+
contextWindow: 202_752,
|
|
141
|
+
sourceUrl: HF_GLM_5_1_URL,
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
id: "glm-5.2",
|
|
145
|
+
aliases: ["GLM-5.2"],
|
|
146
|
+
contextWindow: 1_048_576,
|
|
147
|
+
sourceUrl: HF_GLM_5_2_URL,
|
|
105
148
|
},
|
|
106
149
|
],
|
|
107
150
|
},
|
|
@@ -271,8 +314,10 @@ export function applyProviderModelRegistry(
|
|
|
271
314
|
|
|
272
315
|
const normalized = normalizeModelName(model);
|
|
273
316
|
const existingMetadata = existing.get(normalized);
|
|
317
|
+
const existingIsManual = existingMetadata?.source === "manual";
|
|
274
318
|
if (
|
|
275
319
|
mode === "fill-missing" &&
|
|
320
|
+
existingIsManual &&
|
|
276
321
|
existingMetadata !== undefined &&
|
|
277
322
|
existingMetadata.contextWindow !== undefined &&
|
|
278
323
|
(existingMetadata.outputLimit !== undefined || registryModel.outputLimit === undefined)
|
|
@@ -280,12 +325,19 @@ export function applyProviderModelRegistry(
|
|
|
280
325
|
continue;
|
|
281
326
|
}
|
|
282
327
|
|
|
328
|
+
const keepManualValue = mode === "fill-missing" && existingIsManual;
|
|
283
329
|
existing.set(normalized, {
|
|
284
330
|
model,
|
|
285
|
-
contextWindow:
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
331
|
+
contextWindow: keepManualValue
|
|
332
|
+
? (existingMetadata?.contextWindow ?? registryModel.contextWindow)
|
|
333
|
+
: (registryModel.contextWindow ?? existingMetadata?.contextWindow),
|
|
334
|
+
outputLimit: keepManualValue
|
|
335
|
+
? (existingMetadata?.outputLimit ?? registryModel.outputLimit)
|
|
336
|
+
: (registryModel.outputLimit ?? existingMetadata?.outputLimit),
|
|
337
|
+
source: keepManualValue ? "manual" : "registry",
|
|
338
|
+
sourceUrl: keepManualValue
|
|
339
|
+
? (existingMetadata?.sourceUrl ?? registryModel.sourceUrl ?? sourceUrl)
|
|
340
|
+
: (registryModel.sourceUrl ?? sourceUrl),
|
|
289
341
|
updatedAt,
|
|
290
342
|
});
|
|
291
343
|
matchedModels.push(model);
|
package/src/lib/utils.ts
CHANGED
|
@@ -10,6 +10,13 @@ export function formatTokens(count: number): string {
|
|
|
10
10
|
return (count / 1000).toFixed(1).replace(/\.0$/, "") + "K";
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
export function formatContextWindowTokens(count: number): string {
|
|
14
|
+
if (count >= 1024 * 1024) {
|
|
15
|
+
return (count / (1024 * 1024)).toFixed(1).replace(/\.0$/, "") + "M";
|
|
16
|
+
}
|
|
17
|
+
return (count / 1024).toFixed(1).replace(/\.0$/, "") + "K";
|
|
18
|
+
}
|
|
19
|
+
|
|
13
20
|
export type StatusCategory = "success" | "client_error" | "server_error" | "pending";
|
|
14
21
|
|
|
15
22
|
export function getStatusCategory(status: number | null): StatusCategory {
|
package/src/proxy/providers.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
ProviderConfigSchema,
|
|
10
10
|
ProviderModelMetadataSchema,
|
|
11
11
|
type ProviderConfig,
|
|
12
|
+
type ProviderModelMetadata,
|
|
12
13
|
} from "../lib/providerContract";
|
|
13
14
|
import { withBuiltinProviderModelMetadata } from "../lib/providerModelMetadata";
|
|
14
15
|
|
|
@@ -21,6 +22,27 @@ const ProvidersStoreSchema = z.object({
|
|
|
21
22
|
|
|
22
23
|
type ProvidersStore = z.infer<typeof ProvidersStoreSchema>;
|
|
23
24
|
|
|
25
|
+
function normalizeMetadataModelName(value: string): string {
|
|
26
|
+
return value.trim().toLowerCase().replace(/\s+/g, "-");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function mergeModelMetadata(
|
|
30
|
+
existing: readonly ProviderModelMetadata[] | undefined,
|
|
31
|
+
incoming: readonly ProviderModelMetadata[] | undefined,
|
|
32
|
+
): ProviderModelMetadata[] | undefined {
|
|
33
|
+
if (existing === undefined && incoming === undefined) return undefined;
|
|
34
|
+
|
|
35
|
+
const merged = new Map<string, ProviderModelMetadata>();
|
|
36
|
+
for (const metadata of existing ?? []) {
|
|
37
|
+
merged.set(normalizeMetadataModelName(metadata.model), metadata);
|
|
38
|
+
}
|
|
39
|
+
for (const metadata of incoming ?? []) {
|
|
40
|
+
merged.set(normalizeMetadataModelName(metadata.model), metadata);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return [...merged.values()].toSorted((left, right) => left.model.localeCompare(right.model));
|
|
44
|
+
}
|
|
45
|
+
|
|
24
46
|
const LegacyProviderConfigSchema = z.object({
|
|
25
47
|
id: z.string().optional(),
|
|
26
48
|
name: z.string().optional(),
|
|
@@ -377,6 +399,7 @@ export function addProvider(
|
|
|
377
399
|
openaiBaseUrl?: string,
|
|
378
400
|
source?: "company" | "personal",
|
|
379
401
|
modelMetadataUrl?: string,
|
|
402
|
+
modelMetadata?: ProviderModelMetadata[],
|
|
380
403
|
): ProviderConfig {
|
|
381
404
|
const providers = getProviders();
|
|
382
405
|
const normalizedKey = normalizeApiKey(apiKey);
|
|
@@ -398,6 +421,7 @@ export function addProvider(
|
|
|
398
421
|
const existingIndex = providers.findIndex((p) => p.id === existing.id);
|
|
399
422
|
const newModels = (models ?? []).filter((m) => m !== "");
|
|
400
423
|
existing.modelMetadataUrl = modelMetadataUrl ?? existing.modelMetadataUrl;
|
|
424
|
+
existing.modelMetadata = mergeModelMetadata(existing.modelMetadata, modelMetadata);
|
|
401
425
|
if (newModels.length > 0) {
|
|
402
426
|
const mergedModels = new Set(existing.models ?? []);
|
|
403
427
|
for (const m of newModels) mergedModels.add(m);
|
|
@@ -429,6 +453,7 @@ export function addProvider(
|
|
|
429
453
|
authHeader: authHeader ?? "bearer",
|
|
430
454
|
apiDocsUrl,
|
|
431
455
|
modelMetadataUrl,
|
|
456
|
+
modelMetadata,
|
|
432
457
|
createdAt: now,
|
|
433
458
|
updatedAt: now,
|
|
434
459
|
anthropicBaseUrl: resolvedAnthropicUrl,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createFileRoute } from "@tanstack/react-router";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { getProviders, addProvider } from "../../proxy/providers";
|
|
4
|
+
import { ProviderModelMetadataSchema } from "../../lib/providerContract";
|
|
4
5
|
|
|
5
6
|
const ProviderInputSchema = z.object({
|
|
6
7
|
name: z.string().min(1, "Name is required"),
|
|
@@ -13,6 +14,7 @@ const ProviderInputSchema = z.object({
|
|
|
13
14
|
authHeader: z.enum(["bearer", "x-api-key"]).optional().default("bearer"),
|
|
14
15
|
apiDocsUrl: z.string().optional(),
|
|
15
16
|
modelMetadataUrl: z.string().optional(),
|
|
17
|
+
modelMetadata: z.array(ProviderModelMetadataSchema).optional(),
|
|
16
18
|
source: z.enum(["company", "personal"]).optional(),
|
|
17
19
|
});
|
|
18
20
|
|
|
@@ -39,6 +41,7 @@ export const Route = createFileRoute("/api/providers")({
|
|
|
39
41
|
parsed.data.openaiBaseUrl,
|
|
40
42
|
parsed.data.source,
|
|
41
43
|
parsed.data.modelMetadataUrl,
|
|
44
|
+
parsed.data.modelMetadata,
|
|
42
45
|
);
|
|
43
46
|
return Response.json(newProvider, { status: 201 });
|
|
44
47
|
},
|