@tonyclaw/agent-inspector 2.0.8 → 2.0.10
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-Djb4XFZS.js → CompareDrawer-BWMipEjS.js} +1 -1
- package/.output/public/assets/ProxyViewerContainer-DIAgurux.js +114 -0
- package/.output/public/assets/{ReplayDialog-DBPMIs2I.js → ReplayDialog-DaSzZsvM.js} +1 -1
- package/.output/public/assets/RequestAnatomy-BfQmPHCd.js +1 -0
- package/.output/public/assets/ResponseView-CblEWDMx.js +1 -0
- package/.output/public/assets/StreamingChunkSequence-B4atnL99.js +1 -0
- package/.output/public/assets/_sessionId-DUzYnZXV.js +1 -0
- package/.output/public/assets/index-CxhRsekH.css +1 -0
- package/.output/public/assets/index-jagRsQaV.js +1 -0
- package/.output/public/assets/{main-CwhqfJqM.js → main-DE6Y4-wV.js} +2 -2
- package/.output/server/{_sessionId-DzBy9gLa.mjs → _sessionId-61HO9rUI.mjs} +3 -3
- package/.output/server/_ssr/{CompareDrawer-C5NBvE2e.mjs → CompareDrawer-CSPqneYm.mjs} +2 -2
- package/.output/server/_ssr/{ProxyViewerContainer-DiZjOTQT.mjs → ProxyViewerContainer-DrSMa5O7.mjs} +159 -14
- package/.output/server/_ssr/{ReplayDialog-EpvTwFvp.mjs → ReplayDialog-Dn3naENv.mjs} +3 -3
- package/.output/server/_ssr/{RequestAnatomy-DaQjhixp.mjs → RequestAnatomy-D1X3f3AX.mjs} +336 -42
- package/.output/server/_ssr/{ResponseView-D8tGHCGY.mjs → ResponseView-CwXAWOoE.mjs} +2 -2
- package/.output/server/_ssr/{StreamingChunkSequence-DGQnb1QE.mjs → StreamingChunkSequence-DRSR2FfN.mjs} +2 -2
- package/.output/server/_ssr/{index-N25hqa7t.mjs → index-D4zn5CAg.mjs} +2 -2
- package/.output/server/_ssr/index.mjs +2 -2
- package/.output/server/_ssr/{router-Bf0m6F0G.mjs → router-CSRaa_tu.mjs} +531 -111
- package/.output/server/{_tanstack-start-manifest_v-DlAyJ5DB.mjs → _tanstack-start-manifest_v-CfvVUYYj.mjs} +1 -1
- package/.output/server/index.mjs +60 -60
- package/bin/agent-inspector.js +2 -0
- package/package.json +3 -2
- package/src/components/providers/ProviderCard.tsx +59 -5
- package/src/components/providers/ProviderForm.tsx +42 -0
- package/src/components/providers/ProvidersPanel.tsx +68 -0
- package/src/components/proxy-viewer/LogEntry.tsx +6 -1
- package/src/components/proxy-viewer/anatomy/RequestAnatomy.tsx +134 -3
- package/src/components/proxy-viewer/anatomy/contextIntelligence.ts +298 -40
- package/src/lib/providerContract.ts +12 -0
- package/src/lib/providerModelMetadata.ts +327 -0
- package/src/proxy/providers.ts +112 -52
- package/src/routes/api/providers.$providerId.model-metadata.ts +134 -0
- package/src/routes/api/providers.$providerId.ts +3 -0
- package/src/routes/api/providers.ts +2 -0
- package/.output/public/assets/ProxyViewerContainer-CWdkPZsJ.js +0 -114
- package/.output/public/assets/RequestAnatomy-FBfRNQN7.js +0 -1
- package/.output/public/assets/ResponseView-Db4pJL7U.js +0 -1
- package/.output/public/assets/StreamingChunkSequence-BH175O6R.js +0 -1
- package/.output/public/assets/_sessionId-3ipVoQtW.js +0 -1
- package/.output/public/assets/index-Bo1dGS4R.css +0 -1
- package/.output/public/assets/index-CJgaCJo8.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-
|
|
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-DE6Y4-wV.js"], "assets": [] }, "/": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/index.tsx", "assets": [], "preloads": ["/assets/index-jagRsQaV.js", "/assets/ProxyViewerContainer-DIAgurux.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-DUzYnZXV.js", "/assets/ProxyViewerContainer-DIAgurux.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-DE6Y4-wV.js" });
|
|
2
2
|
export {
|
|
3
3
|
tsrStartManifest
|
|
4
4
|
};
|
package/.output/server/index.mjs
CHANGED
|
@@ -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-
|
|
41
|
+
"mtime": "2026-06-23T07:22:07.083Z",
|
|
42
42
|
"size": 5915,
|
|
43
43
|
"path": "../public/assets/alibaba-TTwafVwX.svg"
|
|
44
44
|
},
|
|
45
|
-
"/assets/index-
|
|
45
|
+
"/assets/index-jagRsQaV.js": {
|
|
46
46
|
"type": "text/javascript; charset=utf-8",
|
|
47
|
-
"etag": '"74-
|
|
48
|
-
"mtime": "2026-06-
|
|
47
|
+
"etag": '"74-o8aH3a+LQxw8VtSMo1zFH2AFd9s"',
|
|
48
|
+
"mtime": "2026-06-23T07:22:07.084Z",
|
|
49
49
|
"size": 116,
|
|
50
|
-
"path": "../public/assets/index-
|
|
50
|
+
"path": "../public/assets/index-jagRsQaV.js"
|
|
51
51
|
},
|
|
52
|
-
"/assets/
|
|
53
|
-
"type": "image/jpeg",
|
|
54
|
-
"etag": '"1b06-IwivU89ko5UTMUM1/t7hn4sQK9A"',
|
|
55
|
-
"mtime": "2026-06-23T00:48:08.639Z",
|
|
56
|
-
"size": 6918,
|
|
57
|
-
"path": "../public/assets/minimax-BPMzvuL-.jpeg"
|
|
58
|
-
},
|
|
59
|
-
"/assets/index-Bo1dGS4R.css": {
|
|
60
|
-
"type": "text/css; charset=utf-8",
|
|
61
|
-
"etag": '"193d5-fkJLOBIU3M1KTA3n4iL7ijIR5tQ"',
|
|
62
|
-
"mtime": "2026-06-23T00:48:08.639Z",
|
|
63
|
-
"size": 103381,
|
|
64
|
-
"path": "../public/assets/index-Bo1dGS4R.css"
|
|
65
|
-
},
|
|
66
|
-
"/assets/CompareDrawer-Djb4XFZS.js": {
|
|
52
|
+
"/assets/CompareDrawer-BWMipEjS.js": {
|
|
67
53
|
"type": "text/javascript; charset=utf-8",
|
|
68
|
-
"etag": '"49fe-
|
|
69
|
-
"mtime": "2026-06-
|
|
54
|
+
"etag": '"49fe-hR3J/+C0VuiWANK0XlZhyVfJ210"',
|
|
55
|
+
"mtime": "2026-06-23T07:22:07.084Z",
|
|
70
56
|
"size": 18942,
|
|
71
|
-
"path": "../public/assets/CompareDrawer-
|
|
57
|
+
"path": "../public/assets/CompareDrawer-BWMipEjS.js"
|
|
72
58
|
},
|
|
73
|
-
"/assets/
|
|
59
|
+
"/assets/index-CxhRsekH.css": {
|
|
60
|
+
"type": "text/css; charset=utf-8",
|
|
61
|
+
"etag": '"19708-ZIH0Qzt7qzQMaJIOvVbGyTC2iIs"',
|
|
62
|
+
"mtime": "2026-06-23T07:22:07.083Z",
|
|
63
|
+
"size": 104200,
|
|
64
|
+
"path": "../public/assets/index-CxhRsekH.css"
|
|
65
|
+
},
|
|
66
|
+
"/assets/main-DE6Y4-wV.js": {
|
|
74
67
|
"type": "text/javascript; charset=utf-8",
|
|
75
|
-
"etag": '"51726-
|
|
76
|
-
"mtime": "2026-06-
|
|
68
|
+
"etag": '"51726-SwgH2GJTY+mB1BN9OIt6pnzmAMo"',
|
|
69
|
+
"mtime": "2026-06-23T07:22:07.084Z",
|
|
77
70
|
"size": 333606,
|
|
78
|
-
"path": "../public/assets/main-
|
|
71
|
+
"path": "../public/assets/main-DE6Y4-wV.js"
|
|
79
72
|
},
|
|
80
|
-
"/assets/ReplayDialog-
|
|
73
|
+
"/assets/ReplayDialog-DaSzZsvM.js": {
|
|
81
74
|
"type": "text/javascript; charset=utf-8",
|
|
82
|
-
"etag": '"2361-
|
|
83
|
-
"mtime": "2026-06-
|
|
75
|
+
"etag": '"2361-WG4KrKbud/9h19JiHAo9XqZb8TQ"',
|
|
76
|
+
"mtime": "2026-06-23T07:22:07.084Z",
|
|
84
77
|
"size": 9057,
|
|
85
|
-
"path": "../public/assets/ReplayDialog-
|
|
78
|
+
"path": "../public/assets/ReplayDialog-DaSzZsvM.js"
|
|
79
|
+
},
|
|
80
|
+
"/assets/ResponseView-CblEWDMx.js": {
|
|
81
|
+
"type": "text/javascript; charset=utf-8",
|
|
82
|
+
"etag": '"30cc-iF/VrWGkLieZnVp8dAicYWbqwl8"',
|
|
83
|
+
"mtime": "2026-06-23T07:22:07.084Z",
|
|
84
|
+
"size": 12492,
|
|
85
|
+
"path": "../public/assets/ResponseView-CblEWDMx.js"
|
|
86
86
|
},
|
|
87
|
-
"/assets/RequestAnatomy-
|
|
87
|
+
"/assets/RequestAnatomy-BfQmPHCd.js": {
|
|
88
88
|
"type": "text/javascript; charset=utf-8",
|
|
89
|
-
"etag": '"
|
|
90
|
-
"mtime": "2026-06-
|
|
91
|
-
"size":
|
|
92
|
-
"path": "../public/assets/RequestAnatomy-
|
|
89
|
+
"etag": '"58ed-vl6OIMLA96fu3bgaH8iHwJjiMIQ"',
|
|
90
|
+
"mtime": "2026-06-23T07:22:07.084Z",
|
|
91
|
+
"size": 22765,
|
|
92
|
+
"path": "../public/assets/RequestAnatomy-BfQmPHCd.js"
|
|
93
93
|
},
|
|
94
|
-
"/assets/
|
|
94
|
+
"/assets/minimax-BPMzvuL-.jpeg": {
|
|
95
|
+
"type": "image/jpeg",
|
|
96
|
+
"etag": '"1b06-IwivU89ko5UTMUM1/t7hn4sQK9A"',
|
|
97
|
+
"mtime": "2026-06-23T07:22:07.083Z",
|
|
98
|
+
"size": 6918,
|
|
99
|
+
"path": "../public/assets/minimax-BPMzvuL-.jpeg"
|
|
100
|
+
},
|
|
101
|
+
"/assets/StreamingChunkSequence-B4atnL99.js": {
|
|
95
102
|
"type": "text/javascript; charset=utf-8",
|
|
96
|
-
"etag": '"
|
|
97
|
-
"mtime": "2026-06-
|
|
98
|
-
"size":
|
|
99
|
-
"path": "../public/assets/
|
|
103
|
+
"etag": '"d5b-ehf7PCZmWUQkbTT5jKTPzOOKdG0"',
|
|
104
|
+
"mtime": "2026-06-23T07:22:07.084Z",
|
|
105
|
+
"size": 3419,
|
|
106
|
+
"path": "../public/assets/StreamingChunkSequence-B4atnL99.js"
|
|
100
107
|
},
|
|
101
108
|
"/assets/zhipuai-BPNAnxo-.svg": {
|
|
102
109
|
"type": "image/svg+xml",
|
|
103
110
|
"etag": '"2bf8-hNaLCTi89nOFCsIIfWpP/jrfo0s"',
|
|
104
|
-
"mtime": "2026-06-
|
|
111
|
+
"mtime": "2026-06-23T07:22:07.083Z",
|
|
105
112
|
"size": 11256,
|
|
106
113
|
"path": "../public/assets/zhipuai-BPNAnxo-.svg"
|
|
107
114
|
},
|
|
108
|
-
"/assets/
|
|
115
|
+
"/assets/_sessionId-DUzYnZXV.js": {
|
|
109
116
|
"type": "text/javascript; charset=utf-8",
|
|
110
|
-
"etag": '"
|
|
111
|
-
"mtime": "2026-06-
|
|
112
|
-
"size":
|
|
113
|
-
"path": "../public/assets/
|
|
117
|
+
"etag": '"d2-BsjKgnQ2mI1zp5E6AjD39KpTe/g"',
|
|
118
|
+
"mtime": "2026-06-23T07:22:07.084Z",
|
|
119
|
+
"size": 210,
|
|
120
|
+
"path": "../public/assets/_sessionId-DUzYnZXV.js"
|
|
114
121
|
},
|
|
115
122
|
"/assets/qwen-CONDcHqt.png": {
|
|
116
123
|
"type": "image/png",
|
|
117
124
|
"etag": '"572c3-cdJAPaHdOvFCGzuaQjagdgOu6XE"',
|
|
118
|
-
"mtime": "2026-06-
|
|
125
|
+
"mtime": "2026-06-23T07:22:07.083Z",
|
|
119
126
|
"size": 357059,
|
|
120
127
|
"path": "../public/assets/qwen-CONDcHqt.png"
|
|
121
128
|
},
|
|
122
|
-
"/assets/
|
|
123
|
-
"type": "text/javascript; charset=utf-8",
|
|
124
|
-
"etag": '"d2-nFYnu19OBrYGuMoYXu2+NgVBGhw"',
|
|
125
|
-
"mtime": "2026-06-23T00:48:08.639Z",
|
|
126
|
-
"size": 210,
|
|
127
|
-
"path": "../public/assets/_sessionId-3ipVoQtW.js"
|
|
128
|
-
},
|
|
129
|
-
"/assets/ProxyViewerContainer-CWdkPZsJ.js": {
|
|
129
|
+
"/assets/ProxyViewerContainer-DIAgurux.js": {
|
|
130
130
|
"type": "text/javascript; charset=utf-8",
|
|
131
|
-
"etag": '"
|
|
132
|
-
"mtime": "2026-06-
|
|
133
|
-
"size":
|
|
134
|
-
"path": "../public/assets/ProxyViewerContainer-
|
|
131
|
+
"etag": '"a3740-w05aF6UPaJiCj3M72I/hJ/rSNQU"',
|
|
132
|
+
"mtime": "2026-06-23T07:22:07.084Z",
|
|
133
|
+
"size": 669504,
|
|
134
|
+
"path": "../public/assets/ProxyViewerContainer-DIAgurux.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.
|
|
3
|
+
"version": "2.0.10",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Agent observability and knowledge capture layer for AI coding tools.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -29,9 +29,10 @@
|
|
|
29
29
|
"ai-coding-tools"
|
|
30
30
|
],
|
|
31
31
|
"bin": {
|
|
32
|
-
"agent-inspector": "
|
|
32
|
+
"agent-inspector": "bin/agent-inspector.js"
|
|
33
33
|
},
|
|
34
34
|
"files": [
|
|
35
|
+
"bin",
|
|
35
36
|
"src",
|
|
36
37
|
"!src/**/*.test.ts",
|
|
37
38
|
"!src/**/*.stories.tsx",
|
|
@@ -23,6 +23,11 @@ import {
|
|
|
23
23
|
} from "lucide-react";
|
|
24
24
|
import type { ProviderConfig } from "../../proxy/providers";
|
|
25
25
|
import { maskApiKey } from "../../lib/mask";
|
|
26
|
+
import {
|
|
27
|
+
findProviderModelMetadata,
|
|
28
|
+
providerHasContextMetadata,
|
|
29
|
+
} from "../../lib/providerModelMetadata";
|
|
30
|
+
import { formatTokens } from "../../lib/utils";
|
|
26
31
|
import type {
|
|
27
32
|
ProviderTestErrorType as ErrorType,
|
|
28
33
|
ProviderTestResult as TestResult,
|
|
@@ -44,6 +49,8 @@ type ProviderCardProps = {
|
|
|
44
49
|
onEdit: (provider: ProviderConfig) => void;
|
|
45
50
|
onDelete: (providerId: string) => void;
|
|
46
51
|
onTest?: (providerId: string) => void;
|
|
52
|
+
onRefreshMetadata?: (providerId: string) => void;
|
|
53
|
+
isRefreshingMetadata?: boolean;
|
|
47
54
|
};
|
|
48
55
|
|
|
49
56
|
function hasSuccessField(result: ProviderTestState): result is TestResult {
|
|
@@ -217,6 +224,8 @@ export function ProviderCard({
|
|
|
217
224
|
onEdit,
|
|
218
225
|
onDelete,
|
|
219
226
|
onTest,
|
|
227
|
+
onRefreshMetadata,
|
|
228
|
+
isRefreshingMetadata = false,
|
|
220
229
|
}: ProviderCardProps): JSX.Element {
|
|
221
230
|
const [showApiKey, setShowApiKey] = useState(false);
|
|
222
231
|
const [copied, setCopied] = useState(false);
|
|
@@ -234,6 +243,10 @@ export function ProviderCard({
|
|
|
234
243
|
Object.entries(KNOWN_PROVIDER_DOCS).find(([keyword]) =>
|
|
235
244
|
provider.name.toLowerCase().includes(keyword),
|
|
236
245
|
)?.[1];
|
|
246
|
+
const hasContextMetadata = providerHasContextMetadata(provider);
|
|
247
|
+
const canRefreshMetadata =
|
|
248
|
+
provider.modelMetadataUrl !== undefined && provider.modelMetadataUrl.trim() !== "";
|
|
249
|
+
const metadataImportLabel = canRefreshMetadata ? "Refresh" : "Import defaults";
|
|
237
250
|
|
|
238
251
|
return (
|
|
239
252
|
<div
|
|
@@ -283,11 +296,52 @@ export function ProviderCard({
|
|
|
283
296
|
|
|
284
297
|
{provider.models !== undefined && provider.models.length > 0 && (
|
|
285
298
|
<div className="flex flex-wrap gap-1">
|
|
286
|
-
{provider.models.map((m) =>
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
299
|
+
{provider.models.map((m) => {
|
|
300
|
+
const metadata = findProviderModelMetadata(provider, m);
|
|
301
|
+
return (
|
|
302
|
+
<span
|
|
303
|
+
key={m}
|
|
304
|
+
className="inline-flex items-center gap-1 text-xs px-1.5 py-0.5 rounded bg-muted text-muted-foreground"
|
|
305
|
+
>
|
|
306
|
+
<span>{m}</span>
|
|
307
|
+
{metadata !== null && metadata.contextWindow !== undefined && (
|
|
308
|
+
<span className="font-mono text-foreground/80">
|
|
309
|
+
{formatTokens(metadata.contextWindow)}
|
|
310
|
+
</span>
|
|
311
|
+
)}
|
|
312
|
+
</span>
|
|
313
|
+
);
|
|
314
|
+
})}
|
|
315
|
+
</div>
|
|
316
|
+
)}
|
|
317
|
+
|
|
318
|
+
{!hasContextMetadata && (
|
|
319
|
+
<div className="flex items-center justify-between gap-2 rounded-md border border-amber-500/25 bg-amber-500/6 px-2 py-1.5 text-xs text-muted-foreground">
|
|
320
|
+
<span>Context window metadata missing</span>
|
|
321
|
+
<div className="flex shrink-0 items-center gap-1">
|
|
322
|
+
<Button
|
|
323
|
+
type="button"
|
|
324
|
+
variant="outline"
|
|
325
|
+
size="sm"
|
|
326
|
+
onClick={() => onRefreshMetadata?.(provider.id)}
|
|
327
|
+
className="h-6 gap-1 px-2 text-xs"
|
|
328
|
+
disabled={isRefreshingMetadata}
|
|
329
|
+
>
|
|
330
|
+
<RotateCw className={`size-3 ${isRefreshingMetadata ? "animate-spin" : ""}`} />
|
|
331
|
+
{metadataImportLabel}
|
|
332
|
+
</Button>
|
|
333
|
+
{!canRefreshMetadata && (
|
|
334
|
+
<Button
|
|
335
|
+
type="button"
|
|
336
|
+
variant="ghost"
|
|
337
|
+
size="sm"
|
|
338
|
+
onClick={() => onEdit(provider)}
|
|
339
|
+
className="h-6 px-2 text-xs"
|
|
340
|
+
>
|
|
341
|
+
Set URL
|
|
342
|
+
</Button>
|
|
343
|
+
)}
|
|
344
|
+
</div>
|
|
291
345
|
</div>
|
|
292
346
|
)}
|
|
293
347
|
|
|
@@ -49,6 +49,7 @@ type ProviderFormProps = {
|
|
|
49
49
|
anthropicBaseUrl?: string;
|
|
50
50
|
openaiBaseUrl?: string;
|
|
51
51
|
apiDocsUrl?: string;
|
|
52
|
+
modelMetadataUrl?: string;
|
|
52
53
|
source?: "company" | "personal";
|
|
53
54
|
}) => void;
|
|
54
55
|
onCancel: () => void;
|
|
@@ -67,6 +68,7 @@ export function ProviderForm({ provider, onSubmit, onCancel }: ProviderFormProps
|
|
|
67
68
|
const [anthropicBaseUrl, setAnthropicBaseUrl] = useState(provider?.anthropicBaseUrl ?? "");
|
|
68
69
|
const [openaiBaseUrl, setOpenaiBaseUrl] = useState(provider?.openaiBaseUrl ?? "");
|
|
69
70
|
const [apiDocsUrl, setApiDocsUrl] = useState(provider?.apiDocsUrl ?? "");
|
|
71
|
+
const [modelMetadataUrl, setModelMetadataUrl] = useState(provider?.modelMetadataUrl ?? "");
|
|
70
72
|
const [source, setSource] = useState<"company" | "personal" | undefined>(provider?.source);
|
|
71
73
|
const [errors, setErrors] = useState<Record<string, string>>({});
|
|
72
74
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
@@ -96,6 +98,8 @@ export function ProviderForm({ provider, onSubmit, onCancel }: ProviderFormProps
|
|
|
96
98
|
const isMiniMax = name.toLowerCase().includes("minimax");
|
|
97
99
|
// Check if Alibaba is detected
|
|
98
100
|
const isAlibaba = name.toLowerCase().includes("alibaba");
|
|
101
|
+
const hasContextMetadata =
|
|
102
|
+
provider?.modelMetadata?.some((metadata) => metadata.contextWindow !== undefined) ?? false;
|
|
99
103
|
|
|
100
104
|
function handleCopy() {
|
|
101
105
|
navigator.clipboard.writeText(apiKey).catch(() => {});
|
|
@@ -111,6 +115,7 @@ export function ProviderForm({ provider, onSubmit, onCancel }: ProviderFormProps
|
|
|
111
115
|
setAnthropicBaseUrl(provider.anthropicBaseUrl ?? "");
|
|
112
116
|
setOpenaiBaseUrl(provider.openaiBaseUrl ?? "");
|
|
113
117
|
setApiDocsUrl(provider.apiDocsUrl ?? "");
|
|
118
|
+
setModelMetadataUrl(provider.modelMetadataUrl ?? "");
|
|
114
119
|
setSource(provider.source);
|
|
115
120
|
setManualAnthropicUrlOverride(false);
|
|
116
121
|
setManualOpenaiUrlOverride(false);
|
|
@@ -160,6 +165,9 @@ export function ProviderForm({ provider, onSubmit, onCancel }: ProviderFormProps
|
|
|
160
165
|
if (openaiBaseUrl.trim() && !isValidUrl(openaiBaseUrl.trim())) {
|
|
161
166
|
newErrors.openaiBaseUrl = "Invalid URL format";
|
|
162
167
|
}
|
|
168
|
+
if (modelMetadataUrl.trim() && !isValidUrl(modelMetadataUrl.trim())) {
|
|
169
|
+
newErrors.modelMetadataUrl = "Invalid URL format";
|
|
170
|
+
}
|
|
163
171
|
if (!anthropicBaseUrl.trim() && !openaiBaseUrl.trim()) {
|
|
164
172
|
newErrors.format = "At least one format URL (Anthropic or OpenAI) is required";
|
|
165
173
|
}
|
|
@@ -188,6 +196,7 @@ export function ProviderForm({ provider, onSubmit, onCancel }: ProviderFormProps
|
|
|
188
196
|
anthropicBaseUrl: anthropicBaseUrl.trim() || undefined,
|
|
189
197
|
openaiBaseUrl: openaiBaseUrl.trim() || undefined,
|
|
190
198
|
apiDocsUrl: apiDocsUrl.trim() || undefined,
|
|
199
|
+
modelMetadataUrl: modelMetadataUrl.trim() || undefined,
|
|
191
200
|
source,
|
|
192
201
|
});
|
|
193
202
|
} finally {
|
|
@@ -237,6 +246,39 @@ export function ProviderForm({ provider, onSubmit, onCancel }: ProviderFormProps
|
|
|
237
246
|
</p>
|
|
238
247
|
</div>
|
|
239
248
|
|
|
249
|
+
<div className="space-y-2">
|
|
250
|
+
<label htmlFor="provider-model-metadata-url" className="text-sm font-medium">
|
|
251
|
+
Model Metadata URL
|
|
252
|
+
</label>
|
|
253
|
+
<input
|
|
254
|
+
id="provider-model-metadata-url"
|
|
255
|
+
type="text"
|
|
256
|
+
value={modelMetadataUrl}
|
|
257
|
+
onChange={(e) => setModelMetadataUrl(e.target.value)}
|
|
258
|
+
placeholder="https://example.com/agent-inspector-models.json"
|
|
259
|
+
className="w-full rounded-md border border-input bg-background px-3 py-2 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"
|
|
260
|
+
/>
|
|
261
|
+
{errors.modelMetadataUrl !== undefined && (
|
|
262
|
+
<p className="text-xs text-destructive">{errors.modelMetadataUrl}</p>
|
|
263
|
+
)}
|
|
264
|
+
{modelMetadataUrl.trim() === "" && (
|
|
265
|
+
<div className="rounded-md border border-border bg-muted/20 px-3 py-2 text-xs text-muted-foreground">
|
|
266
|
+
Built-in limits cover DeepSeek v4 pro/flash and MiniMax M3/M2.7/M2.5/M2.1. Use a
|
|
267
|
+
registry URL for private or custom model limits.
|
|
268
|
+
</div>
|
|
269
|
+
)}
|
|
270
|
+
{provider !== undefined && !hasContextMetadata && modelMetadataUrl.trim() === "" && (
|
|
271
|
+
<p className="text-xs text-amber-400">
|
|
272
|
+
This provider has no matching context metadata yet.
|
|
273
|
+
</p>
|
|
274
|
+
)}
|
|
275
|
+
{modelMetadataUrl.trim() !== "" && (
|
|
276
|
+
<p className="text-xs text-muted-foreground">
|
|
277
|
+
Agent Inspector will refresh model limits from this JSON registry after save.
|
|
278
|
+
</p>
|
|
279
|
+
)}
|
|
280
|
+
</div>
|
|
281
|
+
|
|
240
282
|
<div className="space-y-2">
|
|
241
283
|
<label htmlFor="provider-apikey" className="text-sm font-medium">
|
|
242
284
|
API Key <span className="text-destructive">*</span>
|
|
@@ -29,6 +29,14 @@ const ImportResponseSchema = z.object({
|
|
|
29
29
|
errors: z.array(z.string()).optional(),
|
|
30
30
|
});
|
|
31
31
|
|
|
32
|
+
const ModelMetadataRefreshResponseSchema = z.object({
|
|
33
|
+
provider: ProviderConfigSchema,
|
|
34
|
+
imported: z.number(),
|
|
35
|
+
matchedModels: z.array(z.string()),
|
|
36
|
+
missingModels: z.array(z.string()),
|
|
37
|
+
sourceUrl: z.string(),
|
|
38
|
+
});
|
|
39
|
+
|
|
32
40
|
const TEST_TIMEOUT_SECONDS = 30;
|
|
33
41
|
const NETWORK_ERROR_MESSAGE = "Network error: could not reach the server. Is the proxy running?";
|
|
34
42
|
|
|
@@ -39,6 +47,7 @@ type ProviderFormData = {
|
|
|
39
47
|
anthropicBaseUrl?: string;
|
|
40
48
|
openaiBaseUrl?: string;
|
|
41
49
|
apiDocsUrl?: string;
|
|
50
|
+
modelMetadataUrl?: string;
|
|
42
51
|
source?: "company" | "personal";
|
|
43
52
|
};
|
|
44
53
|
|
|
@@ -50,6 +59,7 @@ function createProviderPayload(data: ProviderFormData) {
|
|
|
50
59
|
anthropicBaseUrl: (data.anthropicBaseUrl?.length ?? 0) > 0 ? data.anthropicBaseUrl : undefined,
|
|
51
60
|
openaiBaseUrl: (data.openaiBaseUrl?.length ?? 0) > 0 ? data.openaiBaseUrl : undefined,
|
|
52
61
|
apiDocsUrl: (data.apiDocsUrl?.length ?? 0) > 0 ? data.apiDocsUrl : undefined,
|
|
62
|
+
modelMetadataUrl: (data.modelMetadataUrl?.length ?? 0) > 0 ? data.modelMetadataUrl : undefined,
|
|
53
63
|
source: data.source,
|
|
54
64
|
};
|
|
55
65
|
}
|
|
@@ -107,6 +117,9 @@ export function ProvidersPanel({
|
|
|
107
117
|
const [confirmOpen, setConfirmOpen] = useState(false);
|
|
108
118
|
const [deletingProviderId, setDeletingProviderId] = useState<string | null>(null);
|
|
109
119
|
const [sourceFilter, setSourceFilter] = useState<"all" | "personal" | "company">("all");
|
|
120
|
+
const [refreshingMetadataProviders, setRefreshingMetadataProviders] = useState<Set<string>>(
|
|
121
|
+
new Set(),
|
|
122
|
+
);
|
|
110
123
|
const listScrollRef = useRef<HTMLDivElement>(null);
|
|
111
124
|
const highlightTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
112
125
|
|
|
@@ -273,6 +286,51 @@ export function ProvidersPanel({
|
|
|
273
286
|
[setTestingTimeLeft, updateTestResults, updateTestingState],
|
|
274
287
|
);
|
|
275
288
|
|
|
289
|
+
const refreshProviderMetadata = useCallback(
|
|
290
|
+
async (providerId: string, url?: string, showError = true): Promise<boolean> => {
|
|
291
|
+
setRefreshingMetadataProviders((prev) => {
|
|
292
|
+
const next = new Set(prev);
|
|
293
|
+
next.add(providerId);
|
|
294
|
+
return next;
|
|
295
|
+
});
|
|
296
|
+
try {
|
|
297
|
+
const payload = url !== undefined && url.trim() !== "" ? { url } : {};
|
|
298
|
+
const res = await fetch(`/api/providers/${providerId}/model-metadata`, {
|
|
299
|
+
method: "POST",
|
|
300
|
+
headers: { "Content-Type": "application/json" },
|
|
301
|
+
body: JSON.stringify(payload),
|
|
302
|
+
});
|
|
303
|
+
if (!res.ok) {
|
|
304
|
+
if (showError) {
|
|
305
|
+
setError(await readApiError(res, "Failed to refresh model metadata"));
|
|
306
|
+
}
|
|
307
|
+
return false;
|
|
308
|
+
}
|
|
309
|
+
const data = await parseJsonResponse(res, ModelMetadataRefreshResponseSchema);
|
|
310
|
+
if (data.imported === 0 && showError) {
|
|
311
|
+
setError("No matching model metadata found for this provider.");
|
|
312
|
+
} else {
|
|
313
|
+
setError(null);
|
|
314
|
+
}
|
|
315
|
+
triggerHighlight(data.provider.id);
|
|
316
|
+
refreshProviders();
|
|
317
|
+
return data.imported > 0;
|
|
318
|
+
} catch {
|
|
319
|
+
if (showError) {
|
|
320
|
+
setError(NETWORK_ERROR_MESSAGE);
|
|
321
|
+
}
|
|
322
|
+
return false;
|
|
323
|
+
} finally {
|
|
324
|
+
setRefreshingMetadataProviders((prev) => {
|
|
325
|
+
const next = new Set(prev);
|
|
326
|
+
next.delete(providerId);
|
|
327
|
+
return next;
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
},
|
|
331
|
+
[refreshProviders, triggerHighlight],
|
|
332
|
+
);
|
|
333
|
+
|
|
276
334
|
function handleAddProvider(data: ProviderFormData): void {
|
|
277
335
|
void (async () => {
|
|
278
336
|
try {
|
|
@@ -288,6 +346,9 @@ export function ProvidersPanel({
|
|
|
288
346
|
const newProvider = await parseJsonResponse(res, ProviderConfigSchema);
|
|
289
347
|
setShowForm(false);
|
|
290
348
|
triggerHighlight(newProvider.id);
|
|
349
|
+
if (data.modelMetadataUrl !== undefined && data.modelMetadataUrl.trim() !== "") {
|
|
350
|
+
await refreshProviderMetadata(newProvider.id, data.modelMetadataUrl, false);
|
|
351
|
+
}
|
|
291
352
|
refreshProviders();
|
|
292
353
|
await runTest(newProvider.id);
|
|
293
354
|
} catch {
|
|
@@ -312,6 +373,9 @@ export function ProvidersPanel({
|
|
|
312
373
|
const updated = await parseJsonResponse(res, ProviderConfigSchema);
|
|
313
374
|
setEditingProvider(undefined);
|
|
314
375
|
triggerHighlight(updated.id);
|
|
376
|
+
if (data.modelMetadataUrl !== undefined && data.modelMetadataUrl.trim() !== "") {
|
|
377
|
+
await refreshProviderMetadata(updated.id, data.modelMetadataUrl, false);
|
|
378
|
+
}
|
|
315
379
|
refreshProviders();
|
|
316
380
|
// Only run connection test when critical fields (apiKey, model, base URLs) changed
|
|
317
381
|
const criticalFieldsChanged =
|
|
@@ -602,6 +666,10 @@ export function ProvidersPanel({
|
|
|
602
666
|
isHighlighted={provider.id === highlightedProviderId}
|
|
603
667
|
onEdit={(p) => setEditingProvider(p)}
|
|
604
668
|
onDelete={handleDeleteProvider}
|
|
669
|
+
onRefreshMetadata={(id) => {
|
|
670
|
+
void refreshProviderMetadata(id);
|
|
671
|
+
}}
|
|
672
|
+
isRefreshingMetadata={refreshingMetadataProviders.has(provider.id)}
|
|
605
673
|
onTest={(id: string) => {
|
|
606
674
|
void runTest(id);
|
|
607
675
|
}}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { GitCompareArrows } from "lucide-react";
|
|
2
2
|
import { Suspense, type JSX } from "react";
|
|
3
|
-
import { useMemo, useRef, useState, memo } from "react";
|
|
3
|
+
import { useEffect, useMemo, useRef, useState, memo } from "react";
|
|
4
4
|
import type { CapturedLog } from "../../proxy/schemas";
|
|
5
5
|
import type { TimeDisplayFormat } from "../../lib/runtimeConfig";
|
|
6
6
|
import { stripClaudeCodeBillingHeader } from "../../proxy/claudeCodeStrip";
|
|
@@ -297,6 +297,11 @@ export const LogEntry = memo(function ({
|
|
|
297
297
|
},
|
|
298
298
|
});
|
|
299
299
|
|
|
300
|
+
useEffect(() => {
|
|
301
|
+
if (!expanded) return;
|
|
302
|
+
setActiveTab(anatomySegments !== null ? "anatomy" : "request");
|
|
303
|
+
}, [anatomySegments, expanded]);
|
|
304
|
+
|
|
300
305
|
return (
|
|
301
306
|
<TooltipProvider>
|
|
302
307
|
<div className="border border-border rounded-lg mb-1 overflow-hidden">
|