@tonyclaw/agent-inspector 2.0.24 → 2.0.25

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.
Files changed (30) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/{CompareDrawer-DrY-gAzy.js → CompareDrawer-DVNvS0GQ.js} +1 -1
  3. package/.output/public/assets/ProxyViewerContainer-By3XtQhZ.js +115 -0
  4. package/.output/public/assets/{ReplayDialog-CFyw6Vu4.js → ReplayDialog-Cw6bN-NR.js} +1 -1
  5. package/.output/public/assets/{RequestAnatomy-DHGJ9py7.js → RequestAnatomy-DnIsirBE.js} +1 -1
  6. package/.output/public/assets/{ResponseView-CB_3yc08.js → ResponseView-ygsUs7FU.js} +1 -1
  7. package/.output/public/assets/{StreamingChunkSequence-CqhHwgDn.js → StreamingChunkSequence-DMUn8qXi.js} +1 -1
  8. package/.output/public/assets/_sessionId-DoqvnFx7.js +1 -0
  9. package/.output/public/assets/index-B0YcFeL-.js +1 -0
  10. package/.output/public/assets/index-DsiKfWCp.css +1 -0
  11. package/.output/public/assets/{main-CVw7-JyE.js → main-kBQ49n13.js} +2 -2
  12. package/.output/server/{_sessionId-V33sBWBC.mjs → _sessionId-CYKaI68v.mjs} +2 -2
  13. package/.output/server/_ssr/{CompareDrawer-C_Fk9joM.mjs → CompareDrawer-CHk4p41X.mjs} +2 -2
  14. package/.output/server/_ssr/{ProxyViewerContainer-CyFKksQa.mjs → ProxyViewerContainer-Co9ENCKV.mjs} +208 -22
  15. package/.output/server/_ssr/{ReplayDialog-BpQHN7O2.mjs → ReplayDialog-BRs3fk8H.mjs} +3 -3
  16. package/.output/server/_ssr/{RequestAnatomy-CuhmKAma.mjs → RequestAnatomy-Cb68EeZz.mjs} +2 -2
  17. package/.output/server/_ssr/{ResponseView-CO6LyBmj.mjs → ResponseView-DGdbfEjs.mjs} +2 -2
  18. package/.output/server/_ssr/{StreamingChunkSequence-DLeVetNR.mjs → StreamingChunkSequence-DptYgirK.mjs} +2 -2
  19. package/.output/server/_ssr/{index-DWUKl9Bf.mjs → index-Ef5mCuww.mjs} +2 -2
  20. package/.output/server/_ssr/index.mjs +2 -2
  21. package/.output/server/_ssr/{router-BrK_DcTS.mjs → router-Cvv7SJnd.mjs} +3 -3
  22. package/.output/server/{_tanstack-start-manifest_v-BjW2XeWE.mjs → _tanstack-start-manifest_v-DQoXxIFQ.mjs} +1 -1
  23. package/.output/server/index.mjs +55 -55
  24. package/package.json +1 -1
  25. package/src/components/providers/ProviderForm.tsx +176 -16
  26. package/src/lib/utils.ts +46 -0
  27. package/.output/public/assets/ProxyViewerContainer-CiLvRNie.js +0 -115
  28. package/.output/public/assets/_sessionId-CgG9CQ9r.js +0 -1
  29. package/.output/public/assets/index-780zTVwp.css +0 -1
  30. package/.output/public/assets/index-CViEXkqa.js +0 -1
@@ -1,4 +1,4 @@
1
- const tsrStartManifest = () => ({ "routes": { "__root__": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/__root.tsx", "children": ["/", "/api/config", "/api/health", "/api/logs", "/api/mcp", "/api/models", "/api/providers", "/api/sessions", "/proxy/$", "/session/$sessionId", "/api/knowledge/candidates", "/api/knowledge/project-context", "/api/knowledge/search", "/api/knowledge/sessions/$sessionId/candidates"], "preloads": ["/assets/main-CVw7-JyE.js"], "assets": [] }, "/": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/index.tsx", "assets": [], "preloads": ["/assets/index-CViEXkqa.js", "/assets/ProxyViewerContainer-CiLvRNie.js"] }, "/api/config": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/config.ts", "children": ["/api/config/paths"] }, "/api/health": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/health.ts" }, "/api/logs": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/logs.ts", "children": ["/api/logs/$id", "/api/logs/stream"] }, "/api/mcp": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/mcp.ts" }, "/api/models": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/models.ts" }, "/api/providers": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/providers.ts", "children": ["/api/providers/$providerId", "/api/providers/export", "/api/providers/import", "/api/providers/scan"] }, "/api/sessions": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/sessions.ts" }, "/proxy/$": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/proxy/$.ts" }, "/session/$sessionId": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/session/$sessionId.tsx", "assets": [], "preloads": ["/assets/_sessionId-CgG9CQ9r.js", "/assets/ProxyViewerContainer-CiLvRNie.js"] }, "/api/config/paths": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/config.paths.ts" }, "/api/knowledge/candidates": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/knowledge.candidates.ts", "children": ["/api/knowledge/candidates/$candidateId"] }, "/api/knowledge/project-context": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/knowledge.project-context.ts" }, "/api/knowledge/search": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/knowledge.search.ts" }, "/api/logs/$id": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/logs.$id.ts", "children": ["/api/logs/$id/chunks", "/api/logs/$id/replay"] }, "/api/logs/stream": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/logs.stream.ts" }, "/api/providers/$providerId": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/providers.$providerId.ts", "children": ["/api/providers/$providerId/model-metadata", "/api/providers/$providerId/test"] }, "/api/providers/export": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/providers.export.ts" }, "/api/providers/import": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/providers.import.ts" }, "/api/providers/scan": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/providers.scan.ts" }, "/api/knowledge/candidates/$candidateId": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/knowledge.candidates.$candidateId.ts", "children": ["/api/knowledge/candidates/$candidateId/promote"] }, "/api/logs/$id/chunks": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/logs.$id.chunks.ts" }, "/api/logs/$id/replay": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/logs.$id.replay.ts" }, "/api/providers/$providerId/model-metadata": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/providers.$providerId.model-metadata.ts" }, "/api/providers/$providerId/test": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/providers.$providerId.test.ts", "children": ["/api/providers/$providerId/test/log"] }, "/api/knowledge/candidates/$candidateId/promote": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/knowledge.candidates.$candidateId.promote.ts" }, "/api/knowledge/sessions/$sessionId/candidates": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/knowledge.sessions.$sessionId.candidates.ts" }, "/api/providers/$providerId/test/log": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/providers.$providerId.test.log.ts" } }, "clientEntry": "/assets/main-CVw7-JyE.js" });
1
+ const tsrStartManifest = () => ({ "routes": { "__root__": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/__root.tsx", "children": ["/", "/api/config", "/api/health", "/api/logs", "/api/mcp", "/api/models", "/api/providers", "/api/sessions", "/proxy/$", "/session/$sessionId", "/api/knowledge/candidates", "/api/knowledge/project-context", "/api/knowledge/search", "/api/knowledge/sessions/$sessionId/candidates"], "preloads": ["/assets/main-kBQ49n13.js"], "assets": [] }, "/": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/index.tsx", "assets": [], "preloads": ["/assets/index-B0YcFeL-.js", "/assets/ProxyViewerContainer-By3XtQhZ.js"] }, "/api/config": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/config.ts", "children": ["/api/config/paths"] }, "/api/health": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/health.ts" }, "/api/logs": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/logs.ts", "children": ["/api/logs/$id", "/api/logs/stream"] }, "/api/mcp": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/mcp.ts" }, "/api/models": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/models.ts" }, "/api/providers": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/providers.ts", "children": ["/api/providers/$providerId", "/api/providers/export", "/api/providers/import", "/api/providers/scan"] }, "/api/sessions": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/sessions.ts" }, "/proxy/$": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/proxy/$.ts" }, "/session/$sessionId": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/session/$sessionId.tsx", "assets": [], "preloads": ["/assets/_sessionId-DoqvnFx7.js", "/assets/ProxyViewerContainer-By3XtQhZ.js"] }, "/api/config/paths": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/config.paths.ts" }, "/api/knowledge/candidates": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/knowledge.candidates.ts", "children": ["/api/knowledge/candidates/$candidateId"] }, "/api/knowledge/project-context": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/knowledge.project-context.ts" }, "/api/knowledge/search": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/knowledge.search.ts" }, "/api/logs/$id": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/logs.$id.ts", "children": ["/api/logs/$id/chunks", "/api/logs/$id/replay"] }, "/api/logs/stream": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/logs.stream.ts" }, "/api/providers/$providerId": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/providers.$providerId.ts", "children": ["/api/providers/$providerId/model-metadata", "/api/providers/$providerId/test"] }, "/api/providers/export": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/providers.export.ts" }, "/api/providers/import": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/providers.import.ts" }, "/api/providers/scan": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/providers.scan.ts" }, "/api/knowledge/candidates/$candidateId": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/knowledge.candidates.$candidateId.ts", "children": ["/api/knowledge/candidates/$candidateId/promote"] }, "/api/logs/$id/chunks": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/logs.$id.chunks.ts" }, "/api/logs/$id/replay": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/logs.$id.replay.ts" }, "/api/providers/$providerId/model-metadata": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/providers.$providerId.model-metadata.ts" }, "/api/providers/$providerId/test": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/providers.$providerId.test.ts", "children": ["/api/providers/$providerId/test/log"] }, "/api/knowledge/candidates/$candidateId/promote": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/knowledge.candidates.$candidateId.promote.ts" }, "/api/knowledge/sessions/$sessionId/candidates": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/knowledge.sessions.$sessionId.candidates.ts" }, "/api/providers/$providerId/test/log": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/providers.$providerId.test.log.ts" } }, "clientEntry": "/assets/main-kBQ49n13.js" });
2
2
  export {
3
3
  tsrStartManifest
4
4
  };
@@ -38,100 +38,100 @@ const assets = {
38
38
  "/assets/alibaba-TTwafVwX.svg": {
39
39
  "type": "image/svg+xml",
40
40
  "etag": '"171b-6dyV5K8QjiaY35sN9qNprh9zDIs"',
41
- "mtime": "2026-06-29T12:29:42.542Z",
41
+ "mtime": "2026-06-29T13:24:14.325Z",
42
42
  "size": 5915,
43
43
  "path": "../public/assets/alibaba-TTwafVwX.svg"
44
44
  },
45
- "/assets/CompareDrawer-DrY-gAzy.js": {
45
+ "/assets/CompareDrawer-DVNvS0GQ.js": {
46
46
  "type": "text/javascript; charset=utf-8",
47
- "etag": '"49fe-MpHiIJshHfw4LFDNCsCMDZDrWGg"',
48
- "mtime": "2026-06-29T12:29:42.542Z",
47
+ "etag": '"49fe-2PP252UHJD4ZMXt5VpoaibQADuU"',
48
+ "mtime": "2026-06-29T13:24:14.326Z",
49
49
  "size": 18942,
50
- "path": "../public/assets/CompareDrawer-DrY-gAzy.js"
50
+ "path": "../public/assets/CompareDrawer-DVNvS0GQ.js"
51
51
  },
52
- "/assets/index-CViEXkqa.js": {
52
+ "/assets/index-B0YcFeL-.js": {
53
53
  "type": "text/javascript; charset=utf-8",
54
- "etag": '"74-U7AFlYRRMX3IzoDVXXko4xBPBvM"',
55
- "mtime": "2026-06-29T12:29:42.542Z",
54
+ "etag": '"74-ohkGewLZlIYioQpffENsT1gm8EE"',
55
+ "mtime": "2026-06-29T13:24:14.325Z",
56
56
  "size": 116,
57
- "path": "../public/assets/index-CViEXkqa.js"
58
- },
59
- "/assets/ReplayDialog-CFyw6Vu4.js": {
60
- "type": "text/javascript; charset=utf-8",
61
- "etag": '"318c-yVT324FTJnGgK/IiiYVI1v6/IEw"',
62
- "mtime": "2026-06-29T12:29:42.542Z",
63
- "size": 12684,
64
- "path": "../public/assets/ReplayDialog-CFyw6Vu4.js"
65
- },
66
- "/assets/RequestAnatomy-DHGJ9py7.js": {
67
- "type": "text/javascript; charset=utf-8",
68
- "etag": '"4485-2C+vaW+lYZaQ0wLkm/KTOsK3AOY"',
69
- "mtime": "2026-06-29T12:29:42.542Z",
70
- "size": 17541,
71
- "path": "../public/assets/RequestAnatomy-DHGJ9py7.js"
57
+ "path": "../public/assets/index-B0YcFeL-.js"
72
58
  },
73
59
  "/assets/minimax-BPMzvuL-.jpeg": {
74
60
  "type": "image/jpeg",
75
61
  "etag": '"1b06-IwivU89ko5UTMUM1/t7hn4sQK9A"',
76
- "mtime": "2026-06-29T12:29:42.542Z",
62
+ "mtime": "2026-06-29T13:24:14.325Z",
77
63
  "size": 6918,
78
64
  "path": "../public/assets/minimax-BPMzvuL-.jpeg"
79
65
  },
80
- "/assets/index-780zTVwp.css": {
66
+ "/assets/index-DsiKfWCp.css": {
81
67
  "type": "text/css; charset=utf-8",
82
- "etag": '"1a380-Lz+Shs13XyKSlh9PTaGE50w/BpU"',
83
- "mtime": "2026-06-29T12:29:42.542Z",
84
- "size": 107392,
85
- "path": "../public/assets/index-780zTVwp.css"
68
+ "etag": '"1a3d5-pnLJEAiyUUKlRw5OUnvwf1dCQeI"',
69
+ "mtime": "2026-06-29T13:24:14.325Z",
70
+ "size": 107477,
71
+ "path": "../public/assets/index-DsiKfWCp.css"
72
+ },
73
+ "/assets/main-kBQ49n13.js": {
74
+ "type": "text/javascript; charset=utf-8",
75
+ "etag": '"51726-+FidFdV98/WDrHsP2x5s4MGlsZI"',
76
+ "mtime": "2026-06-29T13:24:14.325Z",
77
+ "size": 333606,
78
+ "path": "../public/assets/main-kBQ49n13.js"
86
79
  },
87
- "/assets/ResponseView-CB_3yc08.js": {
80
+ "/assets/ResponseView-ygsUs7FU.js": {
88
81
  "type": "text/javascript; charset=utf-8",
89
- "etag": '"30d0-M6AktqEyUeyPg4kcv2fzak6vFlw"',
90
- "mtime": "2026-06-29T12:29:42.542Z",
82
+ "etag": '"30d0-VoRHAUzRfGHjVqxMmhp9+gxQNvg"',
83
+ "mtime": "2026-06-29T13:24:14.326Z",
91
84
  "size": 12496,
92
- "path": "../public/assets/ResponseView-CB_3yc08.js"
85
+ "path": "../public/assets/ResponseView-ygsUs7FU.js"
93
86
  },
94
- "/assets/StreamingChunkSequence-CqhHwgDn.js": {
87
+ "/assets/ReplayDialog-Cw6bN-NR.js": {
95
88
  "type": "text/javascript; charset=utf-8",
96
- "etag": '"d57-C9p9lFGRMu7L/bxJPes/RpOKks0"',
97
- "mtime": "2026-06-29T12:29:42.542Z",
89
+ "etag": '"318c-Ec8xMdTaVtWn+gQco0A8wJxm/Zw"',
90
+ "mtime": "2026-06-29T13:24:14.325Z",
91
+ "size": 12684,
92
+ "path": "../public/assets/ReplayDialog-Cw6bN-NR.js"
93
+ },
94
+ "/assets/StreamingChunkSequence-DMUn8qXi.js": {
95
+ "type": "text/javascript; charset=utf-8",
96
+ "etag": '"d57-sKePDdoc8LggANuuW66cotctSKo"',
97
+ "mtime": "2026-06-29T13:24:14.326Z",
98
98
  "size": 3415,
99
- "path": "../public/assets/StreamingChunkSequence-CqhHwgDn.js"
99
+ "path": "../public/assets/StreamingChunkSequence-DMUn8qXi.js"
100
100
  },
101
- "/assets/_sessionId-CgG9CQ9r.js": {
101
+ "/assets/_sessionId-DoqvnFx7.js": {
102
102
  "type": "text/javascript; charset=utf-8",
103
- "etag": '"d2-QqZWnWujfEY0vnXMOs1NyoEJukI"',
104
- "mtime": "2026-06-29T12:29:42.542Z",
103
+ "etag": '"d2-eM4ckKV5XpF8O96oypRgw/AMrPQ"',
104
+ "mtime": "2026-06-29T13:24:14.325Z",
105
105
  "size": 210,
106
- "path": "../public/assets/_sessionId-CgG9CQ9r.js"
106
+ "path": "../public/assets/_sessionId-DoqvnFx7.js"
107
+ },
108
+ "/assets/RequestAnatomy-DnIsirBE.js": {
109
+ "type": "text/javascript; charset=utf-8",
110
+ "etag": '"4485-FLpxtJEMMSiId/JKv/qeTF/xc4E"',
111
+ "mtime": "2026-06-29T13:24:14.326Z",
112
+ "size": 17541,
113
+ "path": "../public/assets/RequestAnatomy-DnIsirBE.js"
107
114
  },
108
115
  "/assets/zhipuai-BPNAnxo-.svg": {
109
116
  "type": "image/svg+xml",
110
117
  "etag": '"2bf8-hNaLCTi89nOFCsIIfWpP/jrfo0s"',
111
- "mtime": "2026-06-29T12:29:42.542Z",
118
+ "mtime": "2026-06-29T13:24:14.325Z",
112
119
  "size": 11256,
113
120
  "path": "../public/assets/zhipuai-BPNAnxo-.svg"
114
121
  },
115
- "/assets/main-CVw7-JyE.js": {
116
- "type": "text/javascript; charset=utf-8",
117
- "etag": '"51726-W+9s8t7uIYMEgBdKzOagDvVRdKc"',
118
- "mtime": "2026-06-29T12:29:42.542Z",
119
- "size": 333606,
120
- "path": "../public/assets/main-CVw7-JyE.js"
121
- },
122
122
  "/assets/qwen-CONDcHqt.png": {
123
123
  "type": "image/png",
124
124
  "etag": '"572c3-cdJAPaHdOvFCGzuaQjagdgOu6XE"',
125
- "mtime": "2026-06-29T12:29:42.542Z",
125
+ "mtime": "2026-06-29T13:24:14.325Z",
126
126
  "size": 357059,
127
127
  "path": "../public/assets/qwen-CONDcHqt.png"
128
128
  },
129
- "/assets/ProxyViewerContainer-CiLvRNie.js": {
129
+ "/assets/ProxyViewerContainer-By3XtQhZ.js": {
130
130
  "type": "text/javascript; charset=utf-8",
131
- "etag": '"ae3c0-Cpzx6KeaKuGDUgS/cMjRV9y+6zQ"',
132
- "mtime": "2026-06-29T12:29:42.542Z",
133
- "size": 713664,
134
- "path": "../public/assets/ProxyViewerContainer-CiLvRNie.js"
131
+ "etag": '"af111-Qj0fjruqs4EheKPZxm5LynPyIwI"',
132
+ "mtime": "2026-06-29T13:24:14.326Z",
133
+ "size": 717073,
134
+ "path": "../public/assets/ProxyViewerContainer-By3XtQhZ.js"
135
135
  }
136
136
  };
137
137
  function readAsset(id) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tonyclaw/agent-inspector",
3
- "version": "2.0.24",
3
+ "version": "2.0.25",
4
4
  "type": "module",
5
5
  "description": "Agent observability and knowledge capture layer for AI coding tools.",
6
6
  "license": "MIT",
@@ -1,9 +1,11 @@
1
1
  import { type JSX, useState, useEffect, useRef } from "react";
2
2
  import { Button } from "../ui/button";
3
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../ui/select";
3
4
  import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from "../ui/tooltip";
4
5
  import { Eye, EyeOff, Copy, Check, ChevronDown } from "lucide-react";
5
6
  import type { ProviderConfig, ProviderModelMetadata } from "../../lib/providerContract";
6
7
  import { maskApiKey } from "../../lib/mask";
8
+ import { formatContextWindowInput, parseContextWindowTokensInput } from "../../lib/utils";
7
9
 
8
10
  const ZHIPU_PROVIDER_KEYWORDS = ["zhipu", "zhipuai", "bigmodel", "z.ai", "zai", "glm"];
9
11
  const ZHIPU_CODING_PROVIDER_KEYWORDS = [
@@ -129,11 +131,29 @@ type ProviderFormProps = {
129
131
 
130
132
  type ModelMetadataDraft = {
131
133
  contextWindow: string;
134
+ contextWindowMode: ContextWindowMode;
132
135
  outputLimit: string;
133
136
  };
134
137
 
138
+ type ContextWindowMode = "unset" | "preset" | "custom";
139
+
140
+ type ContextWindowUnit = "K" | "M" | "tokens";
141
+
142
+ const CONTEXT_WINDOW_UNSET = "__unset__";
143
+ const CONTEXT_WINDOW_CUSTOM = "__custom__";
144
+
145
+ const CONTEXT_WINDOW_PRESETS = [
146
+ { value: "32K", label: "Small" },
147
+ { value: "64K", label: "Standard" },
148
+ { value: "128K", label: "Medium" },
149
+ { value: "200K", label: "Long" },
150
+ { value: "256K", label: "Extended" },
151
+ { value: "512K", label: "Large" },
152
+ { value: "1M", label: "Max" },
153
+ ];
154
+
135
155
  function emptyMetadataDraft(): ModelMetadataDraft {
136
- return { contextWindow: "", outputLimit: "" };
156
+ return { contextWindow: "", contextWindowMode: "unset", outputLimit: "" };
137
157
  }
138
158
 
139
159
  function normalizeModelName(value: string): string {
@@ -152,14 +172,74 @@ function findModelMetadata(
152
172
  );
153
173
  }
154
174
 
175
+ function findContextWindowPreset(value: string): string | null {
176
+ const tokens = parseContextWindowTokensInput(value);
177
+ if (tokens === null) return null;
178
+ for (const preset of CONTEXT_WINDOW_PRESETS) {
179
+ if (parseContextWindowTokensInput(preset.value) === tokens) return preset.value;
180
+ }
181
+ return null;
182
+ }
183
+
184
+ function contextWindowModeForValue(value: string): ContextWindowMode {
185
+ if (value.trim() === "") return "unset";
186
+ return findContextWindowPreset(value) !== null ? "preset" : "custom";
187
+ }
188
+
189
+ function selectedContextWindowPresetValue(draft: ModelMetadataDraft): string {
190
+ switch (draft.contextWindowMode) {
191
+ case "unset":
192
+ return CONTEXT_WINDOW_UNSET;
193
+ case "custom":
194
+ return CONTEXT_WINDOW_CUSTOM;
195
+ case "preset":
196
+ return findContextWindowPreset(draft.contextWindow) ?? CONTEXT_WINDOW_CUSTOM;
197
+ }
198
+ }
199
+
200
+ function splitContextWindowEditorValue(value: string): { amount: string; unit: ContextWindowUnit } {
201
+ const trimmed = value.trim().replaceAll(",", "");
202
+ if (trimmed === "") return { amount: "", unit: "K" };
203
+ const match = /^(\d+(?:\.\d+)?)\s*([kKmM])?$/.exec(trimmed);
204
+ if (match === null) return { amount: trimmed, unit: "K" };
205
+ const amount = match[1];
206
+ if (amount === undefined) return { amount: trimmed, unit: "K" };
207
+ const unit = match[2];
208
+ if (unit === undefined || unit === "") return { amount, unit: "tokens" };
209
+ switch (unit.toLowerCase()) {
210
+ case "k":
211
+ return { amount, unit: "K" };
212
+ case "m":
213
+ return { amount, unit: "M" };
214
+ default:
215
+ return { amount: trimmed, unit: "K" };
216
+ }
217
+ }
218
+
219
+ function joinContextWindowEditorValue(amount: string, unit: ContextWindowUnit): string {
220
+ const trimmed = amount.trim();
221
+ if (trimmed === "") return "";
222
+ switch (unit) {
223
+ case "K":
224
+ return `${trimmed}K`;
225
+ case "M":
226
+ return `${trimmed}M`;
227
+ case "tokens":
228
+ return trimmed;
229
+ }
230
+ }
231
+
155
232
  function createMetadataDrafts(
156
233
  provider: ProviderConfig | undefined,
157
234
  models: readonly string[],
158
235
  ): ModelMetadataDraft[] {
159
236
  return models.map((model) => {
160
237
  const metadata = findModelMetadata(provider, model);
238
+ const contextWindow =
239
+ metadata?.contextWindow !== undefined ? formatContextWindowInput(metadata.contextWindow) : "";
161
240
  return {
162
- contextWindow: metadata?.contextWindow?.toString() ?? "",
241
+ contextWindow,
242
+ contextWindowMode: contextWindowModeForValue(contextWindow),
163
243
  outputLimit: metadata?.outputLimit?.toString() ?? "",
164
244
  };
165
245
  });
@@ -315,11 +395,11 @@ export function ProviderForm({ provider, onSubmit, onCancel }: ProviderFormProps
315
395
  newErrors.modelMetadataUrl = "Invalid URL format";
316
396
  }
317
397
  modelMetadataDrafts.forEach((draft, index) => {
318
- const contextWindow = readPositiveIntegerInput(draft.contextWindow);
398
+ const contextWindow = parseContextWindowTokensInput(draft.contextWindow);
319
399
  const outputLimit = readPositiveIntegerInput(draft.outputLimit);
320
400
  if (draft.contextWindow.trim() !== "" && contextWindow === null) {
321
401
  newErrors[`modelMetadata.${index}.contextWindow`] =
322
- "Context window must be a positive integer";
402
+ "Context window must be a positive number, optionally using K or M";
323
403
  }
324
404
  if (draft.outputLimit.trim() !== "" && outputLimit === null) {
325
405
  newErrors[`modelMetadata.${index}.outputLimit`] = "Output limit must be a positive integer";
@@ -361,6 +441,51 @@ export function ProviderForm({ provider, onSubmit, onCancel }: ProviderFormProps
361
441
  });
362
442
  }
363
443
 
444
+ function updateContextWindowPreset(index: number, value: string): void {
445
+ switch (value) {
446
+ case CONTEXT_WINDOW_UNSET:
447
+ updateMetadataDraft(index, { contextWindow: "", contextWindowMode: "unset" });
448
+ return;
449
+ case CONTEXT_WINDOW_CUSTOM: {
450
+ const current = modelMetadataDrafts[index] ?? emptyMetadataDraft();
451
+ updateMetadataDraft(index, {
452
+ contextWindow: current.contextWindow,
453
+ contextWindowMode: "custom",
454
+ });
455
+ return;
456
+ }
457
+ default:
458
+ updateMetadataDraft(index, { contextWindow: value, contextWindowMode: "preset" });
459
+ return;
460
+ }
461
+ }
462
+
463
+ function updateContextWindowAmount(index: number, amount: string): void {
464
+ const current = modelMetadataDrafts[index] ?? emptyMetadataDraft();
465
+ const editorValue = splitContextWindowEditorValue(current.contextWindow);
466
+ updateMetadataDraft(index, {
467
+ contextWindow: joinContextWindowEditorValue(amount, editorValue.unit),
468
+ contextWindowMode: "custom",
469
+ });
470
+ }
471
+
472
+ function updateContextWindowUnit(index: number, unit: string): void {
473
+ const current = modelMetadataDrafts[index] ?? emptyMetadataDraft();
474
+ const editorValue = splitContextWindowEditorValue(current.contextWindow);
475
+ switch (unit) {
476
+ case "K":
477
+ case "M":
478
+ case "tokens":
479
+ updateMetadataDraft(index, {
480
+ contextWindow: joinContextWindowEditorValue(editorValue.amount, unit),
481
+ contextWindowMode: "custom",
482
+ });
483
+ return;
484
+ default:
485
+ return;
486
+ }
487
+ }
488
+
364
489
  function removeModel(index: number): void {
365
490
  setModels((prev) => prev.filter((_, idx) => idx !== index));
366
491
  setModelMetadataDrafts((prev) => prev.filter((_, idx) => idx !== index));
@@ -379,7 +504,7 @@ export function ProviderForm({ provider, onSubmit, onCancel }: ProviderFormProps
379
504
  const trimmedModel = model.trim();
380
505
  if (trimmedModel === "") return;
381
506
  const draft = modelMetadataDrafts[index] ?? emptyMetadataDraft();
382
- const contextWindow = readPositiveIntegerInput(draft.contextWindow);
507
+ const contextWindow = parseContextWindowTokensInput(draft.contextWindow);
383
508
  const outputLimit = readPositiveIntegerInput(draft.outputLimit);
384
509
  if (contextWindow === null && outputLimit === null) return;
385
510
 
@@ -548,6 +673,8 @@ export function ProviderForm({ provider, onSubmit, onCancel }: ProviderFormProps
548
673
  const draft = modelMetadataDrafts[i] ?? emptyMetadataDraft();
549
674
  const contextWindowError = errors[`modelMetadata.${i}.contextWindow`];
550
675
  const outputLimitError = errors[`modelMetadata.${i}.outputLimit`];
676
+ const contextWindowPresetValue = selectedContextWindowPresetValue(draft);
677
+ const contextWindowEditorValue = splitContextWindowEditorValue(draft.contextWindow);
551
678
  return (
552
679
  <div
553
680
  key={i}
@@ -624,21 +751,54 @@ export function ProviderForm({ provider, onSubmit, onCancel }: ProviderFormProps
624
751
  </div>
625
752
 
626
753
  <div className="grid gap-2 sm:grid-cols-2">
627
- <label className="space-y-1 text-xs text-muted-foreground">
754
+ <div className="space-y-1 text-xs text-muted-foreground">
628
755
  <span>Context Window</span>
629
- <input
630
- type="number"
631
- min={1}
632
- step={1000}
633
- value={draft.contextWindow}
634
- onChange={(e) => updateMetadataDraft(i, { contextWindow: e.target.value })}
635
- placeholder="e.g. 1048576"
636
- 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]"
637
- />
756
+ <Select
757
+ value={contextWindowPresetValue}
758
+ onValueChange={(value) => updateContextWindowPreset(i, value)}
759
+ >
760
+ <SelectTrigger size="sm" className="w-full bg-background font-mono text-xs">
761
+ <SelectValue />
762
+ </SelectTrigger>
763
+ <SelectContent className="max-h-64">
764
+ <SelectItem value={CONTEXT_WINDOW_UNSET}>Not set</SelectItem>
765
+ {CONTEXT_WINDOW_PRESETS.map((preset) => (
766
+ <SelectItem key={preset.value} value={preset.value}>
767
+ {preset.label} · {preset.value}
768
+ </SelectItem>
769
+ ))}
770
+ <SelectItem value={CONTEXT_WINDOW_CUSTOM}>Custom</SelectItem>
771
+ </SelectContent>
772
+ </Select>
773
+ {draft.contextWindowMode === "custom" && (
774
+ <div className="grid grid-cols-[minmax(0,1fr)_5.75rem] gap-1.5">
775
+ <input
776
+ type="text"
777
+ inputMode="decimal"
778
+ value={contextWindowEditorValue.amount}
779
+ onChange={(e) => updateContextWindowAmount(i, e.target.value)}
780
+ placeholder="128"
781
+ 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]"
782
+ />
783
+ <Select
784
+ value={contextWindowEditorValue.unit}
785
+ onValueChange={(value) => updateContextWindowUnit(i, value)}
786
+ >
787
+ <SelectTrigger size="sm" className="w-full bg-background font-mono text-xs">
788
+ <SelectValue />
789
+ </SelectTrigger>
790
+ <SelectContent>
791
+ <SelectItem value="K">K</SelectItem>
792
+ <SelectItem value="M">M</SelectItem>
793
+ <SelectItem value="tokens">tokens</SelectItem>
794
+ </SelectContent>
795
+ </Select>
796
+ </div>
797
+ )}
638
798
  {contextWindowError !== undefined && (
639
799
  <span className="block text-destructive">{contextWindowError}</span>
640
800
  )}
641
- </label>
801
+ </div>
642
802
  <label className="space-y-1 text-xs text-muted-foreground">
643
803
  <span>Output Limit</span>
644
804
  <input
package/src/lib/utils.ts CHANGED
@@ -17,6 +17,52 @@ export function formatContextWindowTokens(count: number): string {
17
17
  return (count / 1024).toFixed(1).replace(/\.0$/, "") + "K";
18
18
  }
19
19
 
20
+ function formatExactContextUnit(count: number, multiplier: number, unit: "K" | "M"): string | null {
21
+ const tenths = count * 10;
22
+ if (!Number.isSafeInteger(tenths) || tenths % multiplier !== 0) return null;
23
+ return (tenths / multiplier / 10).toFixed(1).replace(/\.0$/, "") + unit;
24
+ }
25
+
26
+ export function formatContextWindowInput(count: number): string {
27
+ if (!Number.isSafeInteger(count) || count <= 0) return count.toString();
28
+ if (count >= 1024 * 1024) {
29
+ const megaValue = formatExactContextUnit(count, 1024 * 1024, "M");
30
+ if (megaValue !== null) return megaValue;
31
+ }
32
+ const kiloValue = formatExactContextUnit(count, 1024, "K");
33
+ if (kiloValue !== null) return kiloValue;
34
+ return count.toString();
35
+ }
36
+
37
+ function contextWindowUnitMultiplier(unit: string | undefined): number | null {
38
+ if (unit === undefined || unit === "") return 1;
39
+ switch (unit.toLowerCase()) {
40
+ case "k":
41
+ return 1024;
42
+ case "m":
43
+ return 1024 * 1024;
44
+ default:
45
+ return null;
46
+ }
47
+ }
48
+
49
+ export function parseContextWindowTokensInput(value: string): number | null {
50
+ const trimmed = value.trim().replaceAll(",", "");
51
+ if (trimmed === "") return null;
52
+ const match = /^(\d+(?:\.\d+)?)\s*([kKmM])?$/.exec(trimmed);
53
+ if (match === null) return null;
54
+ const amountText = match[1];
55
+ if (amountText === undefined) return null;
56
+ const amount = Number(amountText);
57
+ const unit = match[2];
58
+ const multiplier = contextWindowUnitMultiplier(unit);
59
+ if (!Number.isFinite(amount) || amount <= 0 || multiplier === null) return null;
60
+ if ((unit === undefined || unit === "") && !Number.isInteger(amount)) return null;
61
+ const tokens = Math.round(amount * multiplier);
62
+ if (!Number.isSafeInteger(tokens) || tokens <= 0) return null;
63
+ return tokens;
64
+ }
65
+
20
66
  export type StatusCategory = "success" | "client_error" | "server_error" | "pending";
21
67
 
22
68
  export function getStatusCategory(status: number | null): StatusCategory {