@tonyclaw/agent-inspector 2.0.11 → 2.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.output/nitro.json +1 -1
- package/.output/public/assets/{CompareDrawer-C9OWdxHM.js → CompareDrawer-ClM_uVRy.js} +1 -1
- package/.output/public/assets/ProxyViewerContainer-CTNNzXSa.js +114 -0
- package/.output/public/assets/{ReplayDialog-CwE6I1Pe.js → ReplayDialog-7rp7N_KJ.js} +1 -1
- package/.output/public/assets/RequestAnatomy-sL2FFo_N.js +1 -0
- package/.output/public/assets/ResponseView-Cjlf3b6-.js +1 -0
- package/.output/public/assets/{StreamingChunkSequence-CLcLZ7-W.js → StreamingChunkSequence-Ctx3sT3r.js} +1 -1
- package/.output/public/assets/_sessionId-DEWzDQ8-.js +1 -0
- package/.output/public/assets/index-DGIIhZTn.js +1 -0
- package/.output/public/assets/index-D_nZj9Vt.css +1 -0
- package/.output/public/assets/{main-D1Xanzu7.js → main-FvIQopAy.js} +2 -2
- package/.output/server/_libs/lucide-react.mjs +51 -39
- package/.output/server/{_sessionId-D3IyPpws.mjs → _sessionId-BDSgtrsI.mjs} +2 -2
- package/.output/server/_ssr/{CompareDrawer-BIMThqxy.mjs → CompareDrawer-Cqvv0I3x.mjs} +3 -3
- package/.output/server/_ssr/{ProxyViewerContainer-BiBdKWtj.mjs → ProxyViewerContainer-C0YEoG8N.mjs} +1611 -183
- package/.output/server/_ssr/{ReplayDialog-D_wiDyM2.mjs → ReplayDialog-CrsynUKI.mjs} +4 -4
- package/.output/server/_ssr/RequestAnatomy-CR_JUkcL.mjs +688 -0
- package/.output/server/_ssr/{ResponseView-Drk5ghmL.mjs → ResponseView-Dba2Et69.mjs} +3 -3
- package/.output/server/_ssr/{StreamingChunkSequence-F889rmG0.mjs → StreamingChunkSequence-BIMHLstI.mjs} +2 -2
- package/.output/server/_ssr/{index-CaPniq9z.mjs → index-DveIUTm5.mjs} +2 -2
- package/.output/server/_ssr/index.mjs +2 -2
- package/.output/server/_ssr/{router-C7S8FRth.mjs → router-D0yXVG0Q.mjs} +85 -22
- package/.output/server/{_tanstack-start-manifest_v-CVTkFOdT.mjs → _tanstack-start-manifest_v-pe0xJFuQ.mjs} +1 -1
- package/.output/server/index.mjs +62 -62
- package/package.json +1 -1
- package/src/components/ProxyViewer.tsx +453 -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 +99 -79
- 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
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Info } from "lucide-react";
|
|
1
|
+
import { ChevronDown, ChevronRight, Info } from "lucide-react";
|
|
2
2
|
import { type JSX, useMemo, useState } from "react";
|
|
3
|
-
import { cn, formatTokens } from "../../../lib/utils";
|
|
3
|
+
import { cn, formatContextWindowTokens, formatTokens } from "../../../lib/utils";
|
|
4
4
|
import { useProviders } from "../../../lib/useProviders";
|
|
5
5
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "../../ui/tooltip";
|
|
6
6
|
import {
|
|
@@ -97,7 +97,7 @@ function formatRemainingTokens(value: number | null): string {
|
|
|
97
97
|
|
|
98
98
|
function formatContextLimit(intelligence: ContextIntelligence): string {
|
|
99
99
|
if (intelligence.contextWindow.tokens === null) return "Unknown";
|
|
100
|
-
return
|
|
100
|
+
return formatContextWindowTokens(intelligence.contextWindow.tokens);
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
function formatContextSource(intelligence: ContextIntelligence): string {
|
|
@@ -284,6 +284,8 @@ function DuplicateContentPanel({
|
|
|
284
284
|
segments: readonly AnatomySegment[];
|
|
285
285
|
onSegmentActivate?: (segment: AnatomySegment) => void;
|
|
286
286
|
}): JSX.Element | null {
|
|
287
|
+
const [expanded, setExpanded] = useState(false);
|
|
288
|
+
|
|
287
289
|
if (groups.length === 0) return null;
|
|
288
290
|
|
|
289
291
|
const visibleGroups = groups.slice(0, DUPLICATE_GROUP_DISPLAY_COUNT);
|
|
@@ -292,87 +294,105 @@ function DuplicateContentPanel({
|
|
|
292
294
|
|
|
293
295
|
return (
|
|
294
296
|
<section className="space-y-2 rounded border border-border/70 bg-muted/15 p-2">
|
|
295
|
-
<
|
|
296
|
-
|
|
297
|
-
|
|
297
|
+
<button
|
|
298
|
+
type="button"
|
|
299
|
+
onClick={() => setExpanded((current) => !current)}
|
|
300
|
+
aria-expanded={expanded}
|
|
301
|
+
className="flex w-full flex-wrap items-center justify-between gap-2 rounded px-1 py-0.5 text-left transition-colors hover:bg-muted/30 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1"
|
|
302
|
+
>
|
|
303
|
+
<span className="inline-flex min-w-0 items-center gap-1.5">
|
|
304
|
+
{expanded ? (
|
|
305
|
+
<ChevronDown className="size-3.5 shrink-0 text-muted-foreground" />
|
|
306
|
+
) : (
|
|
307
|
+
<ChevronRight className="size-3.5 shrink-0 text-muted-foreground" />
|
|
308
|
+
)}
|
|
309
|
+
<span className="truncate text-xs font-medium text-foreground">Duplicate Content</span>
|
|
310
|
+
</span>
|
|
311
|
+
<span className="font-mono text-[11px] text-muted-foreground">
|
|
298
312
|
{String(groups.length)} group{groups.length === 1 ? "" : "s"} | ~
|
|
299
313
|
{formatTokens(totalRepeatedTokens)} repeated
|
|
300
|
-
</
|
|
301
|
-
</
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
314
|
+
</span>
|
|
315
|
+
</button>
|
|
316
|
+
|
|
317
|
+
{expanded && (
|
|
318
|
+
<>
|
|
319
|
+
<div className="space-y-2">
|
|
320
|
+
{visibleGroups.map((group, groupIndex) => {
|
|
321
|
+
const visibleSegments = group.segments.slice(0, DUPLICATE_SEGMENT_DISPLAY_COUNT);
|
|
322
|
+
const hiddenSegmentCount = group.segments.length - visibleSegments.length;
|
|
323
|
+
return (
|
|
324
|
+
<div
|
|
325
|
+
key={group.key}
|
|
326
|
+
className="space-y-1.5 border-t border-border/50 pt-2 first:border-t-0 first:pt-0"
|
|
327
|
+
>
|
|
328
|
+
<div className="flex min-w-0 items-center gap-2 text-xs">
|
|
329
|
+
<span className="w-5 shrink-0 text-right font-mono text-muted-foreground/70">
|
|
330
|
+
{String(groupIndex + 1)}
|
|
331
|
+
</span>
|
|
332
|
+
<span className="min-w-0 flex-1 truncate text-foreground">
|
|
333
|
+
{group.firstLabel}
|
|
334
|
+
</span>
|
|
335
|
+
<span className="shrink-0 font-mono text-muted-foreground">
|
|
336
|
+
x{String(group.count)} | ~{formatTokens(group.repeatedTokens)} saved
|
|
337
|
+
</span>
|
|
338
|
+
</div>
|
|
339
|
+
|
|
340
|
+
{group.preview.length > 0 && (
|
|
341
|
+
<div className="ml-7 line-clamp-2 break-words rounded bg-background/40 px-2 py-1 font-mono text-[11px] leading-4 text-muted-foreground">
|
|
342
|
+
{group.preview}
|
|
343
|
+
</div>
|
|
344
|
+
)}
|
|
321
345
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
+
<div className="ml-7 flex flex-wrap gap-1">
|
|
347
|
+
{visibleSegments.map((duplicateSegment, segmentIndex) => {
|
|
348
|
+
const target = resolveDuplicateSegment(duplicateSegment, segments);
|
|
349
|
+
const activate =
|
|
350
|
+
target === null || onSegmentActivate === undefined
|
|
351
|
+
? undefined
|
|
352
|
+
: onSegmentActivate;
|
|
353
|
+
const label = `${duplicateSegment.label} ~${formatTokens(
|
|
354
|
+
duplicateSegment.tokens,
|
|
355
|
+
)}`;
|
|
356
|
+
if (activate === undefined || target === null) {
|
|
357
|
+
return (
|
|
358
|
+
<span
|
|
359
|
+
key={`${duplicateSegment.path}-${segmentIndex}`}
|
|
360
|
+
className="inline-flex h-6 max-w-40 items-center rounded border border-border/60 px-1.5 text-[11px] text-muted-foreground"
|
|
361
|
+
title={label}
|
|
362
|
+
>
|
|
363
|
+
<span className="truncate">{label}</span>
|
|
364
|
+
</span>
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
return (
|
|
368
|
+
<button
|
|
369
|
+
key={`${duplicateSegment.path}-${segmentIndex}`}
|
|
370
|
+
type="button"
|
|
371
|
+
onClick={() => activate(target)}
|
|
372
|
+
className="inline-flex h-6 max-w-40 items-center rounded border border-border/60 px-1.5 text-[11px] text-muted-foreground hover:border-border hover:bg-muted/40 hover:text-foreground"
|
|
373
|
+
title="Jump to this duplicate block"
|
|
374
|
+
>
|
|
375
|
+
<span className="truncate">{label}</span>
|
|
376
|
+
</button>
|
|
377
|
+
);
|
|
378
|
+
})}
|
|
379
|
+
{hiddenSegmentCount > 0 && (
|
|
380
|
+
<span className="inline-flex h-6 items-center rounded border border-border/40 px-1.5 font-mono text-[11px] text-muted-foreground/70">
|
|
381
|
+
+{String(hiddenSegmentCount)}
|
|
346
382
|
</span>
|
|
347
|
-
)
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
onClick={() => activate(target)}
|
|
354
|
-
className="inline-flex h-6 max-w-40 items-center rounded border border-border/60 px-1.5 text-[11px] text-muted-foreground hover:border-border hover:bg-muted/40 hover:text-foreground"
|
|
355
|
-
title="Jump to this duplicate block"
|
|
356
|
-
>
|
|
357
|
-
<span className="truncate">{label}</span>
|
|
358
|
-
</button>
|
|
359
|
-
);
|
|
360
|
-
})}
|
|
361
|
-
{hiddenSegmentCount > 0 && (
|
|
362
|
-
<span className="inline-flex h-6 items-center rounded border border-border/40 px-1.5 font-mono text-[11px] text-muted-foreground/70">
|
|
363
|
-
+{String(hiddenSegmentCount)}
|
|
364
|
-
</span>
|
|
365
|
-
)}
|
|
366
|
-
</div>
|
|
367
|
-
</div>
|
|
368
|
-
);
|
|
369
|
-
})}
|
|
370
|
-
</div>
|
|
383
|
+
)}
|
|
384
|
+
</div>
|
|
385
|
+
</div>
|
|
386
|
+
);
|
|
387
|
+
})}
|
|
388
|
+
</div>
|
|
371
389
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
390
|
+
{hiddenGroupCount > 0 && (
|
|
391
|
+
<div className="border-t border-border/50 pt-2 font-mono text-[11px] text-muted-foreground">
|
|
392
|
+
+{String(hiddenGroupCount)} more duplicate group{hiddenGroupCount === 1 ? "" : "s"}
|
|
393
|
+
</div>
|
|
394
|
+
)}
|
|
395
|
+
</>
|
|
376
396
|
)}
|
|
377
397
|
</section>
|
|
378
398
|
);
|
|
@@ -167,7 +167,7 @@ const MODEL_CONTEXT_RULES: readonly ModelContextRule[] = [
|
|
|
167
167
|
},
|
|
168
168
|
{
|
|
169
169
|
family: "DeepSeek V4",
|
|
170
|
-
tokens:
|
|
170
|
+
tokens: 1_048_576,
|
|
171
171
|
mode: "prefix",
|
|
172
172
|
patterns: ["deepseek-v4-pro", "deepseek-v4-flash"],
|
|
173
173
|
},
|
|
@@ -179,15 +179,33 @@ const MODEL_CONTEXT_RULES: readonly ModelContextRule[] = [
|
|
|
179
179
|
},
|
|
180
180
|
{
|
|
181
181
|
family: "MiniMax M3",
|
|
182
|
-
tokens:
|
|
182
|
+
tokens: 1_048_576,
|
|
183
183
|
mode: "includes",
|
|
184
184
|
patterns: ["minimax-m3"],
|
|
185
185
|
},
|
|
186
186
|
{
|
|
187
|
-
family: "MiniMax M2",
|
|
187
|
+
family: "MiniMax M2.7",
|
|
188
188
|
tokens: 204_800,
|
|
189
189
|
mode: "includes",
|
|
190
|
-
patterns: ["minimax-m2.7"
|
|
190
|
+
patterns: ["minimax-m2.7"],
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
family: "MiniMax M2.5/M2.1",
|
|
194
|
+
tokens: 196_608,
|
|
195
|
+
mode: "includes",
|
|
196
|
+
patterns: ["minimax-m2.5", "minimax-m2.1"],
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
family: "GLM-5.2",
|
|
200
|
+
tokens: 1_048_576,
|
|
201
|
+
mode: "prefix",
|
|
202
|
+
patterns: ["glm-5.2"],
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
family: "GLM-5",
|
|
206
|
+
tokens: 202_752,
|
|
207
|
+
mode: "prefix",
|
|
208
|
+
patterns: ["glm-5", "glm-5.1"],
|
|
191
209
|
},
|
|
192
210
|
];
|
|
193
211
|
|
|
@@ -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 {
|