@tonyclaw/agent-inspector 2.0.10 → 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-BWMipEjS.js → CompareDrawer-ClM_uVRy.js} +1 -1
- package/.output/public/assets/ProxyViewerContainer-CTNNzXSa.js +114 -0
- package/.output/public/assets/{ReplayDialog-DaSzZsvM.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-B4atnL99.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-DE6Y4-wV.js → main-FvIQopAy.js} +2 -2
- package/.output/server/_libs/lucide-react.mjs +51 -39
- package/.output/server/{_sessionId-61HO9rUI.mjs → _sessionId-BDSgtrsI.mjs} +2 -2
- package/.output/server/_ssr/{CompareDrawer-CSPqneYm.mjs → CompareDrawer-Cqvv0I3x.mjs} +3 -3
- package/.output/server/_ssr/{ProxyViewerContainer-DrSMa5O7.mjs → ProxyViewerContainer-C0YEoG8N.mjs} +1611 -183
- package/.output/server/_ssr/{ReplayDialog-Dn3naENv.mjs → ReplayDialog-CrsynUKI.mjs} +4 -4
- package/.output/server/_ssr/RequestAnatomy-CR_JUkcL.mjs +688 -0
- package/.output/server/_ssr/{ResponseView-CwXAWOoE.mjs → ResponseView-Dba2Et69.mjs} +3 -3
- package/.output/server/_ssr/{StreamingChunkSequence-DRSR2FfN.mjs → StreamingChunkSequence-BIMHLstI.mjs} +2 -2
- package/.output/server/_ssr/{index-D4zn5CAg.mjs → index-DveIUTm5.mjs} +2 -2
- package/.output/server/_ssr/index.mjs +2 -2
- package/.output/server/_ssr/{router-CSRaa_tu.mjs → router-D0yXVG0Q.mjs} +85 -22
- package/.output/server/{_tanstack-start-manifest_v-CfvVUYYj.mjs → _tanstack-start-manifest_v-pe0xJFuQ.mjs} +1 -1
- package/.output/server/index.mjs +70 -70
- 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 +144 -80
- package/src/components/proxy-viewer/anatomy/contextIntelligence.ts +70 -5
- 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-DIAgurux.js +0 -114
- package/.output/public/assets/RequestAnatomy-BfQmPHCd.js +0 -1
- package/.output/public/assets/ResponseView-CblEWDMx.js +0 -1
- package/.output/public/assets/_sessionId-DUzYnZXV.js +0 -1
- package/.output/public/assets/index-CxhRsekH.css +0 -1
- package/.output/public/assets/index-jagRsQaV.js +0 -1
- package/.output/server/_ssr/RequestAnatomy-D1X3f3AX.mjs +0 -1322
package/.output/server/_ssr/{ProxyViewerContainer-DrSMa5O7.mjs → ProxyViewerContainer-C0YEoG8N.mjs}
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { r as reactExports, j as jsxRuntimeExports, a as React } from "../_libs/react.mjs";
|
|
2
|
-
import { C as CapturedLogSchema, D as DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS, a as DEFAULT_TIME_DISPLAY_FORMAT, b as RuntimeConfigSchema, r as requestFormatForPath, h as createPendingProviderTestResults,
|
|
2
|
+
import { C as CapturedLogSchema, D as DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS, a as DEFAULT_TIME_DISPLAY_FORMAT, b as RuntimeConfigSchema, P as ProviderConfigSchema, r as requestFormatForPath, h as createPendingProviderTestResults, i as ProviderTestResultsSchema, j as createFailedProviderTestResults, M as MAX_SLOW_RESPONSE_THRESHOLD_SECONDS, T as TimeDisplayFormatSchema, g as getSessionPath, e as providerHasContextMetadata, f as findProviderModelMetadata, s as safeGetOwnProperty, K as KnowledgeCandidateSchema, d as stripClaudeCodeBillingHeader, p as parseOpenAIResponse, O as OpenAIRequestSchema, A as AnthropicResponseSchema$1, c as AnthropicRequestSchema, k as resolveProviderContextWindow, l as isPlainRecord } from "./router-D0yXVG0Q.mjs";
|
|
3
3
|
import { u as useSWR, a as useSWRConfig } from "../_libs/swr.mjs";
|
|
4
4
|
import { J as JSZip } from "../_libs/jszip.mjs";
|
|
5
5
|
import { c as clsx } from "../_libs/clsx.mjs";
|
|
@@ -9,7 +9,7 @@ import { R as Root, T as Trigger$2, C as Content, a as Close, b as Title, P as P
|
|
|
9
9
|
import { d as diffJson, a as diffLines } from "../_libs/diff.mjs";
|
|
10
10
|
import { u as useVirtualizer } from "../_libs/tanstack__react-virtual.mjs";
|
|
11
11
|
import { R as Root2, T as Trigger, I as Icon, V as Value, P as Portal, C as Content2, a as Viewport, b as Item, c as ItemIndicator, d as ItemText, S as ScrollUpButton, e as ScrollDownButton } from "../_libs/radix-ui__react-select.mjs";
|
|
12
|
-
import { C as Check, X, P as Plus, D as Download, S as Settings, A as ArrowLeft, a as Copy, b as ChevronDown, U as Upload, c as Scan, d as CircleAlert, T as Terminal, e as ChevronUp, L as LoaderCircle, f as ChevronRight, g as User, h as Clock, M as MessageSquare, Z as Zap, E as ExternalLink, i as Trash2, W as Wrench, j as TriangleAlert, B as Brain, k as EyeOff, l as Eye, R as RotateCw, m as Pencil, n as
|
|
12
|
+
import { C as Check, X, P as Plus, D as Download, S as Settings, A as ArrowLeft, a as Copy, b as ChevronDown, U as Upload, c as Scan, d as CircleAlert, T as Terminal, e as ChevronUp, L as LoaderCircle, f as ChevronRight, g as User, h as Clock, M as MessageSquare, Z as Zap, E as ExternalLink, i as Trash2, W as Wrench, j as TriangleAlert, B as Brain, k as EyeOff, l as Eye, R as RotateCw, m as Pencil, n as ArrowUpRight, o as ArrowDownRight, p as Minus, q as CircleCheckBig, r as CircleX, s as ShieldCheck, t as RefreshCw, u as Save, F as FileSearch, v as CircleCheck, w as CloudUpload, O as OctagonAlert, G as Globe, x as FileTerminal, y as Radio, z as ChevronsUp, H as ChevronsDown, I as FileDiff, J as History, K as RotateCcw, N as Search, Q as GitCompareArrows, V as CircleQuestionMark, Y as Server, _ as Gauge, $ as Lock, a0 as Wifi, a1 as WifiOff, a2 as ArrowUp, a3 as ArrowDown, a4 as Ellipsis, a5 as CodeXml, a6 as Blocks, a7 as MousePointerClick, a8 as FileText, a9 as Rows3, aa as Columns2 } from "../_libs/lucide-react.mjs";
|
|
13
13
|
import { M as Markdown } from "../_libs/react-markdown.mjs";
|
|
14
14
|
import { u as union, d as object, a as array, l as literal, b as string, n as number, c as boolean, _ as _enum } from "../_libs/zod.mjs";
|
|
15
15
|
import { P as Provider, R as Root3, T as Trigger$1, a as Portal$1, C as Content2$1, A as Arrow2 } from "../_libs/radix-ui__react-tooltip.mjs";
|
|
@@ -291,6 +291,12 @@ function formatTokens(count) {
|
|
|
291
291
|
if (count >= 1e6) return (count / 1e6).toFixed(1).replace(/\.0$/, "") + "M";
|
|
292
292
|
return (count / 1e3).toFixed(1).replace(/\.0$/, "") + "K";
|
|
293
293
|
}
|
|
294
|
+
function formatContextWindowTokens(count) {
|
|
295
|
+
if (count >= 1024 * 1024) {
|
|
296
|
+
return (count / (1024 * 1024)).toFixed(1).replace(/\.0$/, "") + "M";
|
|
297
|
+
}
|
|
298
|
+
return (count / 1024).toFixed(1).replace(/\.0$/, "") + "K";
|
|
299
|
+
}
|
|
294
300
|
function getStatusCategory(status) {
|
|
295
301
|
if (status === null) return "pending";
|
|
296
302
|
if (status >= 200 && status < 300) return "success";
|
|
@@ -298,7 +304,31 @@ function getStatusCategory(status) {
|
|
|
298
304
|
if (status >= 500) return "server_error";
|
|
299
305
|
return "pending";
|
|
300
306
|
}
|
|
301
|
-
|
|
307
|
+
async function fetcher(url) {
|
|
308
|
+
return fetchJson(
|
|
309
|
+
url,
|
|
310
|
+
ProviderConfigSchema.array(),
|
|
311
|
+
void 0,
|
|
312
|
+
(response) => `Failed to fetch ${url}: ${response.status}`
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
function useProviders() {
|
|
316
|
+
const response = useSWR(
|
|
317
|
+
"/api/providers",
|
|
318
|
+
fetcher,
|
|
319
|
+
{
|
|
320
|
+
revalidateOnFocus: false,
|
|
321
|
+
revalidateIfStale: false
|
|
322
|
+
}
|
|
323
|
+
);
|
|
324
|
+
return {
|
|
325
|
+
providers: response.data ?? [],
|
|
326
|
+
isLoading: response.isLoading,
|
|
327
|
+
isError: response.error,
|
|
328
|
+
mutate: response.mutate
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
const version = "2.0.12";
|
|
302
332
|
const packageJson = {
|
|
303
333
|
version
|
|
304
334
|
};
|
|
@@ -1359,19 +1389,19 @@ function useCopyFeedback(text) {
|
|
|
1359
1389
|
return { copied, copy };
|
|
1360
1390
|
}
|
|
1361
1391
|
const LazyCompareDrawer = reactExports.lazy(
|
|
1362
|
-
() => import("./CompareDrawer-
|
|
1392
|
+
() => import("./CompareDrawer-Cqvv0I3x.mjs").then((m) => ({ default: m.CompareDrawer }))
|
|
1363
1393
|
);
|
|
1364
1394
|
const LazyReplayDialog = reactExports.lazy(
|
|
1365
|
-
() => import("./ReplayDialog-
|
|
1395
|
+
() => import("./ReplayDialog-CrsynUKI.mjs").then((m) => ({ default: m.ReplayDialog }))
|
|
1366
1396
|
);
|
|
1367
1397
|
const LazyRequestAnatomy = reactExports.lazy(
|
|
1368
|
-
() => import("./RequestAnatomy-
|
|
1398
|
+
() => import("./RequestAnatomy-CR_JUkcL.mjs").then((m) => ({ default: m.RequestAnatomy }))
|
|
1369
1399
|
);
|
|
1370
1400
|
const LazyResponseView = reactExports.lazy(
|
|
1371
|
-
() => import("./ResponseView-
|
|
1401
|
+
() => import("./ResponseView-Dba2Et69.mjs").then((m) => ({ default: m.ResponseView }))
|
|
1372
1402
|
);
|
|
1373
1403
|
const LazyStreamingChunkSequence = reactExports.lazy(
|
|
1374
|
-
() => import("./StreamingChunkSequence-
|
|
1404
|
+
() => import("./StreamingChunkSequence-BIMHLstI.mjs").then((m) => ({
|
|
1375
1405
|
default: m.StreamingChunkSequence
|
|
1376
1406
|
}))
|
|
1377
1407
|
);
|
|
@@ -2986,6 +3016,26 @@ function resolveLogFormat(log) {
|
|
|
2986
3016
|
const pathFormat = requestFormatForPath(log.path);
|
|
2987
3017
|
return pathFormat === "unknown" ? log.apiFormat : pathFormat;
|
|
2988
3018
|
}
|
|
3019
|
+
const LOG_FOCUS_REQUEST_EVENT = "agent-inspector:focus-log";
|
|
3020
|
+
function dispatchLogFocusRequest(request) {
|
|
3021
|
+
window.dispatchEvent(new CustomEvent(LOG_FOCUS_REQUEST_EVENT, { detail: request }));
|
|
3022
|
+
}
|
|
3023
|
+
function readLogFocusRequest(event) {
|
|
3024
|
+
if (typeof CustomEvent === "undefined" || !(event instanceof CustomEvent)) return null;
|
|
3025
|
+
const detail = event.detail;
|
|
3026
|
+
const logId = safeGetOwnProperty(detail, "logId");
|
|
3027
|
+
if (typeof logId !== "number" || !Number.isInteger(logId) || logId < 0) return null;
|
|
3028
|
+
const tab = safeGetOwnProperty(detail, "tab");
|
|
3029
|
+
switch (tab) {
|
|
3030
|
+
case "anatomy":
|
|
3031
|
+
case "request":
|
|
3032
|
+
return { logId, tab };
|
|
3033
|
+
case void 0:
|
|
3034
|
+
return { logId, tab: "request" };
|
|
3035
|
+
default:
|
|
3036
|
+
return null;
|
|
3037
|
+
}
|
|
3038
|
+
}
|
|
2989
3039
|
function shouldShowRawRequestTab(apiFormat, viewMode, strip) {
|
|
2990
3040
|
return apiFormat === "anthropic" && viewMode === "full" && strip;
|
|
2991
3041
|
}
|
|
@@ -3161,6 +3211,14 @@ function parseRequestTools(parsed) {
|
|
|
3161
3211
|
function TabFallback() {
|
|
3162
3212
|
return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-1", "aria-hidden": "true" });
|
|
3163
3213
|
}
|
|
3214
|
+
function resolveFocusedTab(tab, anatomySegments) {
|
|
3215
|
+
switch (tab) {
|
|
3216
|
+
case "anatomy":
|
|
3217
|
+
return anatomySegments === null ? "request" : "anatomy";
|
|
3218
|
+
case "request":
|
|
3219
|
+
return "request";
|
|
3220
|
+
}
|
|
3221
|
+
}
|
|
3164
3222
|
function DiffToggleButton({
|
|
3165
3223
|
active,
|
|
3166
3224
|
onClick
|
|
@@ -3358,6 +3416,19 @@ const LogEntry = reactExports.memo(function({
|
|
|
3358
3416
|
if (!expanded) return;
|
|
3359
3417
|
setActiveTab(anatomySegments !== null ? "anatomy" : "request");
|
|
3360
3418
|
}, [anatomySegments, expanded]);
|
|
3419
|
+
reactExports.useEffect(() => {
|
|
3420
|
+
const handleLogFocusRequest = (event) => {
|
|
3421
|
+
const request = readLogFocusRequest(event);
|
|
3422
|
+
if (request === null) return;
|
|
3423
|
+
if (request.logId !== log.id) return;
|
|
3424
|
+
setExpanded(true);
|
|
3425
|
+
setActiveTab(resolveFocusedTab(request.tab, anatomySegments));
|
|
3426
|
+
};
|
|
3427
|
+
window.addEventListener(LOG_FOCUS_REQUEST_EVENT, handleLogFocusRequest);
|
|
3428
|
+
return () => {
|
|
3429
|
+
window.removeEventListener(LOG_FOCUS_REQUEST_EVENT, handleLogFocusRequest);
|
|
3430
|
+
};
|
|
3431
|
+
}, [anatomySegments, log.id]);
|
|
3361
3432
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs(TooltipProvider, { children: [
|
|
3362
3433
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border border-border rounded-lg mb-1 overflow-hidden", children: [
|
|
3363
3434
|
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
@@ -3828,6 +3899,18 @@ const TurnGroup = reactExports.memo(function TurnGroup2({
|
|
|
3828
3899
|
}
|
|
3829
3900
|
prevCompleteRef.current = isComplete;
|
|
3830
3901
|
}, [collapsible, isComplete]);
|
|
3902
|
+
reactExports.useEffect(() => {
|
|
3903
|
+
const handleLogFocusRequest = (event) => {
|
|
3904
|
+
const request = readLogFocusRequest(event);
|
|
3905
|
+
if (request === null) return;
|
|
3906
|
+
if (!entries.some((entry) => entry.log.id === request.logId)) return;
|
|
3907
|
+
if (collapsible) setCollapsed(false);
|
|
3908
|
+
};
|
|
3909
|
+
window.addEventListener(LOG_FOCUS_REQUEST_EVENT, handleLogFocusRequest);
|
|
3910
|
+
return () => {
|
|
3911
|
+
window.removeEventListener(LOG_FOCUS_REQUEST_EVENT, handleLogFocusRequest);
|
|
3912
|
+
};
|
|
3913
|
+
}, [collapsible, entries]);
|
|
3831
3914
|
const toggleCollapse = reactExports.useCallback(() => {
|
|
3832
3915
|
if (collapsible) setCollapsed((prev) => !prev);
|
|
3833
3916
|
}, [collapsible]);
|
|
@@ -4836,6 +4919,19 @@ const ConversationGroup = reactExports.memo(function({
|
|
|
4836
4919
|
const isLoading = group.logs.some((log) => log.responseStatus === null);
|
|
4837
4920
|
const showTraceRollupMetrics = standalone && !hasPinnedSessionContext;
|
|
4838
4921
|
const turnGroups = reactExports.useMemo(() => buildTurnGroups(group.logs), [group.logs]);
|
|
4922
|
+
reactExports.useEffect(() => {
|
|
4923
|
+
const handleLogFocusRequest = (event) => {
|
|
4924
|
+
if (standalone) return;
|
|
4925
|
+
const request = readLogFocusRequest(event);
|
|
4926
|
+
if (request === null) return;
|
|
4927
|
+
if (!group.logs.some((log) => log.id === request.logId)) return;
|
|
4928
|
+
setExpanded(true);
|
|
4929
|
+
};
|
|
4930
|
+
window.addEventListener(LOG_FOCUS_REQUEST_EVENT, handleLogFocusRequest);
|
|
4931
|
+
return () => {
|
|
4932
|
+
window.removeEventListener(LOG_FOCUS_REQUEST_EVENT, handleLogFocusRequest);
|
|
4933
|
+
};
|
|
4934
|
+
}, [group.logs, standalone]);
|
|
4839
4935
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-2", children: [
|
|
4840
4936
|
!standalone && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
4841
4937
|
ConversationHeader,
|
|
@@ -5531,6 +5627,22 @@ function formatTimeAgo(isoString) {
|
|
|
5531
5627
|
const diffDay = Math.floor(diffHr / 24);
|
|
5532
5628
|
return `${diffDay}d ago`;
|
|
5533
5629
|
}
|
|
5630
|
+
function getModelSourceLink(sourceUrl) {
|
|
5631
|
+
if (sourceUrl === void 0 || sourceUrl.trim() === "") return null;
|
|
5632
|
+
try {
|
|
5633
|
+
const parsed = new URL(sourceUrl);
|
|
5634
|
+
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") return null;
|
|
5635
|
+
const hostname = parsed.hostname.toLowerCase();
|
|
5636
|
+
const isHuggingFace = hostname === "huggingface.co" || hostname.endsWith(".huggingface.co");
|
|
5637
|
+
return {
|
|
5638
|
+
label: isHuggingFace ? "HF" : "Source",
|
|
5639
|
+
title: isHuggingFace ? "Open Hugging Face model source" : "Open model metadata source",
|
|
5640
|
+
url: sourceUrl
|
|
5641
|
+
};
|
|
5642
|
+
} catch {
|
|
5643
|
+
return null;
|
|
5644
|
+
}
|
|
5645
|
+
}
|
|
5534
5646
|
function ProviderCard({
|
|
5535
5647
|
provider,
|
|
5536
5648
|
testResults,
|
|
@@ -5592,13 +5704,28 @@ function ProviderCard({
|
|
|
5592
5704
|
] }),
|
|
5593
5705
|
provider.models !== void 0 && provider.models.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex flex-wrap gap-1", children: provider.models.map((m) => {
|
|
5594
5706
|
const metadata = findProviderModelMetadata(provider, m);
|
|
5707
|
+
const sourceLink = getModelSourceLink(metadata?.sourceUrl);
|
|
5595
5708
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
5596
5709
|
"span",
|
|
5597
5710
|
{
|
|
5598
5711
|
className: "inline-flex items-center gap-1 text-xs px-1.5 py-0.5 rounded bg-muted text-muted-foreground",
|
|
5599
5712
|
children: [
|
|
5600
5713
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: m }),
|
|
5601
|
-
metadata !== null && metadata.contextWindow !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono text-foreground/80", children:
|
|
5714
|
+
metadata !== null && metadata.contextWindow !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono text-foreground/80", children: formatContextWindowTokens(metadata.contextWindow) }),
|
|
5715
|
+
sourceLink !== null && /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
|
|
5716
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
5717
|
+
"a",
|
|
5718
|
+
{
|
|
5719
|
+
href: sourceLink.url,
|
|
5720
|
+
target: "_blank",
|
|
5721
|
+
rel: "noopener noreferrer",
|
|
5722
|
+
className: "inline-flex items-center gap-0.5 rounded border border-border/70 bg-background/70 px-1 font-mono text-[10px] leading-4 text-muted-foreground transition-colors hover:text-foreground",
|
|
5723
|
+
"aria-label": sourceLink.title,
|
|
5724
|
+
children: sourceLink.label
|
|
5725
|
+
}
|
|
5726
|
+
) }),
|
|
5727
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: sourceLink.title })
|
|
5728
|
+
] }) })
|
|
5602
5729
|
]
|
|
5603
5730
|
},
|
|
5604
5731
|
m
|
|
@@ -5782,7 +5909,35 @@ const MINIMAX_MODELS = [
|
|
|
5782
5909
|
"MiniMax M2.1-highspeed",
|
|
5783
5910
|
"MiniMax M2"
|
|
5784
5911
|
];
|
|
5785
|
-
const ALIBABA_MODELS = ["glm-5", "glm-5.1", "qwen3.6-plus", "qwen3.7-max"];
|
|
5912
|
+
const ALIBABA_MODELS = ["glm-5", "glm-5.1", "glm-5.2", "qwen3.6-plus", "qwen3.7-max"];
|
|
5913
|
+
function emptyMetadataDraft() {
|
|
5914
|
+
return { contextWindow: "", outputLimit: "" };
|
|
5915
|
+
}
|
|
5916
|
+
function normalizeModelName(value) {
|
|
5917
|
+
return value.trim().toLowerCase().replace(/\s+/g, "-");
|
|
5918
|
+
}
|
|
5919
|
+
function findModelMetadata(provider, model) {
|
|
5920
|
+
const normalized = normalizeModelName(model);
|
|
5921
|
+
return provider?.modelMetadata?.find(
|
|
5922
|
+
(metadata) => normalizeModelName(metadata.model) === normalized
|
|
5923
|
+
) ?? null;
|
|
5924
|
+
}
|
|
5925
|
+
function createMetadataDrafts(provider, models) {
|
|
5926
|
+
return models.map((model) => {
|
|
5927
|
+
const metadata = findModelMetadata(provider, model);
|
|
5928
|
+
return {
|
|
5929
|
+
contextWindow: metadata?.contextWindow?.toString() ?? "",
|
|
5930
|
+
outputLimit: metadata?.outputLimit?.toString() ?? ""
|
|
5931
|
+
};
|
|
5932
|
+
});
|
|
5933
|
+
}
|
|
5934
|
+
function readPositiveIntegerInput(value) {
|
|
5935
|
+
const trimmed = value.trim();
|
|
5936
|
+
if (trimmed === "") return null;
|
|
5937
|
+
const parsed = Number(trimmed);
|
|
5938
|
+
if (!Number.isInteger(parsed) || parsed <= 0) return null;
|
|
5939
|
+
return parsed;
|
|
5940
|
+
}
|
|
5786
5941
|
function ProviderForm({ provider, onSubmit, onCancel }) {
|
|
5787
5942
|
const [name, setName] = reactExports.useState(provider?.name ?? "");
|
|
5788
5943
|
const [apiKey, setApiKey] = reactExports.useState(provider?.apiKey ?? "");
|
|
@@ -5792,6 +5947,12 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
|
|
|
5792
5947
|
const [models, setModels] = reactExports.useState(
|
|
5793
5948
|
initialModels !== void 0 && initialModels.length > 0 ? initialModels : [""]
|
|
5794
5949
|
);
|
|
5950
|
+
const [modelMetadataDrafts, setModelMetadataDrafts] = reactExports.useState(
|
|
5951
|
+
() => createMetadataDrafts(
|
|
5952
|
+
provider,
|
|
5953
|
+
initialModels !== void 0 && initialModels.length > 0 ? initialModels : [""]
|
|
5954
|
+
)
|
|
5955
|
+
);
|
|
5795
5956
|
const [activeTab, setActiveTab] = reactExports.useState("anthropic");
|
|
5796
5957
|
const [anthropicBaseUrl, setAnthropicBaseUrl] = reactExports.useState(provider?.anthropicBaseUrl ?? "");
|
|
5797
5958
|
const [openaiBaseUrl, setOpenaiBaseUrl] = reactExports.useState(provider?.openaiBaseUrl ?? "");
|
|
@@ -5830,7 +5991,9 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
|
|
|
5830
5991
|
if (provider) {
|
|
5831
5992
|
setName(provider.name);
|
|
5832
5993
|
setApiKey(provider.apiKey);
|
|
5833
|
-
|
|
5994
|
+
const nextModels = (provider.models?.length ?? 0) > 0 ? provider.models : [""];
|
|
5995
|
+
setModels(nextModels);
|
|
5996
|
+
setModelMetadataDrafts(createMetadataDrafts(provider, nextModels));
|
|
5834
5997
|
setAnthropicBaseUrl(provider.anthropicBaseUrl ?? "");
|
|
5835
5998
|
setOpenaiBaseUrl(provider.openaiBaseUrl ?? "");
|
|
5836
5999
|
setApiDocsUrl(provider.apiDocsUrl ?? "");
|
|
@@ -5882,6 +6045,19 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
|
|
|
5882
6045
|
if (modelMetadataUrl.trim() && !isValidUrl(modelMetadataUrl.trim())) {
|
|
5883
6046
|
newErrors.modelMetadataUrl = "Invalid URL format";
|
|
5884
6047
|
}
|
|
6048
|
+
modelMetadataDrafts.forEach((draft, index) => {
|
|
6049
|
+
const contextWindow = readPositiveIntegerInput(draft.contextWindow);
|
|
6050
|
+
const outputLimit = readPositiveIntegerInput(draft.outputLimit);
|
|
6051
|
+
if (draft.contextWindow.trim() !== "" && contextWindow === null) {
|
|
6052
|
+
newErrors[`modelMetadata.${index}.contextWindow`] = "Context window must be a positive integer";
|
|
6053
|
+
}
|
|
6054
|
+
if (draft.outputLimit.trim() !== "" && outputLimit === null) {
|
|
6055
|
+
newErrors[`modelMetadata.${index}.outputLimit`] = "Output limit must be a positive integer";
|
|
6056
|
+
}
|
|
6057
|
+
if (contextWindow !== null && outputLimit !== null && outputLimit > contextWindow) {
|
|
6058
|
+
newErrors[`modelMetadata.${index}.outputLimit`] = "Output limit should not exceed context window";
|
|
6059
|
+
}
|
|
6060
|
+
});
|
|
5885
6061
|
if (!anthropicBaseUrl.trim() && !openaiBaseUrl.trim()) {
|
|
5886
6062
|
newErrors.format = "At least one format URL (Anthropic or OpenAI) is required";
|
|
5887
6063
|
}
|
|
@@ -5896,6 +6072,55 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
|
|
|
5896
6072
|
return false;
|
|
5897
6073
|
}
|
|
5898
6074
|
}
|
|
6075
|
+
function updateModel(index, value) {
|
|
6076
|
+
setModels((prev) => {
|
|
6077
|
+
const next = [...prev];
|
|
6078
|
+
next[index] = value;
|
|
6079
|
+
return next;
|
|
6080
|
+
});
|
|
6081
|
+
}
|
|
6082
|
+
function updateMetadataDraft(index, patch) {
|
|
6083
|
+
setModelMetadataDrafts((prev) => {
|
|
6084
|
+
const next = [...prev];
|
|
6085
|
+
next[index] = { ...next[index] ?? emptyMetadataDraft(), ...patch };
|
|
6086
|
+
return next;
|
|
6087
|
+
});
|
|
6088
|
+
}
|
|
6089
|
+
function removeModel(index) {
|
|
6090
|
+
setModels((prev) => prev.filter((_, idx) => idx !== index));
|
|
6091
|
+
setModelMetadataDrafts((prev) => prev.filter((_, idx) => idx !== index));
|
|
6092
|
+
}
|
|
6093
|
+
function addModel() {
|
|
6094
|
+
setModels((prev) => [...prev, ""]);
|
|
6095
|
+
setModelMetadataDrafts((prev) => [...prev, emptyMetadataDraft()]);
|
|
6096
|
+
}
|
|
6097
|
+
function buildModelMetadata() {
|
|
6098
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
6099
|
+
const metadata = [];
|
|
6100
|
+
models.forEach((model, index) => {
|
|
6101
|
+
const trimmedModel = model.trim();
|
|
6102
|
+
if (trimmedModel === "") return;
|
|
6103
|
+
const draft = modelMetadataDrafts[index] ?? emptyMetadataDraft();
|
|
6104
|
+
const contextWindow = readPositiveIntegerInput(draft.contextWindow);
|
|
6105
|
+
const outputLimit = readPositiveIntegerInput(draft.outputLimit);
|
|
6106
|
+
if (contextWindow === null && outputLimit === null) return;
|
|
6107
|
+
const existing = findModelMetadata(provider, trimmedModel);
|
|
6108
|
+
if (existing !== null && existing.contextWindow === (contextWindow ?? void 0) && existing.outputLimit === (outputLimit ?? void 0)) {
|
|
6109
|
+
metadata.push({ ...existing, model: trimmedModel });
|
|
6110
|
+
return;
|
|
6111
|
+
}
|
|
6112
|
+
const entry = {
|
|
6113
|
+
model: trimmedModel,
|
|
6114
|
+
source: "manual",
|
|
6115
|
+
updatedAt: now
|
|
6116
|
+
};
|
|
6117
|
+
if (contextWindow !== null) entry.contextWindow = contextWindow;
|
|
6118
|
+
if (outputLimit !== null) entry.outputLimit = outputLimit;
|
|
6119
|
+
metadata.push(entry);
|
|
6120
|
+
});
|
|
6121
|
+
if (metadata.length > 0) return metadata;
|
|
6122
|
+
return provider?.modelMetadata !== void 0 ? [] : void 0;
|
|
6123
|
+
}
|
|
5899
6124
|
function handleSubmit(e) {
|
|
5900
6125
|
e.preventDefault();
|
|
5901
6126
|
if (!validate()) return;
|
|
@@ -5909,6 +6134,7 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
|
|
|
5909
6134
|
openaiBaseUrl: openaiBaseUrl.trim() || void 0,
|
|
5910
6135
|
apiDocsUrl: apiDocsUrl.trim() || void 0,
|
|
5911
6136
|
modelMetadataUrl: modelMetadataUrl.trim() || void 0,
|
|
6137
|
+
modelMetadata: buildModelMetadata(),
|
|
5912
6138
|
source
|
|
5913
6139
|
});
|
|
5914
6140
|
} finally {
|
|
@@ -6021,98 +6247,129 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
|
|
|
6021
6247
|
"Models ",
|
|
6022
6248
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-destructive", children: "*" })
|
|
6023
6249
|
] }),
|
|
6024
|
-
models.map((m, i) =>
|
|
6025
|
-
|
|
6026
|
-
{
|
|
6027
|
-
|
|
6028
|
-
|
|
6029
|
-
|
|
6030
|
-
|
|
6031
|
-
|
|
6032
|
-
|
|
6033
|
-
|
|
6034
|
-
|
|
6035
|
-
|
|
6036
|
-
|
|
6037
|
-
|
|
6038
|
-
|
|
6039
|
-
|
|
6040
|
-
|
|
6041
|
-
|
|
6042
|
-
|
|
6043
|
-
|
|
6044
|
-
|
|
6045
|
-
|
|
6046
|
-
|
|
6047
|
-
|
|
6048
|
-
|
|
6049
|
-
|
|
6050
|
-
|
|
6051
|
-
|
|
6052
|
-
|
|
6053
|
-
|
|
6054
|
-
|
|
6055
|
-
|
|
6056
|
-
|
|
6057
|
-
|
|
6058
|
-
|
|
6059
|
-
|
|
6060
|
-
|
|
6250
|
+
models.map((m, i) => {
|
|
6251
|
+
const draft = modelMetadataDrafts[i] ?? emptyMetadataDraft();
|
|
6252
|
+
const contextWindowError = errors[`modelMetadata.${i}.contextWindow`];
|
|
6253
|
+
const outputLimitError = errors[`modelMetadata.${i}.outputLimit`];
|
|
6254
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
6255
|
+
"div",
|
|
6256
|
+
{
|
|
6257
|
+
ref: (el) => {
|
|
6258
|
+
modelRowRefs.current[i] = el;
|
|
6259
|
+
},
|
|
6260
|
+
className: "space-y-2 rounded-md border border-border bg-muted/10 p-2",
|
|
6261
|
+
children: [
|
|
6262
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
6263
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative flex-1", children: [
|
|
6264
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
6265
|
+
"input",
|
|
6266
|
+
{
|
|
6267
|
+
type: "text",
|
|
6268
|
+
value: m,
|
|
6269
|
+
onChange: (e) => updateModel(i, e.target.value),
|
|
6270
|
+
placeholder: isMiniMax || isAlibaba ? "Type or select a model..." : "Model name",
|
|
6271
|
+
className: "w-full rounded-md border border-input bg-background px-4 py-3 pr-8 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:border-ring focus-visible:outline-ring focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50"
|
|
6272
|
+
}
|
|
6273
|
+
),
|
|
6274
|
+
(isMiniMax || isAlibaba) && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
6275
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
6276
|
+
"button",
|
|
6277
|
+
{
|
|
6278
|
+
type: "button",
|
|
6279
|
+
onClick: () => setOpenModelDropdown(openModelDropdown === i ? null : i),
|
|
6280
|
+
className: "absolute right-1 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground transition-colors p-1 z-10",
|
|
6281
|
+
"aria-label": "Show model suggestions",
|
|
6282
|
+
children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "size-4" })
|
|
6283
|
+
}
|
|
6284
|
+
),
|
|
6285
|
+
openModelDropdown === i && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute left-0 right-0 top-full mt-1 z-50 bg-popover border border-border rounded-md shadow-md max-h-48 overflow-y-auto", children: (isMiniMax ? MINIMAX_MODELS : ALIBABA_MODELS).map((opt) => /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
6286
|
+
"button",
|
|
6287
|
+
{
|
|
6288
|
+
type: "button",
|
|
6289
|
+
onClick: () => {
|
|
6290
|
+
updateModel(i, opt);
|
|
6291
|
+
setOpenModelDropdown(null);
|
|
6292
|
+
},
|
|
6293
|
+
className: "w-full text-left px-3 py-2 text-sm hover:bg-muted transition-colors",
|
|
6294
|
+
children: opt
|
|
6295
|
+
},
|
|
6296
|
+
opt
|
|
6297
|
+
)) })
|
|
6298
|
+
] })
|
|
6299
|
+
] }),
|
|
6300
|
+
models.length > 1 && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
6061
6301
|
"button",
|
|
6062
6302
|
{
|
|
6063
6303
|
type: "button",
|
|
6064
|
-
onClick: () =>
|
|
6065
|
-
|
|
6066
|
-
|
|
6067
|
-
|
|
6068
|
-
|
|
6069
|
-
|
|
6070
|
-
|
|
6071
|
-
|
|
6072
|
-
|
|
6073
|
-
|
|
6074
|
-
|
|
6075
|
-
|
|
6076
|
-
|
|
6077
|
-
|
|
6078
|
-
|
|
6079
|
-
|
|
6080
|
-
|
|
6081
|
-
|
|
6082
|
-
|
|
6083
|
-
|
|
6084
|
-
|
|
6085
|
-
|
|
6086
|
-
children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
6087
|
-
"svg",
|
|
6088
|
-
{
|
|
6089
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
6090
|
-
width: "16",
|
|
6091
|
-
height: "16",
|
|
6092
|
-
viewBox: "0 0 24 24",
|
|
6093
|
-
fill: "none",
|
|
6094
|
-
stroke: "currentColor",
|
|
6095
|
-
strokeWidth: "2",
|
|
6096
|
-
strokeLinecap: "round",
|
|
6097
|
-
strokeLinejoin: "round",
|
|
6098
|
-
children: [
|
|
6099
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M3 6h18" }),
|
|
6100
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6" }),
|
|
6101
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2" })
|
|
6102
|
-
]
|
|
6304
|
+
onClick: () => removeModel(i),
|
|
6305
|
+
className: "text-muted-foreground hover:text-destructive transition-colors p-1 shrink-0",
|
|
6306
|
+
"aria-label": "Remove model",
|
|
6307
|
+
children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
6308
|
+
"svg",
|
|
6309
|
+
{
|
|
6310
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
6311
|
+
width: "16",
|
|
6312
|
+
height: "16",
|
|
6313
|
+
viewBox: "0 0 24 24",
|
|
6314
|
+
fill: "none",
|
|
6315
|
+
stroke: "currentColor",
|
|
6316
|
+
strokeWidth: "2",
|
|
6317
|
+
strokeLinecap: "round",
|
|
6318
|
+
strokeLinejoin: "round",
|
|
6319
|
+
children: [
|
|
6320
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M3 6h18" }),
|
|
6321
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6" }),
|
|
6322
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2" })
|
|
6323
|
+
]
|
|
6324
|
+
}
|
|
6325
|
+
)
|
|
6103
6326
|
}
|
|
6104
6327
|
)
|
|
6105
|
-
}
|
|
6106
|
-
|
|
6107
|
-
|
|
6108
|
-
|
|
6109
|
-
|
|
6110
|
-
|
|
6328
|
+
] }),
|
|
6329
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid gap-2 sm:grid-cols-2", children: [
|
|
6330
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "space-y-1 text-xs text-muted-foreground", children: [
|
|
6331
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Context Window" }),
|
|
6332
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
6333
|
+
"input",
|
|
6334
|
+
{
|
|
6335
|
+
type: "number",
|
|
6336
|
+
min: 1,
|
|
6337
|
+
step: 1e3,
|
|
6338
|
+
value: draft.contextWindow,
|
|
6339
|
+
onChange: (e) => updateMetadataDraft(i, { contextWindow: e.target.value }),
|
|
6340
|
+
placeholder: "e.g. 1048576",
|
|
6341
|
+
className: "w-full rounded-md border border-input bg-background px-3 py-2 font-mono text-xs text-foreground ring-offset-background placeholder:text-muted-foreground focus-visible:border-ring focus-visible:outline-ring focus-visible:ring-[3px]"
|
|
6342
|
+
}
|
|
6343
|
+
),
|
|
6344
|
+
contextWindowError !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "block text-destructive", children: contextWindowError })
|
|
6345
|
+
] }),
|
|
6346
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "space-y-1 text-xs text-muted-foreground", children: [
|
|
6347
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Output Limit" }),
|
|
6348
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
6349
|
+
"input",
|
|
6350
|
+
{
|
|
6351
|
+
type: "number",
|
|
6352
|
+
min: 1,
|
|
6353
|
+
step: 1e3,
|
|
6354
|
+
value: draft.outputLimit,
|
|
6355
|
+
onChange: (e) => updateMetadataDraft(i, { outputLimit: e.target.value }),
|
|
6356
|
+
placeholder: "e.g. 8192",
|
|
6357
|
+
className: "w-full rounded-md border border-input bg-background px-3 py-2 font-mono text-xs text-foreground ring-offset-background placeholder:text-muted-foreground focus-visible:border-ring focus-visible:outline-ring focus-visible:ring-[3px]"
|
|
6358
|
+
}
|
|
6359
|
+
),
|
|
6360
|
+
outputLimitError !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "block text-destructive", children: outputLimitError })
|
|
6361
|
+
] })
|
|
6362
|
+
] })
|
|
6363
|
+
]
|
|
6364
|
+
},
|
|
6365
|
+
i
|
|
6366
|
+
);
|
|
6367
|
+
}),
|
|
6111
6368
|
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
6112
6369
|
"button",
|
|
6113
6370
|
{
|
|
6114
6371
|
type: "button",
|
|
6115
|
-
onClick:
|
|
6372
|
+
onClick: addModel,
|
|
6116
6373
|
className: "text-xs text-primary hover:underline flex items-center gap-1",
|
|
6117
6374
|
children: "+ Add Model"
|
|
6118
6375
|
}
|
|
@@ -6234,6 +6491,7 @@ function createProviderPayload(data) {
|
|
|
6234
6491
|
openaiBaseUrl: (data.openaiBaseUrl?.length ?? 0) > 0 ? data.openaiBaseUrl : void 0,
|
|
6235
6492
|
apiDocsUrl: (data.apiDocsUrl?.length ?? 0) > 0 ? data.apiDocsUrl : void 0,
|
|
6236
6493
|
modelMetadataUrl: (data.modelMetadataUrl?.length ?? 0) > 0 ? data.modelMetadataUrl : void 0,
|
|
6494
|
+
modelMetadata: data.modelMetadata,
|
|
6237
6495
|
source: data.source
|
|
6238
6496
|
};
|
|
6239
6497
|
}
|
|
@@ -6771,30 +7029,6 @@ function ProvidersPanel({
|
|
|
6771
7029
|
)
|
|
6772
7030
|
] });
|
|
6773
7031
|
}
|
|
6774
|
-
async function fetcher(url) {
|
|
6775
|
-
return fetchJson(
|
|
6776
|
-
url,
|
|
6777
|
-
ProviderConfigSchema.array(),
|
|
6778
|
-
void 0,
|
|
6779
|
-
(response) => `Failed to fetch ${url}: ${response.status}`
|
|
6780
|
-
);
|
|
6781
|
-
}
|
|
6782
|
-
function useProviders() {
|
|
6783
|
-
const response = useSWR(
|
|
6784
|
-
"/api/providers",
|
|
6785
|
-
fetcher,
|
|
6786
|
-
{
|
|
6787
|
-
revalidateOnFocus: false,
|
|
6788
|
-
revalidateIfStale: false
|
|
6789
|
-
}
|
|
6790
|
-
);
|
|
6791
|
-
return {
|
|
6792
|
-
providers: response.data ?? [],
|
|
6793
|
-
isLoading: response.isLoading,
|
|
6794
|
-
isError: response.error,
|
|
6795
|
-
mutate: response.mutate
|
|
6796
|
-
};
|
|
6797
|
-
}
|
|
6798
7032
|
function SettingsDialog() {
|
|
6799
7033
|
const [open, setOpen] = reactExports.useState(false);
|
|
6800
7034
|
const [activeTab, setActiveTab] = reactExports.useState("providers");
|
|
@@ -7284,6 +7518,851 @@ function useKeyboardNavigation(containerRef, wrapperRef, options = {}) {
|
|
|
7284
7518
|
return () => document.removeEventListener("focusin", handleFocusContainer);
|
|
7285
7519
|
}, [handleFocusContainer]);
|
|
7286
7520
|
}
|
|
7521
|
+
const ANATOMY_ROLE_LABELS = {
|
|
7522
|
+
system: "System",
|
|
7523
|
+
user: "User",
|
|
7524
|
+
assistant: "Assistant",
|
|
7525
|
+
tool: "Tool Results",
|
|
7526
|
+
tools: "Tool Definitions"
|
|
7527
|
+
};
|
|
7528
|
+
const ROLE_COLOR_CLASSES = {
|
|
7529
|
+
system: "bg-sky-500/70",
|
|
7530
|
+
user: "bg-emerald-500/70",
|
|
7531
|
+
assistant: "bg-violet-500/70",
|
|
7532
|
+
tool: "bg-sky-400/55",
|
|
7533
|
+
tools: "bg-slate-500/70"
|
|
7534
|
+
};
|
|
7535
|
+
const ROLE_FOCUS_RING = {
|
|
7536
|
+
system: "focus-visible:ring-sky-300",
|
|
7537
|
+
user: "focus-visible:ring-emerald-300",
|
|
7538
|
+
assistant: "focus-visible:ring-violet-300",
|
|
7539
|
+
tool: "focus-visible:ring-sky-300",
|
|
7540
|
+
tools: "focus-visible:ring-slate-300"
|
|
7541
|
+
};
|
|
7542
|
+
const MAX_VISIBLE_SEGMENTS = 12;
|
|
7543
|
+
const MIN_SEGMENT_PERCENT = 1;
|
|
7544
|
+
const TOOLTIP_PREVIEW_LIMIT = 80;
|
|
7545
|
+
const LABEL_TRUNCATE_LIMIT = 24;
|
|
7546
|
+
function truncateLabel(label) {
|
|
7547
|
+
if (label.length <= LABEL_TRUNCATE_LIMIT) return label;
|
|
7548
|
+
return `${label.slice(0, LABEL_TRUNCATE_LIMIT - 3)}...`;
|
|
7549
|
+
}
|
|
7550
|
+
function truncatePreview(text) {
|
|
7551
|
+
const singleLine = text.replace(/\s+/g, " ").trim();
|
|
7552
|
+
if (singleLine.length <= TOOLTIP_PREVIEW_LIMIT) return singleLine;
|
|
7553
|
+
return `${singleLine.slice(0, TOOLTIP_PREVIEW_LIMIT)}...`;
|
|
7554
|
+
}
|
|
7555
|
+
function formatPercent$1(value) {
|
|
7556
|
+
if (value >= 10) return `${value.toFixed(0)}%`;
|
|
7557
|
+
if (value >= 1) return `${value.toFixed(1)}%`;
|
|
7558
|
+
if (value > 0) return "<1%";
|
|
7559
|
+
return "0%";
|
|
7560
|
+
}
|
|
7561
|
+
const SegmentBar = reactExports.memo(function SegmentBar2({
|
|
7562
|
+
segments,
|
|
7563
|
+
totalTokens,
|
|
7564
|
+
showLabels = true,
|
|
7565
|
+
onActivate
|
|
7566
|
+
}) {
|
|
7567
|
+
const total = reactExports.useMemo(() => {
|
|
7568
|
+
if (totalTokens > 0) return totalTokens;
|
|
7569
|
+
return segments.reduce((sum, segment2) => sum + segment2.size, 0);
|
|
7570
|
+
}, [segments, totalTokens]);
|
|
7571
|
+
const visibleSegments = segments.slice(0, MAX_VISIBLE_SEGMENTS);
|
|
7572
|
+
const overflowSegments = segments.slice(MAX_VISIBLE_SEGMENTS);
|
|
7573
|
+
const overflowSize = overflowSegments.reduce((sum, segment2) => sum + segment2.size, 0);
|
|
7574
|
+
const overflowCount = overflowSegments.length;
|
|
7575
|
+
const overflowStartIndex = MAX_VISIBLE_SEGMENTS;
|
|
7576
|
+
const overflowEndIndex = overflowStartIndex + overflowCount - 1;
|
|
7577
|
+
const hasOverflow = overflowCount > 0;
|
|
7578
|
+
const interactive = onActivate !== void 0;
|
|
7579
|
+
const visibleTotal = reactExports.useMemo(
|
|
7580
|
+
() => visibleSegments.reduce((sum, segment2) => sum + segment2.size, 0),
|
|
7581
|
+
[visibleSegments]
|
|
7582
|
+
);
|
|
7583
|
+
const ariaLabel = reactExports.useMemo(
|
|
7584
|
+
() => `Request context: ~${formatTokens(total)} tokens across ${segments.length} segment${segments.length === 1 ? "" : "s"}`,
|
|
7585
|
+
[segments.length, total]
|
|
7586
|
+
);
|
|
7587
|
+
if (segments.length === 0 || total <= 0) {
|
|
7588
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { role: "img", "aria-label": ariaLabel, className: "h-6" });
|
|
7589
|
+
}
|
|
7590
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipProvider, { delayDuration: 150, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { role: "img", "aria-label": ariaLabel, className: "flex flex-col gap-1.5", children: [
|
|
7591
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
7592
|
+
"div",
|
|
7593
|
+
{
|
|
7594
|
+
className: "relative flex h-6 w-full overflow-hidden rounded border border-border/40 bg-muted/30",
|
|
7595
|
+
"data-testid": "anatomy-segment-bar",
|
|
7596
|
+
children: [
|
|
7597
|
+
visibleSegments.map((segment2, index) => {
|
|
7598
|
+
const rawPercent = total > 0 ? segment2.size / total * 100 : 0;
|
|
7599
|
+
const percent = Math.max(MIN_SEGMENT_PERCENT, rawPercent);
|
|
7600
|
+
const segmentClassName = cn(
|
|
7601
|
+
"h-full border-r border-background/80 last:border-r-0",
|
|
7602
|
+
interactive ? "opacity-90 hover:opacity-100 focus:opacity-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-1 focus-visible:ring-offset-background" : "opacity-90",
|
|
7603
|
+
ROLE_COLOR_CLASSES[segment2.role],
|
|
7604
|
+
interactive ? ROLE_FOCUS_RING[segment2.role] : ""
|
|
7605
|
+
);
|
|
7606
|
+
const segmentStyle = { width: `${percent}%` };
|
|
7607
|
+
const ariaText = `${segment2.label}, ${formatPercent$1(rawPercent)}, ~${formatTokens(
|
|
7608
|
+
segment2.size
|
|
7609
|
+
)} tokens`;
|
|
7610
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
|
|
7611
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: interactive ? /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
7612
|
+
"button",
|
|
7613
|
+
{
|
|
7614
|
+
type: "button",
|
|
7615
|
+
role: "button",
|
|
7616
|
+
tabIndex: 0,
|
|
7617
|
+
onClick: () => onActivate(segment2),
|
|
7618
|
+
onKeyDown: (event) => {
|
|
7619
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
7620
|
+
event.preventDefault();
|
|
7621
|
+
onActivate(segment2);
|
|
7622
|
+
}
|
|
7623
|
+
},
|
|
7624
|
+
"data-anatomy-path": segment2.path,
|
|
7625
|
+
"aria-label": ariaText,
|
|
7626
|
+
className: segmentClassName,
|
|
7627
|
+
style: segmentStyle
|
|
7628
|
+
}
|
|
7629
|
+
) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { "aria-label": ariaText, className: segmentClassName, style: segmentStyle }) }),
|
|
7630
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs(TooltipContent, { side: "bottom", className: "max-w-sm text-xs p-2 space-y-0.5", children: [
|
|
7631
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "font-semibold", children: [
|
|
7632
|
+
segment2.label,
|
|
7633
|
+
" - ",
|
|
7634
|
+
formatPercent$1(rawPercent),
|
|
7635
|
+
" - ~",
|
|
7636
|
+
formatTokens(segment2.size),
|
|
7637
|
+
" ",
|
|
7638
|
+
"tokens"
|
|
7639
|
+
] }),
|
|
7640
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-muted-foreground", children: ANATOMY_ROLE_LABELS[segment2.role] }),
|
|
7641
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-muted-foreground", children: [
|
|
7642
|
+
segment2.characters.toLocaleString(),
|
|
7643
|
+
" chars"
|
|
7644
|
+
] }),
|
|
7645
|
+
segment2.text.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-muted-foreground/90 break-words whitespace-pre-wrap", children: truncatePreview(segment2.text) })
|
|
7646
|
+
] })
|
|
7647
|
+
] }, `${segment2.path}-${index}`);
|
|
7648
|
+
}),
|
|
7649
|
+
hasOverflow && /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
|
|
7650
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
7651
|
+
"div",
|
|
7652
|
+
{
|
|
7653
|
+
role: "img",
|
|
7654
|
+
"aria-label": `${overflowCount} additional segments`,
|
|
7655
|
+
className: "flex h-full items-center justify-center bg-muted-foreground/30 px-1.5 text-[10px] font-mono text-background",
|
|
7656
|
+
style: {
|
|
7657
|
+
width: `${Math.max(MIN_SEGMENT_PERCENT, overflowSize / total * 100)}%`
|
|
7658
|
+
},
|
|
7659
|
+
children: [
|
|
7660
|
+
"... +",
|
|
7661
|
+
overflowCount
|
|
7662
|
+
]
|
|
7663
|
+
}
|
|
7664
|
+
) }),
|
|
7665
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs(TooltipContent, { side: "bottom", className: "text-xs", children: [
|
|
7666
|
+
overflowCount,
|
|
7667
|
+
" more segment",
|
|
7668
|
+
overflowCount === 1 ? "" : "s",
|
|
7669
|
+
" (indices",
|
|
7670
|
+
" ",
|
|
7671
|
+
overflowStartIndex,
|
|
7672
|
+
"-",
|
|
7673
|
+
overflowEndIndex,
|
|
7674
|
+
")"
|
|
7675
|
+
] })
|
|
7676
|
+
] })
|
|
7677
|
+
]
|
|
7678
|
+
}
|
|
7679
|
+
),
|
|
7680
|
+
showLabels && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex w-full gap-1 text-[10px] text-muted-foreground", children: [
|
|
7681
|
+
visibleSegments.map((segment2, index) => {
|
|
7682
|
+
const rawPercent = total > 0 ? segment2.size / total * 100 : 0;
|
|
7683
|
+
const percent = Math.max(MIN_SEGMENT_PERCENT, rawPercent);
|
|
7684
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
7685
|
+
"div",
|
|
7686
|
+
{
|
|
7687
|
+
className: "flex flex-col gap-0.5 truncate",
|
|
7688
|
+
style: { width: `${percent}%` },
|
|
7689
|
+
title: `${segment2.label} - ${formatPercent$1(rawPercent)} - ~${formatTokens(
|
|
7690
|
+
segment2.size
|
|
7691
|
+
)} tokens`,
|
|
7692
|
+
children: [
|
|
7693
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate font-mono text-foreground/80", children: truncateLabel(segment2.label) }),
|
|
7694
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "truncate font-mono text-muted-foreground/70", children: [
|
|
7695
|
+
formatPercent$1(rawPercent),
|
|
7696
|
+
" - ~",
|
|
7697
|
+
formatTokens(segment2.size)
|
|
7698
|
+
] })
|
|
7699
|
+
]
|
|
7700
|
+
},
|
|
7701
|
+
`label-${segment2.path}-${index}`
|
|
7702
|
+
);
|
|
7703
|
+
}),
|
|
7704
|
+
hasOverflow && /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
7705
|
+
"div",
|
|
7706
|
+
{
|
|
7707
|
+
className: "flex flex-col gap-0.5 truncate text-muted-foreground",
|
|
7708
|
+
style: { width: `${Math.max(MIN_SEGMENT_PERCENT, overflowSize / total * 100)}%` },
|
|
7709
|
+
children: [
|
|
7710
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "truncate font-mono text-foreground/60", children: [
|
|
7711
|
+
"... +",
|
|
7712
|
+
overflowCount
|
|
7713
|
+
] }),
|
|
7714
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "truncate font-mono text-muted-foreground/60", children: [
|
|
7715
|
+
"~",
|
|
7716
|
+
formatTokens(overflowSize)
|
|
7717
|
+
] })
|
|
7718
|
+
]
|
|
7719
|
+
}
|
|
7720
|
+
)
|
|
7721
|
+
] }),
|
|
7722
|
+
visibleTotal < total * 0.1 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-0" })
|
|
7723
|
+
] }) });
|
|
7724
|
+
});
|
|
7725
|
+
const WATCH_USAGE_PERCENT = 0.75;
|
|
7726
|
+
const DANGER_USAGE_PERCENT = 0.9;
|
|
7727
|
+
const LONG_TOOL_SCHEMA_TOKENS = 8e3;
|
|
7728
|
+
const LARGE_ROLE_PERCENT = 0.35;
|
|
7729
|
+
const DUPLICATE_MIN_TOKENS = 40;
|
|
7730
|
+
const DUPLICATE_MIN_CHARS = 160;
|
|
7731
|
+
const DUPLICATE_NESTED_MAX_DEPTH = 5;
|
|
7732
|
+
const DUPLICATE_NESTED_MAX_CANDIDATES_PER_SEGMENT = 8;
|
|
7733
|
+
const HISTORY_BLOAT_SEGMENTS = 18;
|
|
7734
|
+
const HISTORY_BLOAT_TOKENS = 6e4;
|
|
7735
|
+
const HISTORY_BLOAT_PERCENT = 0.7;
|
|
7736
|
+
const SYSTEM_HEAVY_TOKENS = 3e3;
|
|
7737
|
+
const SYSTEM_HEAVY_PERCENT = 0.3;
|
|
7738
|
+
const DUPLICATE_PREVIEW_CHARS = 160;
|
|
7739
|
+
const DUPLICATE_NESTED_FIELD_NAMES = /* @__PURE__ */ new Set([
|
|
7740
|
+
"answer",
|
|
7741
|
+
"body",
|
|
7742
|
+
"content",
|
|
7743
|
+
"data",
|
|
7744
|
+
"error",
|
|
7745
|
+
"message",
|
|
7746
|
+
"markdown",
|
|
7747
|
+
"output",
|
|
7748
|
+
"response",
|
|
7749
|
+
"result",
|
|
7750
|
+
"stderr",
|
|
7751
|
+
"stdout",
|
|
7752
|
+
"text",
|
|
7753
|
+
"transcript"
|
|
7754
|
+
]);
|
|
7755
|
+
const EXPLICIT_CONTEXT_WINDOW_FIELDS = [
|
|
7756
|
+
"context_window",
|
|
7757
|
+
"contextWindow",
|
|
7758
|
+
"context_length",
|
|
7759
|
+
"contextLength",
|
|
7760
|
+
"max_context_length",
|
|
7761
|
+
"maxContextLength",
|
|
7762
|
+
"max_input_tokens",
|
|
7763
|
+
"maxInputTokens"
|
|
7764
|
+
];
|
|
7765
|
+
const OUTPUT_RESERVE_FIELDS = [
|
|
7766
|
+
"max_tokens",
|
|
7767
|
+
"maxTokens",
|
|
7768
|
+
"max_completion_tokens",
|
|
7769
|
+
"maxCompletionTokens",
|
|
7770
|
+
"max_output_tokens",
|
|
7771
|
+
"maxOutputTokens"
|
|
7772
|
+
];
|
|
7773
|
+
const MODEL_CONTEXT_RULES = [
|
|
7774
|
+
{
|
|
7775
|
+
family: "OpenAI GPT-4.1",
|
|
7776
|
+
tokens: 1047576,
|
|
7777
|
+
mode: "prefix",
|
|
7778
|
+
patterns: ["gpt-4.1"]
|
|
7779
|
+
},
|
|
7780
|
+
{
|
|
7781
|
+
family: "OpenAI GPT-4o",
|
|
7782
|
+
tokens: 128e3,
|
|
7783
|
+
mode: "prefix",
|
|
7784
|
+
patterns: ["gpt-4o"]
|
|
7785
|
+
},
|
|
7786
|
+
{
|
|
7787
|
+
family: "OpenAI o-series",
|
|
7788
|
+
tokens: 128e3,
|
|
7789
|
+
mode: "prefix",
|
|
7790
|
+
patterns: ["o1", "o3", "o4"]
|
|
7791
|
+
},
|
|
7792
|
+
{
|
|
7793
|
+
family: "Anthropic Claude",
|
|
7794
|
+
tokens: 2e5,
|
|
7795
|
+
mode: "prefix",
|
|
7796
|
+
patterns: ["claude-"]
|
|
7797
|
+
},
|
|
7798
|
+
{
|
|
7799
|
+
family: "DeepSeek V4",
|
|
7800
|
+
tokens: 1048576,
|
|
7801
|
+
mode: "prefix",
|
|
7802
|
+
patterns: ["deepseek-v4-pro", "deepseek-v4-flash"]
|
|
7803
|
+
},
|
|
7804
|
+
{
|
|
7805
|
+
family: "DeepSeek",
|
|
7806
|
+
tokens: 64e3,
|
|
7807
|
+
mode: "prefix",
|
|
7808
|
+
patterns: ["deepseek-"]
|
|
7809
|
+
},
|
|
7810
|
+
{
|
|
7811
|
+
family: "MiniMax M3",
|
|
7812
|
+
tokens: 1048576,
|
|
7813
|
+
mode: "includes",
|
|
7814
|
+
patterns: ["minimax-m3"]
|
|
7815
|
+
},
|
|
7816
|
+
{
|
|
7817
|
+
family: "MiniMax M2.7",
|
|
7818
|
+
tokens: 204800,
|
|
7819
|
+
mode: "includes",
|
|
7820
|
+
patterns: ["minimax-m2.7"]
|
|
7821
|
+
},
|
|
7822
|
+
{
|
|
7823
|
+
family: "MiniMax M2.5/M2.1",
|
|
7824
|
+
tokens: 196608,
|
|
7825
|
+
mode: "includes",
|
|
7826
|
+
patterns: ["minimax-m2.5", "minimax-m2.1"]
|
|
7827
|
+
},
|
|
7828
|
+
{
|
|
7829
|
+
family: "GLM-5.2",
|
|
7830
|
+
tokens: 1048576,
|
|
7831
|
+
mode: "prefix",
|
|
7832
|
+
patterns: ["glm-5.2"]
|
|
7833
|
+
},
|
|
7834
|
+
{
|
|
7835
|
+
family: "GLM-5",
|
|
7836
|
+
tokens: 202752,
|
|
7837
|
+
mode: "prefix",
|
|
7838
|
+
patterns: ["glm-5", "glm-5.1"]
|
|
7839
|
+
}
|
|
7840
|
+
];
|
|
7841
|
+
const ROLE_LABELS = {
|
|
7842
|
+
system: "System",
|
|
7843
|
+
user: "User messages",
|
|
7844
|
+
assistant: "Assistant messages",
|
|
7845
|
+
tool: "Tool results",
|
|
7846
|
+
tools: "Tool definitions"
|
|
7847
|
+
};
|
|
7848
|
+
function readPositiveInteger(value) {
|
|
7849
|
+
if (typeof value === "number") {
|
|
7850
|
+
if (Number.isFinite(value) && value > 0) return Math.floor(value);
|
|
7851
|
+
return null;
|
|
7852
|
+
}
|
|
7853
|
+
if (typeof value === "string") {
|
|
7854
|
+
const parsed = Number(value);
|
|
7855
|
+
if (Number.isFinite(parsed) && parsed > 0) return Math.floor(parsed);
|
|
7856
|
+
}
|
|
7857
|
+
return null;
|
|
7858
|
+
}
|
|
7859
|
+
function readPositiveIntegerField(parsed, fields) {
|
|
7860
|
+
for (const field of fields) {
|
|
7861
|
+
const value = readPositiveInteger(safeGetOwnProperty(parsed, field));
|
|
7862
|
+
if (value !== null) return value;
|
|
7863
|
+
}
|
|
7864
|
+
return null;
|
|
7865
|
+
}
|
|
7866
|
+
function readRequestModel(parsed) {
|
|
7867
|
+
const value = safeGetOwnProperty(parsed, "model");
|
|
7868
|
+
if (typeof value !== "string") return null;
|
|
7869
|
+
const trimmed = value.trim();
|
|
7870
|
+
return trimmed === "" ? null : trimmed;
|
|
7871
|
+
}
|
|
7872
|
+
function matchesRule(model, rule) {
|
|
7873
|
+
switch (rule.mode) {
|
|
7874
|
+
case "prefix":
|
|
7875
|
+
return rule.patterns.some((pattern) => model.startsWith(pattern));
|
|
7876
|
+
case "includes":
|
|
7877
|
+
return rule.patterns.some((pattern) => model.includes(pattern));
|
|
7878
|
+
}
|
|
7879
|
+
}
|
|
7880
|
+
function resolveContextWindow(model, parsed, providers = []) {
|
|
7881
|
+
const explicitWindow = readPositiveIntegerField(parsed, EXPLICIT_CONTEXT_WINDOW_FIELDS);
|
|
7882
|
+
if (explicitWindow !== null) {
|
|
7883
|
+
return {
|
|
7884
|
+
tokens: explicitWindow,
|
|
7885
|
+
label: "Request metadata",
|
|
7886
|
+
source: "request"
|
|
7887
|
+
};
|
|
7888
|
+
}
|
|
7889
|
+
const providerWindow = resolveProviderContextWindow(model, providers);
|
|
7890
|
+
if (providerWindow !== null) {
|
|
7891
|
+
return {
|
|
7892
|
+
tokens: providerWindow.tokens,
|
|
7893
|
+
label: `${providerWindow.providerName} config`,
|
|
7894
|
+
source: "provider"
|
|
7895
|
+
};
|
|
7896
|
+
}
|
|
7897
|
+
if (model === null) {
|
|
7898
|
+
return {
|
|
7899
|
+
tokens: null,
|
|
7900
|
+
label: "Unknown model",
|
|
7901
|
+
source: "unknown"
|
|
7902
|
+
};
|
|
7903
|
+
}
|
|
7904
|
+
const normalized = model.trim().toLowerCase().replace(/\s+/g, "-");
|
|
7905
|
+
for (const rule of MODEL_CONTEXT_RULES) {
|
|
7906
|
+
if (matchesRule(normalized, rule)) {
|
|
7907
|
+
return {
|
|
7908
|
+
tokens: rule.tokens,
|
|
7909
|
+
label: `${rule.family} local rule`,
|
|
7910
|
+
source: "model-rule"
|
|
7911
|
+
};
|
|
7912
|
+
}
|
|
7913
|
+
}
|
|
7914
|
+
return {
|
|
7915
|
+
tokens: null,
|
|
7916
|
+
label: "No local rule",
|
|
7917
|
+
source: "unknown"
|
|
7918
|
+
};
|
|
7919
|
+
}
|
|
7920
|
+
function clampPercent(value) {
|
|
7921
|
+
if (value < 0) return 0;
|
|
7922
|
+
if (value > 1) return 1;
|
|
7923
|
+
return value;
|
|
7924
|
+
}
|
|
7925
|
+
function riskLevel(usagePercent) {
|
|
7926
|
+
if (usagePercent === null) return "unknown";
|
|
7927
|
+
if (usagePercent >= DANGER_USAGE_PERCENT) return "danger";
|
|
7928
|
+
if (usagePercent >= WATCH_USAGE_PERCENT) return "watch";
|
|
7929
|
+
return "ok";
|
|
7930
|
+
}
|
|
7931
|
+
function sumSegments(segments, role) {
|
|
7932
|
+
return segments.filter((segment2) => segment2.role === role).reduce((sum, segment2) => sum + segment2.size, 0);
|
|
7933
|
+
}
|
|
7934
|
+
function buildRoleUsages(segments, total) {
|
|
7935
|
+
const roles = ["system", "user", "assistant", "tool", "tools"];
|
|
7936
|
+
return roles.map((role) => {
|
|
7937
|
+
const tokens = sumSegments(segments, role);
|
|
7938
|
+
return {
|
|
7939
|
+
role,
|
|
7940
|
+
label: ROLE_LABELS[role],
|
|
7941
|
+
tokens,
|
|
7942
|
+
percent: total > 0 ? tokens / total : 0
|
|
7943
|
+
};
|
|
7944
|
+
}).filter((usage) => usage.tokens > 0).sort((a, b) => b.tokens - a.tokens);
|
|
7945
|
+
}
|
|
7946
|
+
function normalizeDuplicateText(text) {
|
|
7947
|
+
return text.replace(/\s+/g, " ").trim().toLowerCase();
|
|
7948
|
+
}
|
|
7949
|
+
function duplicatePreview(text) {
|
|
7950
|
+
const singleLine = text.replace(/\s+/g, " ").trim();
|
|
7951
|
+
if (singleLine.length <= DUPLICATE_PREVIEW_CHARS) return singleLine;
|
|
7952
|
+
return `${singleLine.slice(0, DUPLICATE_PREVIEW_CHARS - 3)}...`;
|
|
7953
|
+
}
|
|
7954
|
+
function duplicateSegmentRef(candidate) {
|
|
7955
|
+
return {
|
|
7956
|
+
role: candidate.role,
|
|
7957
|
+
label: candidate.label,
|
|
7958
|
+
path: candidate.path,
|
|
7959
|
+
tokens: candidate.tokens,
|
|
7960
|
+
characters: candidate.characters
|
|
7961
|
+
};
|
|
7962
|
+
}
|
|
7963
|
+
function duplicateCandidateFromSegment(segment2) {
|
|
7964
|
+
return {
|
|
7965
|
+
role: segment2.role,
|
|
7966
|
+
label: segment2.label,
|
|
7967
|
+
path: segment2.path,
|
|
7968
|
+
tokens: segment2.size,
|
|
7969
|
+
characters: segment2.characters,
|
|
7970
|
+
text: segment2.text
|
|
7971
|
+
};
|
|
7972
|
+
}
|
|
7973
|
+
function jsonLikeText(text) {
|
|
7974
|
+
const trimmed = text.trim();
|
|
7975
|
+
return trimmed.startsWith("{") || trimmed.startsWith("[");
|
|
7976
|
+
}
|
|
7977
|
+
function parseJsonLikeText(text) {
|
|
7978
|
+
if (!jsonLikeText(text)) return null;
|
|
7979
|
+
try {
|
|
7980
|
+
const parsed = JSON.parse(text);
|
|
7981
|
+
return parsed;
|
|
7982
|
+
} catch {
|
|
7983
|
+
return null;
|
|
7984
|
+
}
|
|
7985
|
+
}
|
|
7986
|
+
function normalizePathFieldName(field) {
|
|
7987
|
+
return field.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
7988
|
+
}
|
|
7989
|
+
function lastNamedPathField(path) {
|
|
7990
|
+
for (let index = path.length - 1; index >= 0; index -= 1) {
|
|
7991
|
+
const field = path[index] ?? "";
|
|
7992
|
+
if (field.startsWith("[") && field.endsWith("]")) continue;
|
|
7993
|
+
return field;
|
|
7994
|
+
}
|
|
7995
|
+
return null;
|
|
7996
|
+
}
|
|
7997
|
+
function nestedDuplicateFieldPath(path) {
|
|
7998
|
+
const field = lastNamedPathField(path);
|
|
7999
|
+
if (field === null) return false;
|
|
8000
|
+
return DUPLICATE_NESTED_FIELD_NAMES.has(normalizePathFieldName(field));
|
|
8001
|
+
}
|
|
8002
|
+
function formatNestedDuplicatePath(path) {
|
|
8003
|
+
return path.map((part) => part.startsWith("[") ? part : `.${part}`).join("").replace(/^\./, "");
|
|
8004
|
+
}
|
|
8005
|
+
function duplicateCandidateFromNestedText(segment2, text, path) {
|
|
8006
|
+
const characters = countCharacters(text);
|
|
8007
|
+
if (characters < DUPLICATE_MIN_CHARS) return null;
|
|
8008
|
+
const tokens = estimateTokens(text);
|
|
8009
|
+
if (tokens < DUPLICATE_MIN_TOKENS) return null;
|
|
8010
|
+
return {
|
|
8011
|
+
role: segment2.role,
|
|
8012
|
+
label: `${segment2.label} ${formatNestedDuplicatePath(path)}`,
|
|
8013
|
+
path: segment2.path,
|
|
8014
|
+
tokens,
|
|
8015
|
+
characters,
|
|
8016
|
+
text
|
|
8017
|
+
};
|
|
8018
|
+
}
|
|
8019
|
+
function collectNestedDuplicateCandidates({
|
|
8020
|
+
value,
|
|
8021
|
+
segment: segment2,
|
|
8022
|
+
path,
|
|
8023
|
+
depth,
|
|
8024
|
+
candidates
|
|
8025
|
+
}) {
|
|
8026
|
+
if (candidates.length >= DUPLICATE_NESTED_MAX_CANDIDATES_PER_SEGMENT) return;
|
|
8027
|
+
if (depth > DUPLICATE_NESTED_MAX_DEPTH) return;
|
|
8028
|
+
if (typeof value === "string") {
|
|
8029
|
+
if (!nestedDuplicateFieldPath(path)) return;
|
|
8030
|
+
const candidate = duplicateCandidateFromNestedText(segment2, value, path);
|
|
8031
|
+
if (candidate !== null) candidates.push(candidate);
|
|
8032
|
+
return;
|
|
8033
|
+
}
|
|
8034
|
+
if (Array.isArray(value)) {
|
|
8035
|
+
value.forEach(
|
|
8036
|
+
(item, index) => collectNestedDuplicateCandidates({
|
|
8037
|
+
value: item,
|
|
8038
|
+
segment: segment2,
|
|
8039
|
+
path: [...path, `[${String(index)}]`],
|
|
8040
|
+
depth: depth + 1,
|
|
8041
|
+
candidates
|
|
8042
|
+
})
|
|
8043
|
+
);
|
|
8044
|
+
return;
|
|
8045
|
+
}
|
|
8046
|
+
if (!isPlainRecord(value)) return;
|
|
8047
|
+
Object.entries(value).forEach(
|
|
8048
|
+
([key, child]) => collectNestedDuplicateCandidates({
|
|
8049
|
+
value: child,
|
|
8050
|
+
segment: segment2,
|
|
8051
|
+
path: [...path, key],
|
|
8052
|
+
depth: depth + 1,
|
|
8053
|
+
candidates
|
|
8054
|
+
})
|
|
8055
|
+
);
|
|
8056
|
+
}
|
|
8057
|
+
function duplicateCandidatesForSegment(segment2) {
|
|
8058
|
+
const segmentCandidate = duplicateCandidateFromSegment(segment2);
|
|
8059
|
+
const parsed = parseJsonLikeText(segment2.text);
|
|
8060
|
+
if (parsed === null) return [segmentCandidate];
|
|
8061
|
+
const candidates = [];
|
|
8062
|
+
collectNestedDuplicateCandidates({
|
|
8063
|
+
value: parsed,
|
|
8064
|
+
segment: segment2,
|
|
8065
|
+
path: [],
|
|
8066
|
+
depth: 0,
|
|
8067
|
+
candidates
|
|
8068
|
+
});
|
|
8069
|
+
const normalizedSegment = normalizeDuplicateText(segment2.text);
|
|
8070
|
+
const seen = /* @__PURE__ */ new Set([normalizedSegment]);
|
|
8071
|
+
const uniqueNested = candidates.filter((candidate) => {
|
|
8072
|
+
const normalized = normalizeDuplicateText(candidate.text);
|
|
8073
|
+
if (normalized === "") return false;
|
|
8074
|
+
if (seen.has(normalized)) return false;
|
|
8075
|
+
seen.add(normalized);
|
|
8076
|
+
return true;
|
|
8077
|
+
});
|
|
8078
|
+
return [segmentCandidate, ...uniqueNested];
|
|
8079
|
+
}
|
|
8080
|
+
function duplicateContextGroups(segments) {
|
|
8081
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
8082
|
+
for (const segment2 of segments) {
|
|
8083
|
+
const candidates = duplicateCandidatesForSegment(segment2);
|
|
8084
|
+
for (const candidate of candidates) {
|
|
8085
|
+
if (candidate.tokens < DUPLICATE_MIN_TOKENS) continue;
|
|
8086
|
+
if (candidate.characters < DUPLICATE_MIN_CHARS) continue;
|
|
8087
|
+
const normalized = normalizeDuplicateText(candidate.text);
|
|
8088
|
+
if (normalized === "") continue;
|
|
8089
|
+
const existing = buckets.get(normalized);
|
|
8090
|
+
if (existing === void 0) {
|
|
8091
|
+
buckets.set(normalized, {
|
|
8092
|
+
tokens: candidate.tokens,
|
|
8093
|
+
candidates: [candidate]
|
|
8094
|
+
});
|
|
8095
|
+
continue;
|
|
8096
|
+
}
|
|
8097
|
+
buckets.set(normalized, {
|
|
8098
|
+
tokens: existing.tokens + candidate.tokens,
|
|
8099
|
+
candidates: [...existing.candidates, candidate]
|
|
8100
|
+
});
|
|
8101
|
+
}
|
|
8102
|
+
}
|
|
8103
|
+
const groups = [];
|
|
8104
|
+
for (const bucket of buckets.values()) {
|
|
8105
|
+
if (bucket.candidates.length < 2) continue;
|
|
8106
|
+
const firstCandidate = bucket.candidates[0] ?? null;
|
|
8107
|
+
if (firstCandidate === null) continue;
|
|
8108
|
+
const repeatedTokens = bucket.tokens - firstCandidate.tokens;
|
|
8109
|
+
groups.push({
|
|
8110
|
+
key: `${firstCandidate.path}:${firstCandidate.label}:${String(
|
|
8111
|
+
bucket.candidates.length
|
|
8112
|
+
)}:${String(bucket.tokens)}`,
|
|
8113
|
+
count: bucket.candidates.length,
|
|
8114
|
+
totalTokens: bucket.tokens,
|
|
8115
|
+
repeatedTokens,
|
|
8116
|
+
preview: duplicatePreview(firstCandidate.text),
|
|
8117
|
+
firstLabel: firstCandidate.label,
|
|
8118
|
+
segments: bucket.candidates.map(duplicateSegmentRef)
|
|
8119
|
+
});
|
|
8120
|
+
}
|
|
8121
|
+
return groups.sort(
|
|
8122
|
+
(left, right) => right.repeatedTokens - left.repeatedTokens || right.totalTokens - left.totalTokens
|
|
8123
|
+
);
|
|
8124
|
+
}
|
|
8125
|
+
function duplicateDiagnostic(duplicateGroups) {
|
|
8126
|
+
if (duplicateGroups.length === 0) return null;
|
|
8127
|
+
const repeatedGroups = duplicateGroups.length;
|
|
8128
|
+
const repeatedSegments = duplicateGroups.reduce((sum, group) => sum + group.count, 0);
|
|
8129
|
+
const repeatedTokens = duplicateGroups.reduce((sum, group) => sum + group.repeatedTokens, 0);
|
|
8130
|
+
return {
|
|
8131
|
+
kind: "duplicate",
|
|
8132
|
+
severity: repeatedTokens >= LONG_TOOL_SCHEMA_TOKENS ? "danger" : "watch",
|
|
8133
|
+
title: "Duplicate context",
|
|
8134
|
+
detail: `${String(repeatedSegments)} repeated blocks across ${String(
|
|
8135
|
+
repeatedGroups
|
|
8136
|
+
)} group${repeatedGroups === 1 ? "" : "s"}, ~${String(repeatedTokens)} repeated tokens.`
|
|
8137
|
+
};
|
|
8138
|
+
}
|
|
8139
|
+
function duplicateReclaimableTokens(duplicateGroups) {
|
|
8140
|
+
return duplicateGroups.reduce((sum, group) => sum + group.repeatedTokens, 0);
|
|
8141
|
+
}
|
|
8142
|
+
function diagnosticsForRolePressure(roleUsages, total, messageSegmentCount) {
|
|
8143
|
+
const diagnostics = [];
|
|
8144
|
+
const toolUsage = roleUsages.find((usage) => usage.role === "tools") ?? null;
|
|
8145
|
+
if (toolUsage !== null && (toolUsage.tokens >= LONG_TOOL_SCHEMA_TOKENS || toolUsage.percent >= LARGE_ROLE_PERCENT && toolUsage.tokens >= SYSTEM_HEAVY_TOKENS)) {
|
|
8146
|
+
diagnostics.push({
|
|
8147
|
+
kind: "tool-schema",
|
|
8148
|
+
severity: toolUsage.tokens >= LONG_TOOL_SCHEMA_TOKENS ? "danger" : "watch",
|
|
8149
|
+
title: "Large tool schema",
|
|
8150
|
+
detail: `Tool definitions take ${Math.round(toolUsage.percent * 100)}% of request context.`
|
|
8151
|
+
});
|
|
8152
|
+
}
|
|
8153
|
+
const systemUsage = roleUsages.find((usage) => usage.role === "system") ?? null;
|
|
8154
|
+
if (systemUsage !== null && systemUsage.tokens >= SYSTEM_HEAVY_TOKENS && systemUsage.percent >= SYSTEM_HEAVY_PERCENT) {
|
|
8155
|
+
diagnostics.push({
|
|
8156
|
+
kind: "system-heavy",
|
|
8157
|
+
severity: "watch",
|
|
8158
|
+
title: "Large system prompt",
|
|
8159
|
+
detail: `System instructions take ${Math.round(systemUsage.percent * 100)}% of request context.`
|
|
8160
|
+
});
|
|
8161
|
+
}
|
|
8162
|
+
const historyTokens = roleUsages.filter((usage) => usage.role === "user" || usage.role === "assistant" || usage.role === "tool").reduce((sum, usage) => sum + usage.tokens, 0);
|
|
8163
|
+
const historyPercent = total > 0 ? historyTokens / total : 0;
|
|
8164
|
+
if (messageSegmentCount >= HISTORY_BLOAT_SEGMENTS && (historyTokens >= HISTORY_BLOAT_TOKENS || historyPercent >= HISTORY_BLOAT_PERCENT)) {
|
|
8165
|
+
diagnostics.push({
|
|
8166
|
+
kind: "history-bloat",
|
|
8167
|
+
severity: historyPercent >= 0.85 ? "danger" : "watch",
|
|
8168
|
+
title: "Conversation history growth",
|
|
8169
|
+
detail: `${String(messageSegmentCount)} message blocks take ${Math.round(
|
|
8170
|
+
historyPercent * 100
|
|
8171
|
+
)}% of request context.`
|
|
8172
|
+
});
|
|
8173
|
+
}
|
|
8174
|
+
return diagnostics;
|
|
8175
|
+
}
|
|
8176
|
+
function contextHealth({
|
|
8177
|
+
risk,
|
|
8178
|
+
contextWindow,
|
|
8179
|
+
diagnostics,
|
|
8180
|
+
duplicateGroups
|
|
8181
|
+
}) {
|
|
8182
|
+
const diagnosticKinds = new Set(diagnostics.map((diagnostic) => diagnostic.kind));
|
|
8183
|
+
let opportunityCount = diagnosticKinds.size;
|
|
8184
|
+
if (risk === "watch" || risk === "danger") opportunityCount += 1;
|
|
8185
|
+
if (contextWindow.source === "unknown") opportunityCount += 1;
|
|
8186
|
+
const hasDangerDiagnostic = diagnostics.some((diagnostic) => diagnostic.severity === "danger");
|
|
8187
|
+
const level = risk === "danger" || hasDangerDiagnostic ? "risk" : opportunityCount > 0 ? "optimizable" : "ok";
|
|
8188
|
+
return {
|
|
8189
|
+
level,
|
|
8190
|
+
opportunityCount,
|
|
8191
|
+
reclaimableTokens: duplicateReclaimableTokens(duplicateGroups)
|
|
8192
|
+
};
|
|
8193
|
+
}
|
|
8194
|
+
function analyzeContextIntelligence({
|
|
8195
|
+
segments,
|
|
8196
|
+
inputTokens,
|
|
8197
|
+
parsed,
|
|
8198
|
+
model,
|
|
8199
|
+
providers = []
|
|
8200
|
+
}) {
|
|
8201
|
+
const totalEstimatedTokens = segments.reduce((sum, segment2) => sum + segment2.size, 0);
|
|
8202
|
+
const requestModel = model ?? readRequestModel(parsed);
|
|
8203
|
+
const estimatedInputTokens = inputTokens ?? totalEstimatedTokens;
|
|
8204
|
+
const outputReserveTokens = readPositiveIntegerField(parsed, OUTPUT_RESERVE_FIELDS);
|
|
8205
|
+
const contextWindow = resolveContextWindow(requestModel, parsed, providers);
|
|
8206
|
+
const reservedTokens = outputReserveTokens ?? 0;
|
|
8207
|
+
const windowUsedTokens = contextWindow.tokens === null ? null : estimatedInputTokens + reservedTokens;
|
|
8208
|
+
const remainingInputTokens = contextWindow.tokens === null ? null : contextWindow.tokens - estimatedInputTokens;
|
|
8209
|
+
const remainingAfterReserveTokens = contextWindow.tokens === null ? null : contextWindow.tokens - estimatedInputTokens - reservedTokens;
|
|
8210
|
+
const usagePercent = contextWindow.tokens === null || windowUsedTokens === null ? null : clampPercent(windowUsedTokens / contextWindow.tokens);
|
|
8211
|
+
const roleUsages = buildRoleUsages(segments, totalEstimatedTokens);
|
|
8212
|
+
const largestRole = roleUsages[0] ?? null;
|
|
8213
|
+
const messageSegmentCount = segments.filter((segment2) => segment2.role !== "tools").length;
|
|
8214
|
+
const duplicateGroups = duplicateContextGroups(segments);
|
|
8215
|
+
const duplicate = duplicateDiagnostic(duplicateGroups);
|
|
8216
|
+
const diagnostics = diagnosticsForRolePressure(
|
|
8217
|
+
roleUsages,
|
|
8218
|
+
totalEstimatedTokens,
|
|
8219
|
+
messageSegmentCount
|
|
8220
|
+
);
|
|
8221
|
+
if (duplicate !== null) diagnostics.unshift(duplicate);
|
|
8222
|
+
const risk = riskLevel(usagePercent);
|
|
8223
|
+
return {
|
|
8224
|
+
model: requestModel,
|
|
8225
|
+
contextWindow,
|
|
8226
|
+
estimatedInputTokens,
|
|
8227
|
+
outputReserveTokens,
|
|
8228
|
+
windowUsedTokens,
|
|
8229
|
+
remainingInputTokens,
|
|
8230
|
+
remainingAfterReserveTokens,
|
|
8231
|
+
usagePercent,
|
|
8232
|
+
riskLevel: risk,
|
|
8233
|
+
roleUsages,
|
|
8234
|
+
largestRole,
|
|
8235
|
+
diagnostics,
|
|
8236
|
+
duplicateGroups,
|
|
8237
|
+
health: contextHealth({
|
|
8238
|
+
risk,
|
|
8239
|
+
contextWindow,
|
|
8240
|
+
diagnostics,
|
|
8241
|
+
duplicateGroups
|
|
8242
|
+
})
|
|
8243
|
+
};
|
|
8244
|
+
}
|
|
8245
|
+
const CONTEXT_USAGE_THRESHOLDS = {
|
|
8246
|
+
watch: WATCH_USAGE_PERCENT,
|
|
8247
|
+
danger: DANGER_USAGE_PERCENT
|
|
8248
|
+
};
|
|
8249
|
+
function parseRequestBody(rawBody) {
|
|
8250
|
+
if (rawBody === null) return null;
|
|
8251
|
+
try {
|
|
8252
|
+
const parsed = JSON.parse(rawBody);
|
|
8253
|
+
return parsed;
|
|
8254
|
+
} catch {
|
|
8255
|
+
return null;
|
|
8256
|
+
}
|
|
8257
|
+
}
|
|
8258
|
+
function roleUsageSegment(usage) {
|
|
8259
|
+
return {
|
|
8260
|
+
role: usage.role,
|
|
8261
|
+
label: usage.label,
|
|
8262
|
+
size: usage.tokens,
|
|
8263
|
+
characters: usage.tokens * 4,
|
|
8264
|
+
text: "",
|
|
8265
|
+
path: `role:${usage.role}`
|
|
8266
|
+
};
|
|
8267
|
+
}
|
|
8268
|
+
function snapshotFromLog(log, providers) {
|
|
8269
|
+
const parsed = parseRequestBody(log.rawRequestBody);
|
|
8270
|
+
if (parsed === null) return null;
|
|
8271
|
+
const adapter = getLogFormatAdapter(resolveLogFormat(log));
|
|
8272
|
+
const segments = adapter.anatomySegments(parsed);
|
|
8273
|
+
if (segments === null) return null;
|
|
8274
|
+
const intelligence = analyzeContextIntelligence({
|
|
8275
|
+
segments,
|
|
8276
|
+
inputTokens: log.inputTokens ?? null,
|
|
8277
|
+
parsed,
|
|
8278
|
+
model: log.model,
|
|
8279
|
+
providers
|
|
8280
|
+
});
|
|
8281
|
+
const model = intelligence.model ?? log.model ?? "Unknown model";
|
|
8282
|
+
const roleSegments = intelligence.roleUsages.map(roleUsageSegment);
|
|
8283
|
+
return {
|
|
8284
|
+
logId: log.id,
|
|
8285
|
+
model,
|
|
8286
|
+
timestamp: log.timestamp,
|
|
8287
|
+
contextWindow: intelligence.contextWindow,
|
|
8288
|
+
estimatedInputTokens: intelligence.estimatedInputTokens,
|
|
8289
|
+
windowUsedTokens: intelligence.windowUsedTokens,
|
|
8290
|
+
usagePercent: intelligence.usagePercent,
|
|
8291
|
+
riskLevel: intelligence.riskLevel,
|
|
8292
|
+
health: intelligence.health,
|
|
8293
|
+
diagnostics: intelligence.diagnostics.map((diagnostic) => diagnostic.title),
|
|
8294
|
+
roleSegments,
|
|
8295
|
+
roleTotalTokens: roleSegments.reduce((sum, segment2) => sum + segment2.size, 0)
|
|
8296
|
+
};
|
|
8297
|
+
}
|
|
8298
|
+
function isHigherPeak(candidate, current) {
|
|
8299
|
+
if (candidate.usagePercent !== null && current.usagePercent !== null) {
|
|
8300
|
+
return candidate.usagePercent > current.usagePercent;
|
|
8301
|
+
}
|
|
8302
|
+
if (candidate.usagePercent !== null && current.usagePercent === null) return true;
|
|
8303
|
+
if (candidate.usagePercent === null && current.usagePercent !== null) return false;
|
|
8304
|
+
return candidate.estimatedInputTokens > current.estimatedInputTokens;
|
|
8305
|
+
}
|
|
8306
|
+
function riskRank(risk) {
|
|
8307
|
+
switch (risk) {
|
|
8308
|
+
case "danger":
|
|
8309
|
+
return 4;
|
|
8310
|
+
case "watch":
|
|
8311
|
+
return 3;
|
|
8312
|
+
case "unknown":
|
|
8313
|
+
return 2;
|
|
8314
|
+
case "ok":
|
|
8315
|
+
return 1;
|
|
8316
|
+
}
|
|
8317
|
+
}
|
|
8318
|
+
function compareModelSummary(left, right) {
|
|
8319
|
+
const riskDelta = riskRank(right.latest.riskLevel) - riskRank(left.latest.riskLevel);
|
|
8320
|
+
if (riskDelta !== 0) return riskDelta;
|
|
8321
|
+
const leftPeak = left.peak.usagePercent ?? -1;
|
|
8322
|
+
const rightPeak = right.peak.usagePercent ?? -1;
|
|
8323
|
+
const peakDelta = rightPeak - leftPeak;
|
|
8324
|
+
if (peakDelta !== 0) return peakDelta;
|
|
8325
|
+
return right.latest.logId - left.latest.logId;
|
|
8326
|
+
}
|
|
8327
|
+
function trendPercent(first, latest) {
|
|
8328
|
+
if (first.usagePercent === null || latest.usagePercent === null) return null;
|
|
8329
|
+
return (latest.usagePercent - first.usagePercent) * 100;
|
|
8330
|
+
}
|
|
8331
|
+
function buildSessionContextSummary(logs, providers = []) {
|
|
8332
|
+
const sortedLogs = [...logs].sort((left, right) => left.id - right.id);
|
|
8333
|
+
const byModel = /* @__PURE__ */ new Map();
|
|
8334
|
+
for (const log of sortedLogs) {
|
|
8335
|
+
const snapshot = snapshotFromLog(log, providers);
|
|
8336
|
+
if (snapshot === null) continue;
|
|
8337
|
+
const existing = byModel.get(snapshot.model);
|
|
8338
|
+
if (existing === void 0) {
|
|
8339
|
+
byModel.set(snapshot.model, {
|
|
8340
|
+
model: snapshot.model,
|
|
8341
|
+
requestCount: 1,
|
|
8342
|
+
first: snapshot,
|
|
8343
|
+
latest: snapshot,
|
|
8344
|
+
peak: snapshot
|
|
8345
|
+
});
|
|
8346
|
+
continue;
|
|
8347
|
+
}
|
|
8348
|
+
byModel.set(snapshot.model, {
|
|
8349
|
+
model: existing.model,
|
|
8350
|
+
requestCount: existing.requestCount + 1,
|
|
8351
|
+
first: existing.first,
|
|
8352
|
+
latest: snapshot,
|
|
8353
|
+
peak: isHigherPeak(snapshot, existing.peak) ? snapshot : existing.peak
|
|
8354
|
+
});
|
|
8355
|
+
}
|
|
8356
|
+
const models = [...byModel.values()].map((entry) => ({
|
|
8357
|
+
model: entry.model,
|
|
8358
|
+
requestCount: entry.requestCount,
|
|
8359
|
+
first: entry.first,
|
|
8360
|
+
latest: entry.latest,
|
|
8361
|
+
peak: entry.peak,
|
|
8362
|
+
trendPercent: trendPercent(entry.first, entry.latest)
|
|
8363
|
+
})).sort(compareModelSummary);
|
|
8364
|
+
return { models };
|
|
8365
|
+
}
|
|
7287
8366
|
function truncateSessionId(id) {
|
|
7288
8367
|
if (id.length <= 30) return id;
|
|
7289
8368
|
return id.slice(0, 12) + "…" + id.slice(-12);
|
|
@@ -7311,6 +8390,142 @@ function getFirstUserAgent(logs) {
|
|
|
7311
8390
|
}
|
|
7312
8391
|
return null;
|
|
7313
8392
|
}
|
|
8393
|
+
function resolveSessionContextScope({
|
|
8394
|
+
pinnedSessionId,
|
|
8395
|
+
selectedSession,
|
|
8396
|
+
sessions,
|
|
8397
|
+
logs
|
|
8398
|
+
}) {
|
|
8399
|
+
if (pinnedSessionId !== void 0) {
|
|
8400
|
+
const scopedLogs = logs.filter((log) => log.sessionId === pinnedSessionId);
|
|
8401
|
+
if (scopedLogs.length === 0) return null;
|
|
8402
|
+
return { label: pinnedSessionId, logs: scopedLogs, showBackLink: true };
|
|
8403
|
+
}
|
|
8404
|
+
if (selectedSession !== "__all__") {
|
|
8405
|
+
const scopedLogs = logs.filter((log) => log.sessionId === selectedSession);
|
|
8406
|
+
if (scopedLogs.length === 0) return null;
|
|
8407
|
+
return { label: selectedSession, logs: scopedLogs, showBackLink: true };
|
|
8408
|
+
}
|
|
8409
|
+
const singleSessionId = sessions.length === 1 ? sessions[0] ?? null : null;
|
|
8410
|
+
if (singleSessionId !== null) {
|
|
8411
|
+
const allVisibleLogsBelongToSession = logs.every((log) => log.sessionId === singleSessionId);
|
|
8412
|
+
if (allVisibleLogsBelongToSession) {
|
|
8413
|
+
return { label: singleSessionId, logs: [...logs], showBackLink: false };
|
|
8414
|
+
}
|
|
8415
|
+
}
|
|
8416
|
+
const allVisibleLogsAreSessionless = sessions.length === 0 && logs.length > 0 && logs.every((log) => log.sessionId === null);
|
|
8417
|
+
if (allVisibleLogsAreSessionless) {
|
|
8418
|
+
return { label: "Visible requests", logs: [...logs], showBackLink: false };
|
|
8419
|
+
}
|
|
8420
|
+
return null;
|
|
8421
|
+
}
|
|
8422
|
+
function formatPercent(value) {
|
|
8423
|
+
if (value === null) return "Unknown";
|
|
8424
|
+
const percent = value * 100;
|
|
8425
|
+
if (percent >= 10) return `${percent.toFixed(0)}%`;
|
|
8426
|
+
if (percent >= 1) return `${percent.toFixed(1)}%`;
|
|
8427
|
+
if (percent > 0) return "<1%";
|
|
8428
|
+
return "0%";
|
|
8429
|
+
}
|
|
8430
|
+
function formatTrend(value) {
|
|
8431
|
+
if (value === null) return "n/a";
|
|
8432
|
+
if (Math.abs(value) < 0.1) return "0%";
|
|
8433
|
+
const sign = value > 0 ? "+" : "";
|
|
8434
|
+
return `${sign}${value.toFixed(1)}%`;
|
|
8435
|
+
}
|
|
8436
|
+
function progressPercent(value) {
|
|
8437
|
+
if (value === null || !Number.isFinite(value)) return 0;
|
|
8438
|
+
return Math.min(Math.max(value * 100, 0), 100);
|
|
8439
|
+
}
|
|
8440
|
+
function peakMarkerPercent(latestPercent, peakPercent) {
|
|
8441
|
+
if (peakPercent === null) return null;
|
|
8442
|
+
const latest = progressPercent(latestPercent);
|
|
8443
|
+
const peak = progressPercent(peakPercent);
|
|
8444
|
+
if (peak <= 0) return null;
|
|
8445
|
+
if (Math.abs(peak - latest) < 0.5) return null;
|
|
8446
|
+
return peak;
|
|
8447
|
+
}
|
|
8448
|
+
function sessionRiskBarClass(risk) {
|
|
8449
|
+
switch (risk) {
|
|
8450
|
+
case "danger":
|
|
8451
|
+
return "bg-red-400";
|
|
8452
|
+
case "watch":
|
|
8453
|
+
return "bg-amber-300";
|
|
8454
|
+
case "unknown":
|
|
8455
|
+
return "bg-muted-foreground/50";
|
|
8456
|
+
case "ok":
|
|
8457
|
+
return "bg-emerald-400";
|
|
8458
|
+
}
|
|
8459
|
+
}
|
|
8460
|
+
function formatContextWindow(summary) {
|
|
8461
|
+
const tokens = summary.latest.contextWindow.tokens;
|
|
8462
|
+
if (tokens === null) return "Window unknown";
|
|
8463
|
+
return `Window ${formatContextWindowTokens(tokens)}`;
|
|
8464
|
+
}
|
|
8465
|
+
function formatLogReference(logId) {
|
|
8466
|
+
return `#${String(logId)}`;
|
|
8467
|
+
}
|
|
8468
|
+
function sessionRiskClass(risk) {
|
|
8469
|
+
switch (risk) {
|
|
8470
|
+
case "danger":
|
|
8471
|
+
return "text-red-300";
|
|
8472
|
+
case "watch":
|
|
8473
|
+
return "text-amber-200";
|
|
8474
|
+
case "unknown":
|
|
8475
|
+
return "text-muted-foreground";
|
|
8476
|
+
case "ok":
|
|
8477
|
+
return "text-emerald-300";
|
|
8478
|
+
}
|
|
8479
|
+
}
|
|
8480
|
+
function sessionHealthClass(level) {
|
|
8481
|
+
switch (level) {
|
|
8482
|
+
case "risk":
|
|
8483
|
+
return "border-red-400/30 bg-red-500/10 text-red-100";
|
|
8484
|
+
case "optimizable":
|
|
8485
|
+
return "border-amber-400/25 bg-amber-400/8 text-amber-100";
|
|
8486
|
+
case "ok":
|
|
8487
|
+
return "border-emerald-400/25 bg-emerald-400/8 text-emerald-100";
|
|
8488
|
+
}
|
|
8489
|
+
}
|
|
8490
|
+
function sessionHealthLabel(level) {
|
|
8491
|
+
switch (level) {
|
|
8492
|
+
case "risk":
|
|
8493
|
+
return "Risk";
|
|
8494
|
+
case "optimizable":
|
|
8495
|
+
return "Optimizable";
|
|
8496
|
+
case "ok":
|
|
8497
|
+
return "OK";
|
|
8498
|
+
}
|
|
8499
|
+
}
|
|
8500
|
+
const LOG_FOCUS_RETRY_LIMIT = 10;
|
|
8501
|
+
const LOG_FOCUS_RETRY_DELAY_MS = 80;
|
|
8502
|
+
function highlightLogTarget(target) {
|
|
8503
|
+
target.setAttribute("data-deep-link-highlight", "true");
|
|
8504
|
+
window.setTimeout(() => {
|
|
8505
|
+
target.removeAttribute("data-deep-link-highlight");
|
|
8506
|
+
}, 1800);
|
|
8507
|
+
}
|
|
8508
|
+
function focusLogTarget(logId) {
|
|
8509
|
+
const target = document.getElementById(`log-${String(logId)}`);
|
|
8510
|
+
if (!(target instanceof HTMLElement)) return false;
|
|
8511
|
+
target.scrollIntoView({ block: "center", behavior: "smooth" });
|
|
8512
|
+
target.focus({ preventScroll: true });
|
|
8513
|
+
highlightLogTarget(target);
|
|
8514
|
+
return true;
|
|
8515
|
+
}
|
|
8516
|
+
function openLogContext(logId) {
|
|
8517
|
+
dispatchLogFocusRequest({ logId, tab: "anatomy" });
|
|
8518
|
+
let attempts = 0;
|
|
8519
|
+
const tryFocus = () => {
|
|
8520
|
+
dispatchLogFocusRequest({ logId, tab: "anatomy" });
|
|
8521
|
+
if (focusLogTarget(logId)) return;
|
|
8522
|
+
attempts += 1;
|
|
8523
|
+
if (attempts < LOG_FOCUS_RETRY_LIMIT) {
|
|
8524
|
+
window.setTimeout(tryFocus, LOG_FOCUS_RETRY_DELAY_MS);
|
|
8525
|
+
}
|
|
8526
|
+
};
|
|
8527
|
+
window.setTimeout(tryFocus, 0);
|
|
8528
|
+
}
|
|
7314
8529
|
function CopyableCommand({ command }) {
|
|
7315
8530
|
const [copied, setCopied] = reactExports.useState(false);
|
|
7316
8531
|
const handleCopy = reactExports.useCallback(() => {
|
|
@@ -7372,12 +8587,202 @@ function McpReadyBadge() {
|
|
|
7372
8587
|
/* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { sideOffset: 8, className: "max-w-[320px] text-left leading-relaxed", children: "Coding agents can inspect logs, replay requests, test providers, and debug sessions through MCP at /api/mcp." })
|
|
7373
8588
|
] }) });
|
|
7374
8589
|
}
|
|
8590
|
+
function SessionContextLogButton({ label, logId }) {
|
|
8591
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
8592
|
+
"button",
|
|
8593
|
+
{
|
|
8594
|
+
type: "button",
|
|
8595
|
+
onClick: () => openLogContext(logId),
|
|
8596
|
+
className: "inline-flex h-5 items-center rounded px-1 font-mono text-[10px] text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
|
|
8597
|
+
title: `Open Context for ${formatLogReference(logId)}`,
|
|
8598
|
+
children: [
|
|
8599
|
+
label,
|
|
8600
|
+
" ",
|
|
8601
|
+
formatLogReference(logId)
|
|
8602
|
+
]
|
|
8603
|
+
}
|
|
8604
|
+
);
|
|
8605
|
+
}
|
|
8606
|
+
function SessionContextProgressBar({
|
|
8607
|
+
latestPercent,
|
|
8608
|
+
peakPercent,
|
|
8609
|
+
risk,
|
|
8610
|
+
onOpenContext
|
|
8611
|
+
}) {
|
|
8612
|
+
const latest = progressPercent(latestPercent);
|
|
8613
|
+
const peakMarker = peakMarkerPercent(latestPercent, peakPercent);
|
|
8614
|
+
const ariaLabel = peakPercent === null ? `Latest context window usage ${formatPercent(latestPercent)}` : `Latest context window usage ${formatPercent(latestPercent)}, peak ${formatPercent(
|
|
8615
|
+
peakPercent
|
|
8616
|
+
)}`;
|
|
8617
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
8618
|
+
"button",
|
|
8619
|
+
{
|
|
8620
|
+
type: "button",
|
|
8621
|
+
onClick: onOpenContext,
|
|
8622
|
+
className: "group block w-full rounded-full focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1",
|
|
8623
|
+
"aria-label": `${ariaLabel}. Open latest Context details`,
|
|
8624
|
+
title: "Open latest Context details",
|
|
8625
|
+
children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
8626
|
+
"span",
|
|
8627
|
+
{
|
|
8628
|
+
className: "relative block h-2.5 overflow-hidden rounded-full border border-border/70 bg-background transition-colors group-hover:border-foreground/30",
|
|
8629
|
+
role: "meter",
|
|
8630
|
+
"aria-label": ariaLabel,
|
|
8631
|
+
"aria-valuemin": 0,
|
|
8632
|
+
"aria-valuemax": 100,
|
|
8633
|
+
"aria-valuenow": Math.round(latest),
|
|
8634
|
+
children: [
|
|
8635
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
8636
|
+
"span",
|
|
8637
|
+
{
|
|
8638
|
+
className: cn("block h-full rounded-full transition-[width]", sessionRiskBarClass(risk)),
|
|
8639
|
+
style: { width: `${latest}%` }
|
|
8640
|
+
}
|
|
8641
|
+
),
|
|
8642
|
+
peakMarker !== null && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
8643
|
+
"span",
|
|
8644
|
+
{
|
|
8645
|
+
className: "absolute top-0 h-full w-0.5 rounded-full bg-amber-100/75 shadow-[0_0_0_1px_rgba(0,0,0,0.25)]",
|
|
8646
|
+
style: { left: `calc(${peakMarker}% - 0.5px)` },
|
|
8647
|
+
"aria-hidden": "true"
|
|
8648
|
+
}
|
|
8649
|
+
)
|
|
8650
|
+
]
|
|
8651
|
+
}
|
|
8652
|
+
)
|
|
8653
|
+
}
|
|
8654
|
+
);
|
|
8655
|
+
}
|
|
8656
|
+
function SessionModelContextRow({ summary }) {
|
|
8657
|
+
const trend = summary.trendPercent;
|
|
8658
|
+
const TrendIcon = trend === null || Math.abs(trend) < 0.1 ? null : trend > 0 ? ArrowUpRight : ArrowDownRight;
|
|
8659
|
+
const diagnosticLabel = summary.latest.diagnostics[0] ?? null;
|
|
8660
|
+
const latestUsage = formatPercent(summary.latest.usagePercent);
|
|
8661
|
+
const peakUsage = formatPercent(summary.peak.usagePercent);
|
|
8662
|
+
const openLatestContext = () => {
|
|
8663
|
+
openLogContext(summary.latest.logId);
|
|
8664
|
+
};
|
|
8665
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid gap-x-4 gap-y-2 border-t border-border/60 py-2 first:border-t-0 lg:grid-cols-[minmax(180px,1fr)_minmax(260px,1fr)_minmax(260px,1.4fr)]", children: [
|
|
8666
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0", children: [
|
|
8667
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [
|
|
8668
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
8669
|
+
"button",
|
|
8670
|
+
{
|
|
8671
|
+
type: "button",
|
|
8672
|
+
onClick: openLatestContext,
|
|
8673
|
+
className: "min-w-0 truncate rounded-sm text-left font-mono font-semibold text-foreground transition-colors hover:text-cyan-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1",
|
|
8674
|
+
title: `Open latest Context for ${summary.model}`,
|
|
8675
|
+
children: summary.model
|
|
8676
|
+
}
|
|
8677
|
+
),
|
|
8678
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
8679
|
+
"span",
|
|
8680
|
+
{
|
|
8681
|
+
className: cn(
|
|
8682
|
+
"inline-flex h-5 shrink-0 items-center rounded border px-1.5 font-mono text-[10px]",
|
|
8683
|
+
sessionHealthClass(summary.latest.health.level)
|
|
8684
|
+
),
|
|
8685
|
+
children: sessionHealthLabel(summary.latest.health.level)
|
|
8686
|
+
}
|
|
8687
|
+
)
|
|
8688
|
+
] }),
|
|
8689
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-1 flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1 text-[11px] text-muted-foreground", children: [
|
|
8690
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
|
|
8691
|
+
summary.requestCount,
|
|
8692
|
+
" req"
|
|
8693
|
+
] }),
|
|
8694
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: formatContextWindow(summary) }),
|
|
8695
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate", title: summary.latest.contextWindow.label, children: summary.latest.contextWindow.label })
|
|
8696
|
+
] })
|
|
8697
|
+
] }),
|
|
8698
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0 space-y-2 text-xs", children: [
|
|
8699
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
8700
|
+
SessionContextProgressBar,
|
|
8701
|
+
{
|
|
8702
|
+
latestPercent: summary.latest.usagePercent,
|
|
8703
|
+
peakPercent: summary.peak.usagePercent,
|
|
8704
|
+
risk: summary.latest.riskLevel,
|
|
8705
|
+
onOpenContext: openLatestContext
|
|
8706
|
+
}
|
|
8707
|
+
),
|
|
8708
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-3 gap-2", children: [
|
|
8709
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0", children: [
|
|
8710
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] uppercase text-muted-foreground", children: "Latest" }),
|
|
8711
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: cn("mt-0.5 font-mono", sessionRiskClass(summary.latest.riskLevel)), children: latestUsage }),
|
|
8712
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(SessionContextLogButton, { label: "Log", logId: summary.latest.logId })
|
|
8713
|
+
] }),
|
|
8714
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0", children: [
|
|
8715
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] uppercase text-muted-foreground", children: "Peak" }),
|
|
8716
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: cn("mt-0.5 font-mono", sessionRiskClass(summary.peak.riskLevel)), children: peakUsage }),
|
|
8717
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(SessionContextLogButton, { label: "Log", logId: summary.peak.logId })
|
|
8718
|
+
] }),
|
|
8719
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0", children: [
|
|
8720
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] uppercase text-muted-foreground", children: "Trend" }),
|
|
8721
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-0.5 flex items-center gap-1 font-mono text-foreground", children: [
|
|
8722
|
+
TrendIcon !== null && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
8723
|
+
TrendIcon,
|
|
8724
|
+
{
|
|
8725
|
+
className: cn(
|
|
8726
|
+
"size-3",
|
|
8727
|
+
trend !== null && trend > 0 ? "text-amber-300" : "text-emerald-300"
|
|
8728
|
+
)
|
|
8729
|
+
}
|
|
8730
|
+
),
|
|
8731
|
+
formatTrend(trend)
|
|
8732
|
+
] }),
|
|
8733
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-5", "aria-hidden": "true" })
|
|
8734
|
+
] })
|
|
8735
|
+
] })
|
|
8736
|
+
] }),
|
|
8737
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0 space-y-1.5", children: [
|
|
8738
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
8739
|
+
SegmentBar,
|
|
8740
|
+
{
|
|
8741
|
+
segments: summary.latest.roleSegments,
|
|
8742
|
+
totalTokens: summary.latest.roleTotalTokens,
|
|
8743
|
+
showLabels: false
|
|
8744
|
+
}
|
|
8745
|
+
),
|
|
8746
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1 text-[11px] text-muted-foreground", children: [
|
|
8747
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono", children: [
|
|
8748
|
+
"~",
|
|
8749
|
+
formatTokens(summary.latest.estimatedInputTokens),
|
|
8750
|
+
" input"
|
|
8751
|
+
] }),
|
|
8752
|
+
summary.latest.windowUsedTokens !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono", children: [
|
|
8753
|
+
"~",
|
|
8754
|
+
formatTokens(summary.latest.windowUsedTokens),
|
|
8755
|
+
" used"
|
|
8756
|
+
] }),
|
|
8757
|
+
diagnosticLabel !== null && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate", children: diagnosticLabel })
|
|
8758
|
+
] })
|
|
8759
|
+
] })
|
|
8760
|
+
] });
|
|
8761
|
+
}
|
|
8762
|
+
function SessionContextSummaryPanel({
|
|
8763
|
+
summary
|
|
8764
|
+
}) {
|
|
8765
|
+
if (summary.models.length === 0) return null;
|
|
8766
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-3 border-t border-border/70 pt-2", children: [
|
|
8767
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-1 flex items-center justify-between gap-2", children: [
|
|
8768
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[11px] font-semibold uppercase tracking-normal text-muted-foreground", children: "Session Context" }),
|
|
8769
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "font-mono text-[10px] text-muted-foreground", children: [
|
|
8770
|
+
summary.models.length,
|
|
8771
|
+
" model",
|
|
8772
|
+
summary.models.length === 1 ? "" : "s"
|
|
8773
|
+
] })
|
|
8774
|
+
] }),
|
|
8775
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "max-h-52 overflow-y-auto pr-1", children: summary.models.map((modelSummary) => /* @__PURE__ */ jsxRuntimeExports.jsx(SessionModelContextRow, { summary: modelSummary }, modelSummary.model)) })
|
|
8776
|
+
] });
|
|
8777
|
+
}
|
|
7375
8778
|
function SessionContextBar({
|
|
7376
|
-
|
|
8779
|
+
label,
|
|
7377
8780
|
logs,
|
|
7378
8781
|
totalIn,
|
|
7379
8782
|
totalOut,
|
|
7380
|
-
timeDisplayFormat
|
|
8783
|
+
timeDisplayFormat,
|
|
8784
|
+
contextSummary,
|
|
8785
|
+
showBackLink
|
|
7381
8786
|
}) {
|
|
7382
8787
|
const [copied, setCopied] = reactExports.useState(false);
|
|
7383
8788
|
const timeRange = reactExports.useMemo(
|
|
@@ -7391,55 +8796,58 @@ function SessionContextBar({
|
|
|
7391
8796
|
setTimeout(() => setCopied(false), 2e3);
|
|
7392
8797
|
});
|
|
7393
8798
|
}, []);
|
|
7394
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-4
|
|
7395
|
-
/* @__PURE__ */ jsxRuntimeExports.
|
|
7396
|
-
|
|
7397
|
-
|
|
7398
|
-
|
|
7399
|
-
|
|
7400
|
-
|
|
7401
|
-
|
|
7402
|
-
|
|
7403
|
-
|
|
7404
|
-
|
|
7405
|
-
|
|
7406
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "
|
|
7407
|
-
/* @__PURE__ */ jsxRuntimeExports.
|
|
7408
|
-
|
|
7409
|
-
|
|
7410
|
-
|
|
7411
|
-
|
|
7412
|
-
|
|
7413
|
-
|
|
7414
|
-
|
|
7415
|
-
|
|
7416
|
-
|
|
7417
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-1 flex flex-wrap items-center gap-x-3 gap-y-1 text-muted-foreground", children: [
|
|
7418
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
|
|
7419
|
-
logs.length,
|
|
7420
|
-
" request",
|
|
7421
|
-
logs.length !== 1 ? "s" : ""
|
|
8799
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-4 border border-border rounded-md bg-muted/20 px-3 py-2 text-xs", children: [
|
|
8800
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
8801
|
+
showBackLink && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
8802
|
+
"a",
|
|
8803
|
+
{
|
|
8804
|
+
href: "/",
|
|
8805
|
+
className: "inline-flex size-8 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
|
|
8806
|
+
"aria-label": "Back to all sessions",
|
|
8807
|
+
title: "Back to all sessions",
|
|
8808
|
+
children: /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowLeft, { className: "size-3.5" })
|
|
8809
|
+
}
|
|
8810
|
+
),
|
|
8811
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
8812
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [
|
|
8813
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono font-semibold text-purple-400/90 truncate", title: label, children: truncateSessionId(label) }),
|
|
8814
|
+
userAgent !== null && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
8815
|
+
"span",
|
|
8816
|
+
{
|
|
8817
|
+
className: "font-mono text-muted-foreground truncate max-w-[220px]",
|
|
8818
|
+
title: userAgent,
|
|
8819
|
+
children: userAgent
|
|
8820
|
+
}
|
|
8821
|
+
)
|
|
7422
8822
|
] }),
|
|
7423
|
-
|
|
7424
|
-
|
|
7425
|
-
|
|
7426
|
-
|
|
7427
|
-
|
|
7428
|
-
|
|
8823
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-1 flex flex-wrap items-center gap-x-3 gap-y-1 text-muted-foreground", children: [
|
|
8824
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
|
|
8825
|
+
logs.length,
|
|
8826
|
+
" request",
|
|
8827
|
+
logs.length !== 1 ? "s" : ""
|
|
8828
|
+
] }),
|
|
8829
|
+
timeRange !== null && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: timeRange }),
|
|
8830
|
+
(totalIn > 0 || totalOut > 0) && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono", children: [
|
|
8831
|
+
formatTokens(totalIn),
|
|
8832
|
+
" in / ",
|
|
8833
|
+
formatTokens(totalOut),
|
|
8834
|
+
" out"
|
|
8835
|
+
] })
|
|
7429
8836
|
] })
|
|
7430
|
-
] })
|
|
8837
|
+
] }),
|
|
8838
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
8839
|
+
"button",
|
|
8840
|
+
{
|
|
8841
|
+
type: "button",
|
|
8842
|
+
onClick: handleCopyLink,
|
|
8843
|
+
className: "inline-flex size-8 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
|
|
8844
|
+
"aria-label": copied ? "Copied session link" : "Copy session link",
|
|
8845
|
+
title: copied ? "Copied session link" : "Copy session link",
|
|
8846
|
+
children: copied ? /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-3.5" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Copy, { className: "size-3.5" })
|
|
8847
|
+
}
|
|
8848
|
+
)
|
|
7431
8849
|
] }),
|
|
7432
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
7433
|
-
"button",
|
|
7434
|
-
{
|
|
7435
|
-
type: "button",
|
|
7436
|
-
onClick: handleCopyLink,
|
|
7437
|
-
className: "inline-flex size-8 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
|
|
7438
|
-
"aria-label": copied ? "Copied session link" : "Copy session link",
|
|
7439
|
-
title: copied ? "Copied session link" : "Copy session link",
|
|
7440
|
-
children: copied ? /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-3.5" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Copy, { className: "size-3.5" })
|
|
7441
|
-
}
|
|
7442
|
-
)
|
|
8850
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(SessionContextSummaryPanel, { summary: contextSummary })
|
|
7443
8851
|
] });
|
|
7444
8852
|
}
|
|
7445
8853
|
function ProxyViewer({
|
|
@@ -7461,6 +8869,7 @@ function ProxyViewer({
|
|
|
7461
8869
|
pinnedSessionId
|
|
7462
8870
|
}) {
|
|
7463
8871
|
const { totalIn, totalOut } = reactExports.useMemo(() => computeTokenSummary(logs), [logs]);
|
|
8872
|
+
const { providers } = useProviders();
|
|
7464
8873
|
const [exporting, setExporting] = reactExports.useState(false);
|
|
7465
8874
|
const [comparePair, setComparePair] = reactExports.useState(null);
|
|
7466
8875
|
const [crabEntrancePhase, setCrabEntrancePhase] = reactExports.useState(
|
|
@@ -7503,7 +8912,20 @@ function ProxyViewer({
|
|
|
7503
8912
|
setComparePair(null);
|
|
7504
8913
|
}, []);
|
|
7505
8914
|
const groups = reactExports.useMemo(() => groupLogsByConversation(logs), [logs]);
|
|
7506
|
-
const
|
|
8915
|
+
const sessionContextScope = reactExports.useMemo(
|
|
8916
|
+
() => resolveSessionContextScope({ pinnedSessionId, selectedSession, sessions, logs }),
|
|
8917
|
+
[logs, pinnedSessionId, selectedSession, sessions]
|
|
8918
|
+
);
|
|
8919
|
+
const sessionContextLogs = sessionContextScope?.logs ?? [];
|
|
8920
|
+
const hasSessionContext = sessionContextScope !== null && sessionContextLogs.length > 0;
|
|
8921
|
+
const { totalIn: sessionContextTotalIn, totalOut: sessionContextTotalOut } = reactExports.useMemo(
|
|
8922
|
+
() => computeTokenSummary(sessionContextLogs),
|
|
8923
|
+
[sessionContextLogs]
|
|
8924
|
+
);
|
|
8925
|
+
const sessionContextSummary = reactExports.useMemo(
|
|
8926
|
+
() => buildSessionContextSummary(sessionContextLogs, providers),
|
|
8927
|
+
[sessionContextLogs, providers]
|
|
8928
|
+
);
|
|
7507
8929
|
const cacheTrends = reactExports.useMemo(() => computeCacheTrends(groups), [groups]);
|
|
7508
8930
|
const comparisonPredecessors = reactExports.useMemo(() => buildValidPredecessors(groups), [groups]);
|
|
7509
8931
|
const handleCompareWithPrevious = reactExports.useCallback(
|
|
@@ -7571,14 +8993,16 @@ function ProxyViewer({
|
|
|
7571
8993
|
] }),
|
|
7572
8994
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "justify-self-end", children: /* @__PURE__ */ jsxRuntimeExports.jsx(SettingsDialog, {}) })
|
|
7573
8995
|
] }),
|
|
7574
|
-
|
|
8996
|
+
hasSessionContext && sessionContextScope !== null && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
7575
8997
|
SessionContextBar,
|
|
7576
8998
|
{
|
|
7577
|
-
|
|
7578
|
-
logs,
|
|
7579
|
-
totalIn,
|
|
7580
|
-
totalOut,
|
|
7581
|
-
timeDisplayFormat
|
|
8999
|
+
label: sessionContextScope.label,
|
|
9000
|
+
logs: sessionContextLogs,
|
|
9001
|
+
totalIn: sessionContextTotalIn,
|
|
9002
|
+
totalOut: sessionContextTotalOut,
|
|
9003
|
+
timeDisplayFormat,
|
|
9004
|
+
contextSummary: sessionContextSummary,
|
|
9005
|
+
showBackLink: sessionContextScope.showBackLink
|
|
7582
9006
|
}
|
|
7583
9007
|
),
|
|
7584
9008
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3 mb-4", children: [
|
|
@@ -7617,7 +9041,7 @@ function ProxyViewer({
|
|
|
7617
9041
|
)
|
|
7618
9042
|
] }),
|
|
7619
9043
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1" }),
|
|
7620
|
-
!
|
|
9044
|
+
!hasSessionContext && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-muted-foreground text-xs font-mono", children: [
|
|
7621
9045
|
logs.length,
|
|
7622
9046
|
" request",
|
|
7623
9047
|
logs.length !== 1 ? "s" : "",
|
|
@@ -7689,7 +9113,7 @@ function ProxyViewer({
|
|
|
7689
9113
|
comparisonPredecessors,
|
|
7690
9114
|
onClearGroup,
|
|
7691
9115
|
standalone: groups.length === 1,
|
|
7692
|
-
hasPinnedSessionContext,
|
|
9116
|
+
hasPinnedSessionContext: hasSessionContext,
|
|
7693
9117
|
timeDisplayFormat
|
|
7694
9118
|
},
|
|
7695
9119
|
group.id
|
|
@@ -7976,12 +9400,14 @@ function ProxyViewerContainer({
|
|
|
7976
9400
|
] });
|
|
7977
9401
|
}
|
|
7978
9402
|
export {
|
|
9403
|
+
ANATOMY_ROLE_LABELS as A,
|
|
7979
9404
|
Badge as B,
|
|
7980
|
-
|
|
9405
|
+
CONTEXT_USAGE_THRESHOLDS as C,
|
|
7981
9406
|
Dialog as D,
|
|
7982
9407
|
JsonViewerFromString as J,
|
|
7983
9408
|
ProxyViewerContainer as P,
|
|
7984
|
-
|
|
9409
|
+
ROLE_COLOR_CLASSES as R,
|
|
9410
|
+
SegmentBar as S,
|
|
7985
9411
|
Tabs as T,
|
|
7986
9412
|
getConversationId as a,
|
|
7987
9413
|
DialogContent as b,
|
|
@@ -7998,13 +9424,15 @@ export {
|
|
|
7998
9424
|
TooltipTrigger as m,
|
|
7999
9425
|
TooltipContent as n,
|
|
8000
9426
|
Button as o,
|
|
8001
|
-
|
|
8002
|
-
|
|
9427
|
+
analyzeContextIntelligence as p,
|
|
9428
|
+
formatContextWindowTokens as q,
|
|
8003
9429
|
resolveLogFormat as r,
|
|
8004
9430
|
getStatusCategory as s,
|
|
8005
|
-
|
|
9431
|
+
Collapsible as t,
|
|
8006
9432
|
useProviders as u,
|
|
8007
|
-
|
|
8008
|
-
|
|
8009
|
-
|
|
9433
|
+
CollapsibleTrigger as v,
|
|
9434
|
+
CollapsibleContent as w,
|
|
9435
|
+
ScrollArea as x,
|
|
9436
|
+
JsonViewer as y,
|
|
9437
|
+
safeJsonValue as z
|
|
8010
9438
|
};
|