@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
|
@@ -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 {
|
|
@@ -36,6 +36,9 @@ const DIVERGENCE_AMBER_THRESHOLD = 0.25;
|
|
|
36
36
|
const TOP_CONTRIBUTOR_COUNT = 5;
|
|
37
37
|
const DUPLICATE_GROUP_DISPLAY_COUNT = 4;
|
|
38
38
|
const DUPLICATE_SEGMENT_DISPLAY_COUNT = 6;
|
|
39
|
+
const TOOL_SCHEMA_SAVINGS_RATIO = 0.25;
|
|
40
|
+
const SYSTEM_PROMPT_SAVINGS_RATIO = 0.2;
|
|
41
|
+
const HISTORY_SAVINGS_RATIO = 0.2;
|
|
39
42
|
const ROLE_ORDER: AnatomyRole[] = ["system", "user", "assistant", "tool", "tools"];
|
|
40
43
|
|
|
41
44
|
const ROLE_DESCRIPTIONS: Record<AnatomyRole, string> = {
|
|
@@ -77,6 +80,19 @@ const HEALTH_LABEL: Record<ContextHealthLevel, string> = {
|
|
|
77
80
|
risk: "Risk",
|
|
78
81
|
};
|
|
79
82
|
|
|
83
|
+
type ContextAdvisorKind = ContextDiagnostic["kind"] | "context-pressure" | "context-window";
|
|
84
|
+
|
|
85
|
+
type ContextAdvisorItem = {
|
|
86
|
+
key: string;
|
|
87
|
+
kind: ContextAdvisorKind;
|
|
88
|
+
severity: "watch" | "danger";
|
|
89
|
+
title: string;
|
|
90
|
+
impact: string;
|
|
91
|
+
action: string;
|
|
92
|
+
savingTokens: number | null;
|
|
93
|
+
target: AnatomySegment | null;
|
|
94
|
+
};
|
|
95
|
+
|
|
80
96
|
function formatPercent(value: number): string {
|
|
81
97
|
if (value >= 10) return `${value.toFixed(0)}%`;
|
|
82
98
|
if (value >= 1) return `${value.toFixed(1)}%`;
|
|
@@ -97,7 +113,7 @@ function formatRemainingTokens(value: number | null): string {
|
|
|
97
113
|
|
|
98
114
|
function formatContextLimit(intelligence: ContextIntelligence): string {
|
|
99
115
|
if (intelligence.contextWindow.tokens === null) return "Unknown";
|
|
100
|
-
return
|
|
116
|
+
return formatContextWindowTokens(intelligence.contextWindow.tokens);
|
|
101
117
|
}
|
|
102
118
|
|
|
103
119
|
function formatContextSource(intelligence: ContextIntelligence): string {
|
|
@@ -115,8 +131,8 @@ function formatContextSource(intelligence: ContextIntelligence): string {
|
|
|
115
131
|
}
|
|
116
132
|
}
|
|
117
133
|
|
|
118
|
-
function
|
|
119
|
-
switch (
|
|
134
|
+
function advisorTone(severity: ContextAdvisorItem["severity"]): string {
|
|
135
|
+
switch (severity) {
|
|
120
136
|
case "watch":
|
|
121
137
|
return "border-amber-400/30 bg-amber-400/8 text-amber-100";
|
|
122
138
|
case "danger":
|
|
@@ -151,10 +167,250 @@ function ContextHealthBadge({ health }: { health: ContextHealth }): JSX.Element
|
|
|
151
167
|
);
|
|
152
168
|
}
|
|
153
169
|
|
|
170
|
+
function roleUsageTokens(intelligence: ContextIntelligence, role: AnatomyRole): number {
|
|
171
|
+
return intelligence.roleUsages.find((usage) => usage.role === role)?.tokens ?? 0;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function roleUsagePercent(intelligence: ContextIntelligence, role: AnatomyRole): number {
|
|
175
|
+
return intelligence.roleUsages.find((usage) => usage.role === role)?.percent ?? 0;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function estimatedSaving(tokens: number, ratio: number): number | null {
|
|
179
|
+
if (tokens <= 0) return null;
|
|
180
|
+
const saving = Math.round(tokens * ratio);
|
|
181
|
+
return saving > 0 ? saving : null;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function largestSegmentForRole(
|
|
185
|
+
segments: readonly AnatomySegment[],
|
|
186
|
+
role: AnatomyRole,
|
|
187
|
+
): AnatomySegment | null {
|
|
188
|
+
return (
|
|
189
|
+
[...segments]
|
|
190
|
+
.filter((segment) => segment.role === role)
|
|
191
|
+
.sort((left, right) => right.size - left.size)[0] ?? null
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function largestSegmentForHistory(segments: readonly AnatomySegment[]): AnatomySegment | null {
|
|
196
|
+
return (
|
|
197
|
+
[...segments]
|
|
198
|
+
.filter(
|
|
199
|
+
(segment) =>
|
|
200
|
+
segment.role === "user" || segment.role === "assistant" || segment.role === "tool",
|
|
201
|
+
)
|
|
202
|
+
.sort((left, right) => right.size - left.size)[0] ?? null
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function firstDuplicateTarget(
|
|
207
|
+
groups: readonly DuplicateContextGroup[],
|
|
208
|
+
segments: readonly AnatomySegment[],
|
|
209
|
+
): AnatomySegment | null {
|
|
210
|
+
const firstGroup = groups[0] ?? null;
|
|
211
|
+
if (firstGroup === null) return null;
|
|
212
|
+
const firstSegment = firstGroup.segments[0] ?? null;
|
|
213
|
+
if (firstSegment === null) return null;
|
|
214
|
+
return resolveDuplicateSegment(firstSegment, segments);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function duplicateSaving(groups: readonly DuplicateContextGroup[]): number | null {
|
|
218
|
+
const saving = groups.reduce((sum, group) => sum + group.repeatedTokens, 0);
|
|
219
|
+
return saving > 0 ? saving : null;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function historyTokens(intelligence: ContextIntelligence): number {
|
|
223
|
+
return (
|
|
224
|
+
roleUsageTokens(intelligence, "user") +
|
|
225
|
+
roleUsageTokens(intelligence, "assistant") +
|
|
226
|
+
roleUsageTokens(intelligence, "tool")
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function advisorItemFromDiagnostic({
|
|
231
|
+
diagnostic,
|
|
232
|
+
intelligence,
|
|
233
|
+
segments,
|
|
234
|
+
}: {
|
|
235
|
+
diagnostic: ContextDiagnostic;
|
|
236
|
+
intelligence: ContextIntelligence;
|
|
237
|
+
segments: readonly AnatomySegment[];
|
|
238
|
+
}): ContextAdvisorItem {
|
|
239
|
+
switch (diagnostic.kind) {
|
|
240
|
+
case "duplicate":
|
|
241
|
+
return {
|
|
242
|
+
key: "duplicate",
|
|
243
|
+
kind: "duplicate",
|
|
244
|
+
severity: diagnostic.severity,
|
|
245
|
+
title: "Duplicate content",
|
|
246
|
+
impact: diagnostic.detail,
|
|
247
|
+
action: "Remove repeated blocks or keep one canonical copy in the next request.",
|
|
248
|
+
savingTokens: duplicateSaving(intelligence.duplicateGroups),
|
|
249
|
+
target: firstDuplicateTarget(intelligence.duplicateGroups, segments),
|
|
250
|
+
};
|
|
251
|
+
case "tool-schema": {
|
|
252
|
+
const tokens = roleUsageTokens(intelligence, "tools");
|
|
253
|
+
const percent = roleUsagePercent(intelligence, "tools");
|
|
254
|
+
return {
|
|
255
|
+
key: "tool-schema",
|
|
256
|
+
kind: "tool-schema",
|
|
257
|
+
severity: diagnostic.severity,
|
|
258
|
+
title: "Tool schema pressure",
|
|
259
|
+
impact: `Tool definitions use ~${formatTokens(tokens)} tokens (${formatPercent(
|
|
260
|
+
percent * 100,
|
|
261
|
+
)}).`,
|
|
262
|
+
action: "Disable unused tools or shorten verbose schema descriptions.",
|
|
263
|
+
savingTokens: estimatedSaving(tokens, TOOL_SCHEMA_SAVINGS_RATIO),
|
|
264
|
+
target: largestSegmentForRole(segments, "tools"),
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
case "system-heavy": {
|
|
268
|
+
const tokens = roleUsageTokens(intelligence, "system");
|
|
269
|
+
const percent = roleUsagePercent(intelligence, "system");
|
|
270
|
+
return {
|
|
271
|
+
key: "system-heavy",
|
|
272
|
+
kind: "system-heavy",
|
|
273
|
+
severity: diagnostic.severity,
|
|
274
|
+
title: "Large system prompt",
|
|
275
|
+
impact: `System instructions use ~${formatTokens(tokens)} tokens (${formatPercent(
|
|
276
|
+
percent * 100,
|
|
277
|
+
)}).`,
|
|
278
|
+
action: "Compress repeated policy text and move stable guidance into reusable memory.",
|
|
279
|
+
savingTokens: estimatedSaving(tokens, SYSTEM_PROMPT_SAVINGS_RATIO),
|
|
280
|
+
target: largestSegmentForRole(segments, "system"),
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
case "history-bloat": {
|
|
284
|
+
const tokens = historyTokens(intelligence);
|
|
285
|
+
return {
|
|
286
|
+
key: "history-bloat",
|
|
287
|
+
kind: "history-bloat",
|
|
288
|
+
severity: diagnostic.severity,
|
|
289
|
+
title: "Conversation history growth",
|
|
290
|
+
impact: diagnostic.detail,
|
|
291
|
+
action: "Summarize older turns and trim resolved tool outputs before continuing.",
|
|
292
|
+
savingTokens: estimatedSaving(tokens, HISTORY_SAVINGS_RATIO),
|
|
293
|
+
target: largestSegmentForHistory(segments),
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function buildContextAdvisorItems({
|
|
300
|
+
intelligence,
|
|
301
|
+
segments,
|
|
302
|
+
}: {
|
|
303
|
+
intelligence: ContextIntelligence;
|
|
304
|
+
segments: readonly AnatomySegment[];
|
|
305
|
+
}): ContextAdvisorItem[] {
|
|
306
|
+
const items: ContextAdvisorItem[] = [];
|
|
307
|
+
|
|
308
|
+
if (intelligence.riskLevel === "watch" || intelligence.riskLevel === "danger") {
|
|
309
|
+
items.push({
|
|
310
|
+
key: "context-pressure",
|
|
311
|
+
kind: "context-pressure",
|
|
312
|
+
severity: intelligence.riskLevel === "danger" ? "danger" : "watch",
|
|
313
|
+
title: "Context window pressure",
|
|
314
|
+
impact: `${formatUsagePercent(intelligence.usagePercent)} used, ${formatRemainingTokens(
|
|
315
|
+
intelligence.remainingAfterReserveTokens,
|
|
316
|
+
)} remaining after output reserve.`,
|
|
317
|
+
action: "Start with the largest context block, then remove duplicates or summarize history.",
|
|
318
|
+
savingTokens: null,
|
|
319
|
+
target: [...segments].sort((left, right) => right.size - left.size)[0] ?? null,
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (intelligence.contextWindow.source === "unknown") {
|
|
324
|
+
items.push({
|
|
325
|
+
key: "context-window",
|
|
326
|
+
kind: "context-window",
|
|
327
|
+
severity: "watch",
|
|
328
|
+
title: "Context window unknown",
|
|
329
|
+
impact: "Agent Inspector cannot calculate remaining space for this model.",
|
|
330
|
+
action: "Add model metadata in Provider settings so risk and remaining space are accurate.",
|
|
331
|
+
savingTokens: null,
|
|
332
|
+
target: null,
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
intelligence.diagnostics.forEach((diagnostic) => {
|
|
337
|
+
items.push(advisorItemFromDiagnostic({ diagnostic, intelligence, segments }));
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
return items;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function ContextAdvisorPanel({
|
|
344
|
+
items,
|
|
345
|
+
onSegmentActivate,
|
|
346
|
+
}: {
|
|
347
|
+
items: readonly ContextAdvisorItem[];
|
|
348
|
+
onSegmentActivate?: (segment: AnatomySegment) => void;
|
|
349
|
+
}): JSX.Element | null {
|
|
350
|
+
if (items.length === 0) return null;
|
|
351
|
+
|
|
352
|
+
return (
|
|
353
|
+
<section className="space-y-2 rounded border border-border/70 bg-muted/15 p-2">
|
|
354
|
+
<div className="flex flex-wrap items-center justify-between gap-2">
|
|
355
|
+
<div className="text-xs font-medium text-foreground">Context Advisor</div>
|
|
356
|
+
<div className="font-mono text-[11px] text-muted-foreground">
|
|
357
|
+
{String(items.length)} issue{items.length === 1 ? "" : "s"}
|
|
358
|
+
</div>
|
|
359
|
+
</div>
|
|
360
|
+
<div className="grid gap-2">
|
|
361
|
+
{items.map((item) => {
|
|
362
|
+
const target = item.target;
|
|
363
|
+
const canJump = target !== null && onSegmentActivate !== undefined;
|
|
364
|
+
return (
|
|
365
|
+
<div
|
|
366
|
+
key={item.key}
|
|
367
|
+
className={cn("rounded border px-2 py-1.5 text-xs", advisorTone(item.severity))}
|
|
368
|
+
>
|
|
369
|
+
<div className="flex min-w-0 flex-wrap items-start justify-between gap-2">
|
|
370
|
+
<div className="min-w-0">
|
|
371
|
+
<div className="font-medium">{item.title}</div>
|
|
372
|
+
<div className="mt-0.5 text-[11px] text-muted-foreground">
|
|
373
|
+
Impact: {item.impact}
|
|
374
|
+
</div>
|
|
375
|
+
<div className="mt-0.5 text-[11px] text-muted-foreground">
|
|
376
|
+
Action: {item.action}
|
|
377
|
+
</div>
|
|
378
|
+
</div>
|
|
379
|
+
<div className="flex shrink-0 items-center gap-1.5">
|
|
380
|
+
{item.savingTokens !== null && (
|
|
381
|
+
<span className="font-mono text-[11px] text-muted-foreground">
|
|
382
|
+
Save ~{formatTokens(item.savingTokens)}
|
|
383
|
+
</span>
|
|
384
|
+
)}
|
|
385
|
+
{canJump && target !== null && onSegmentActivate !== undefined && (
|
|
386
|
+
<button
|
|
387
|
+
type="button"
|
|
388
|
+
onClick={() => onSegmentActivate(target)}
|
|
389
|
+
className="inline-flex h-6 items-center gap-1 rounded border border-border/60 px-1.5 text-[11px] text-muted-foreground hover:border-border hover:bg-muted/40 hover:text-foreground"
|
|
390
|
+
title="Jump to the most relevant context block"
|
|
391
|
+
>
|
|
392
|
+
<ChevronRight className="size-3" />
|
|
393
|
+
Jump
|
|
394
|
+
</button>
|
|
395
|
+
)}
|
|
396
|
+
</div>
|
|
397
|
+
</div>
|
|
398
|
+
</div>
|
|
399
|
+
);
|
|
400
|
+
})}
|
|
401
|
+
</div>
|
|
402
|
+
</section>
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
|
|
154
406
|
function ContextIntelligenceStrip({
|
|
155
407
|
intelligence,
|
|
408
|
+
segments,
|
|
409
|
+
onSegmentActivate,
|
|
156
410
|
}: {
|
|
157
411
|
intelligence: ContextIntelligence;
|
|
412
|
+
segments: readonly AnatomySegment[];
|
|
413
|
+
onSegmentActivate?: (segment: AnatomySegment) => void;
|
|
158
414
|
}): JSX.Element {
|
|
159
415
|
const usageWidth =
|
|
160
416
|
intelligence.usagePercent === null ? 0 : Math.max(1, intelligence.usagePercent * 100);
|
|
@@ -163,10 +419,7 @@ function ContextIntelligenceStrip({
|
|
|
163
419
|
? "No output reserve"
|
|
164
420
|
: `Output reserve ${formatTokens(intelligence.outputReserveTokens)}`;
|
|
165
421
|
const largestRole = intelligence.largestRole;
|
|
166
|
-
const
|
|
167
|
-
intelligence.duplicateGroups.length === 0
|
|
168
|
-
? intelligence.diagnostics
|
|
169
|
-
: intelligence.diagnostics.filter((diagnostic) => diagnostic.kind !== "duplicate");
|
|
422
|
+
const advisorItems = buildContextAdvisorItems({ intelligence, segments });
|
|
170
423
|
|
|
171
424
|
return (
|
|
172
425
|
<div className="space-y-2">
|
|
@@ -251,19 +504,7 @@ function ContextIntelligenceStrip({
|
|
|
251
504
|
</div>
|
|
252
505
|
)}
|
|
253
506
|
|
|
254
|
-
{
|
|
255
|
-
<div className="grid gap-1.5 sm:grid-cols-2">
|
|
256
|
-
{visibleDiagnostics.map((diagnostic) => (
|
|
257
|
-
<div
|
|
258
|
-
key={`${diagnostic.kind}-${diagnostic.title}`}
|
|
259
|
-
className={cn("rounded border px-2 py-1.5 text-xs", diagnosticTone(diagnostic))}
|
|
260
|
-
>
|
|
261
|
-
<div className="font-medium">{diagnostic.title}</div>
|
|
262
|
-
<div className="mt-0.5 text-[11px] text-muted-foreground">{diagnostic.detail}</div>
|
|
263
|
-
</div>
|
|
264
|
-
))}
|
|
265
|
-
</div>
|
|
266
|
-
)}
|
|
507
|
+
<ContextAdvisorPanel items={advisorItems} onSegmentActivate={onSegmentActivate} />
|
|
267
508
|
</div>
|
|
268
509
|
);
|
|
269
510
|
}
|
|
@@ -284,6 +525,8 @@ function DuplicateContentPanel({
|
|
|
284
525
|
segments: readonly AnatomySegment[];
|
|
285
526
|
onSegmentActivate?: (segment: AnatomySegment) => void;
|
|
286
527
|
}): JSX.Element | null {
|
|
528
|
+
const [expanded, setExpanded] = useState(false);
|
|
529
|
+
|
|
287
530
|
if (groups.length === 0) return null;
|
|
288
531
|
|
|
289
532
|
const visibleGroups = groups.slice(0, DUPLICATE_GROUP_DISPLAY_COUNT);
|
|
@@ -292,87 +535,105 @@ function DuplicateContentPanel({
|
|
|
292
535
|
|
|
293
536
|
return (
|
|
294
537
|
<section className="space-y-2 rounded border border-border/70 bg-muted/15 p-2">
|
|
295
|
-
<
|
|
296
|
-
|
|
297
|
-
|
|
538
|
+
<button
|
|
539
|
+
type="button"
|
|
540
|
+
onClick={() => setExpanded((current) => !current)}
|
|
541
|
+
aria-expanded={expanded}
|
|
542
|
+
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"
|
|
543
|
+
>
|
|
544
|
+
<span className="inline-flex min-w-0 items-center gap-1.5">
|
|
545
|
+
{expanded ? (
|
|
546
|
+
<ChevronDown className="size-3.5 shrink-0 text-muted-foreground" />
|
|
547
|
+
) : (
|
|
548
|
+
<ChevronRight className="size-3.5 shrink-0 text-muted-foreground" />
|
|
549
|
+
)}
|
|
550
|
+
<span className="truncate text-xs font-medium text-foreground">Duplicate Content</span>
|
|
551
|
+
</span>
|
|
552
|
+
<span className="font-mono text-[11px] text-muted-foreground">
|
|
298
553
|
{String(groups.length)} group{groups.length === 1 ? "" : "s"} | ~
|
|
299
554
|
{formatTokens(totalRepeatedTokens)} repeated
|
|
300
|
-
</
|
|
301
|
-
</
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
555
|
+
</span>
|
|
556
|
+
</button>
|
|
557
|
+
|
|
558
|
+
{expanded && (
|
|
559
|
+
<>
|
|
560
|
+
<div className="space-y-2">
|
|
561
|
+
{visibleGroups.map((group, groupIndex) => {
|
|
562
|
+
const visibleSegments = group.segments.slice(0, DUPLICATE_SEGMENT_DISPLAY_COUNT);
|
|
563
|
+
const hiddenSegmentCount = group.segments.length - visibleSegments.length;
|
|
564
|
+
return (
|
|
565
|
+
<div
|
|
566
|
+
key={group.key}
|
|
567
|
+
className="space-y-1.5 border-t border-border/50 pt-2 first:border-t-0 first:pt-0"
|
|
568
|
+
>
|
|
569
|
+
<div className="flex min-w-0 items-center gap-2 text-xs">
|
|
570
|
+
<span className="w-5 shrink-0 text-right font-mono text-muted-foreground/70">
|
|
571
|
+
{String(groupIndex + 1)}
|
|
572
|
+
</span>
|
|
573
|
+
<span className="min-w-0 flex-1 truncate text-foreground">
|
|
574
|
+
{group.firstLabel}
|
|
575
|
+
</span>
|
|
576
|
+
<span className="shrink-0 font-mono text-muted-foreground">
|
|
577
|
+
x{String(group.count)} | ~{formatTokens(group.repeatedTokens)} saved
|
|
578
|
+
</span>
|
|
579
|
+
</div>
|
|
580
|
+
|
|
581
|
+
{group.preview.length > 0 && (
|
|
582
|
+
<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">
|
|
583
|
+
{group.preview}
|
|
584
|
+
</div>
|
|
585
|
+
)}
|
|
321
586
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
587
|
+
<div className="ml-7 flex flex-wrap gap-1">
|
|
588
|
+
{visibleSegments.map((duplicateSegment, segmentIndex) => {
|
|
589
|
+
const target = resolveDuplicateSegment(duplicateSegment, segments);
|
|
590
|
+
const activate =
|
|
591
|
+
target === null || onSegmentActivate === undefined
|
|
592
|
+
? undefined
|
|
593
|
+
: onSegmentActivate;
|
|
594
|
+
const label = `${duplicateSegment.label} ~${formatTokens(
|
|
595
|
+
duplicateSegment.tokens,
|
|
596
|
+
)}`;
|
|
597
|
+
if (activate === undefined || target === null) {
|
|
598
|
+
return (
|
|
599
|
+
<span
|
|
600
|
+
key={`${duplicateSegment.path}-${segmentIndex}`}
|
|
601
|
+
className="inline-flex h-6 max-w-40 items-center rounded border border-border/60 px-1.5 text-[11px] text-muted-foreground"
|
|
602
|
+
title={label}
|
|
603
|
+
>
|
|
604
|
+
<span className="truncate">{label}</span>
|
|
605
|
+
</span>
|
|
606
|
+
);
|
|
607
|
+
}
|
|
608
|
+
return (
|
|
609
|
+
<button
|
|
610
|
+
key={`${duplicateSegment.path}-${segmentIndex}`}
|
|
611
|
+
type="button"
|
|
612
|
+
onClick={() => activate(target)}
|
|
613
|
+
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"
|
|
614
|
+
title="Jump to this duplicate block"
|
|
615
|
+
>
|
|
616
|
+
<span className="truncate">{label}</span>
|
|
617
|
+
</button>
|
|
618
|
+
);
|
|
619
|
+
})}
|
|
620
|
+
{hiddenSegmentCount > 0 && (
|
|
621
|
+
<span className="inline-flex h-6 items-center rounded border border-border/40 px-1.5 font-mono text-[11px] text-muted-foreground/70">
|
|
622
|
+
+{String(hiddenSegmentCount)}
|
|
346
623
|
</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>
|
|
624
|
+
)}
|
|
625
|
+
</div>
|
|
626
|
+
</div>
|
|
627
|
+
);
|
|
628
|
+
})}
|
|
629
|
+
</div>
|
|
371
630
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
631
|
+
{hiddenGroupCount > 0 && (
|
|
632
|
+
<div className="border-t border-border/50 pt-2 font-mono text-[11px] text-muted-foreground">
|
|
633
|
+
+{String(hiddenGroupCount)} more duplicate group{hiddenGroupCount === 1 ? "" : "s"}
|
|
634
|
+
</div>
|
|
635
|
+
)}
|
|
636
|
+
</>
|
|
376
637
|
)}
|
|
377
638
|
</section>
|
|
378
639
|
);
|
|
@@ -533,7 +794,13 @@ export function RequestAnatomy({
|
|
|
533
794
|
</div>
|
|
534
795
|
)}
|
|
535
796
|
|
|
536
|
-
{intelligence !== null &&
|
|
797
|
+
{intelligence !== null && (
|
|
798
|
+
<ContextIntelligenceStrip
|
|
799
|
+
intelligence={intelligence}
|
|
800
|
+
segments={segments}
|
|
801
|
+
onSegmentActivate={onSegmentActivate}
|
|
802
|
+
/>
|
|
803
|
+
)}
|
|
537
804
|
|
|
538
805
|
{intelligence !== null && (
|
|
539
806
|
<DuplicateContentPanel
|
|
@@ -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
|
|