@tonyclaw/agent-inspector 2.1.16 → 2.1.17
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/cli.js +57 -13
- package/.output/nitro.json +1 -1
- package/.output/public/assets/{CompareDrawer-Ccrrcx1j.js → CompareDrawer-B9sLBHw5.js} +1 -1
- package/.output/public/assets/ProxyViewerContainer-DbWjc_BE.js +106 -0
- package/.output/public/assets/{ReplayDialog-C7axhr-l.js → ReplayDialog-D9I9W9n3.js} +1 -1
- package/.output/public/assets/{RequestAnatomy-WOptg8j_.js → RequestAnatomy-DGXK_Rii.js} +1 -1
- package/.output/public/assets/{ResponseView-CSAcxh_M.js → ResponseView-df_JzwsS.js} +2 -2
- package/.output/public/assets/{StreamingChunkSequence-DJS5KhPx.js → StreamingChunkSequence-i3DM5IKL.js} +1 -1
- package/.output/public/assets/_sessionId-CTpCHdFh.js +1 -0
- package/.output/public/assets/index-Blqvndcn.js +1 -0
- package/.output/public/assets/index-Cs79WRQj.css +1 -0
- package/.output/public/assets/{index-DMPNh46t.js → index-Dqc2r1ea.js} +1 -1
- package/.output/public/assets/{json-viewer-inX5QSa3.js → json-viewer-D3QWQlSB.js} +1 -1
- package/.output/public/assets/{main-aCAKYGSD.js → main-ZhxhOCmF.js} +2 -2
- package/.output/server/_libs/lucide-react.mjs +220 -202
- package/.output/server/{_sessionId-C0fhvQnn.mjs → _sessionId-YFrP0lZP.mjs} +2 -2
- package/.output/server/_ssr/{CompareDrawer-CO-Ti5sg.mjs → CompareDrawer-qC3adEeq.mjs} +3 -3
- package/.output/server/_ssr/{ProxyViewerContainer-N4J7uBCX.mjs → ProxyViewerContainer-DCZIJC2b.mjs} +1858 -1396
- package/.output/server/_ssr/{ReplayDialog-J1VxC0In.mjs → ReplayDialog-CvX4XYCi.mjs} +4 -4
- package/.output/server/_ssr/{RequestAnatomy-DBH-F3rq.mjs → RequestAnatomy-HQfrxjO1.mjs} +2 -2
- package/.output/server/_ssr/{ResponseView-DLFwV7Hd.mjs → ResponseView-QSlSD11e.mjs} +3 -3
- package/.output/server/_ssr/{StreamingChunkSequence-BywmuND0.mjs → StreamingChunkSequence-DTAiwlnI.mjs} +2 -2
- package/.output/server/_ssr/{index-CStymIc0.mjs → index-D_EzfXaN.mjs} +2 -2
- package/.output/server/_ssr/index.mjs +2 -2
- package/.output/server/_ssr/{json-viewer-DlQu0N1x.mjs → json-viewer-CrIX0e0Q.mjs} +3 -3
- package/.output/server/_ssr/{router-CQ4Zxhmr.mjs → router-__o2wrfO.mjs} +398 -95
- package/.output/server/{_tanstack-start-manifest_v-BHeJBGfo.mjs → _tanstack-start-manifest_v-DbqyLpsI.mjs} +1 -1
- package/.output/server/index.mjs +73 -73
- package/README.md +57 -2
- package/package.json +1 -1
- package/src/cli/startupOutput.ts +16 -0
- package/src/cli.ts +61 -15
- package/src/components/ProxyViewer.tsx +395 -64
- package/src/components/providers/ProviderCard.tsx +132 -31
- package/src/components/providers/ProviderForm.tsx +43 -11
- package/src/components/providers/ProvidersPanel.tsx +1 -1
- package/src/components/proxy-viewer/ConversationHeader.tsx +133 -112
- package/src/components/proxy-viewer/LogEntry.tsx +109 -26
- package/src/components/proxy-viewer/LogEntryHeader.tsx +339 -329
- package/src/components/proxy-viewer/ThreadConnector.tsx +102 -83
- package/src/components/proxy-viewer/TurnGroup.tsx +22 -63
- package/src/components/proxy-viewer/bodyHydration.ts +56 -0
- package/src/lib/providerTestContract.ts +56 -10
- package/src/lib/providerTestPrompt.ts +23 -1
- package/src/lib/upstreamUrl.ts +105 -1
- package/src/mcp/toolHandlers.ts +1 -1
- package/src/routes/api/providers.$providerId.test.log.ts +85 -10
- package/.output/public/assets/ProxyViewerContainer-DLqClc9A.js +0 -106
- package/.output/public/assets/_sessionId-DUSalzKH.js +0 -1
- package/.output/public/assets/index-BPpA21dY.css +0 -1
- package/.output/public/assets/index-Bt0Az2I2.js +0 -1
package/src/lib/upstreamUrl.ts
CHANGED
|
@@ -1,9 +1,24 @@
|
|
|
1
1
|
export const PATH_V1_CHAT_COMPLETIONS = "/v1/chat/completions";
|
|
2
2
|
export const PATH_V1_RESPONSES = "/v1/responses";
|
|
3
|
+
export const PATH_V1_MESSAGES = "/v1/messages";
|
|
4
|
+
|
|
5
|
+
export type UpstreamUrlDiagnostic = {
|
|
6
|
+
severity: "info" | "warning";
|
|
7
|
+
message: string;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export type UpstreamUrlPreviewOptions = {
|
|
11
|
+
providerName?: string;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export type UpstreamUrlPreview = {
|
|
15
|
+
finalUrl: string;
|
|
16
|
+
diagnostics: UpstreamUrlDiagnostic[];
|
|
17
|
+
};
|
|
3
18
|
|
|
4
19
|
export function buildUpstreamUrl(upstreamBase: string, normalizedPath: string): string {
|
|
5
20
|
const base = upstreamBase.endsWith("/") ? upstreamBase.slice(0, -1) : upstreamBase;
|
|
6
|
-
const endpointPath = normalizedPath
|
|
21
|
+
const endpointPath = endpointPathFor(normalizedPath);
|
|
7
22
|
const queryIndex = normalizedPath.indexOf("?");
|
|
8
23
|
const search = queryIndex >= 0 ? normalizedPath.slice(queryIndex) : "";
|
|
9
24
|
|
|
@@ -24,6 +39,62 @@ export function buildUpstreamUrl(upstreamBase: string, normalizedPath: string):
|
|
|
24
39
|
return base + normalizedPath;
|
|
25
40
|
}
|
|
26
41
|
|
|
42
|
+
export function previewUpstreamUrl(
|
|
43
|
+
upstreamBase: string,
|
|
44
|
+
normalizedPath: string,
|
|
45
|
+
options: UpstreamUrlPreviewOptions = {},
|
|
46
|
+
): UpstreamUrlPreview {
|
|
47
|
+
const base = upstreamBase.trim().replace(/\/+$/, "");
|
|
48
|
+
const endpointPath = endpointPathFor(normalizedPath);
|
|
49
|
+
const finalUrl = buildUpstreamUrl(base, normalizedPath);
|
|
50
|
+
const diagnostics: UpstreamUrlDiagnostic[] = [];
|
|
51
|
+
const exactEndpoint = endpointPath !== "" && base.endsWith(endpointPath);
|
|
52
|
+
|
|
53
|
+
if (exactEndpoint) {
|
|
54
|
+
diagnostics.push({
|
|
55
|
+
severity: "info",
|
|
56
|
+
message: "This already includes the exact endpoint; no duplicate suffix will be appended.",
|
|
57
|
+
});
|
|
58
|
+
} else if (isZhipuVersionedOpenAIBase(base) && isVersionedChatCompletionsPath(normalizedPath)) {
|
|
59
|
+
diagnostics.push({
|
|
60
|
+
severity: "info",
|
|
61
|
+
message: "Zhipu bases already include the API version; only /chat/completions is appended.",
|
|
62
|
+
});
|
|
63
|
+
} else if (base.endsWith("/v1") && normalizedPath.startsWith("/v1/")) {
|
|
64
|
+
diagnostics.push({
|
|
65
|
+
severity: "info",
|
|
66
|
+
message: "Base URL already ends with /v1; the suffix will not repeat /v1.",
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (isResponsesPath(endpointPath) && baseEndsWithChatEndpoint(base)) {
|
|
71
|
+
diagnostics.push({
|
|
72
|
+
severity: "warning",
|
|
73
|
+
message: "This looks like a Chat Completions endpoint, so Responses would append after it.",
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (isChatCompletionsPath(endpointPath) && baseEndsWithResponsesEndpoint(base)) {
|
|
78
|
+
diagnostics.push({
|
|
79
|
+
severity: "warning",
|
|
80
|
+
message: "This looks like a Responses endpoint, so Chat Completions would append after it.",
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (isResponsesPath(endpointPath) && isLikelyDeepSeekBase(base, options.providerName)) {
|
|
85
|
+
diagnostics.push({
|
|
86
|
+
severity: "warning",
|
|
87
|
+
message: "DeepSeek's official API does not currently expose OpenAI Responses.",
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return { finalUrl, diagnostics };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function endpointPathFor(normalizedPath: string): string {
|
|
95
|
+
return normalizedPath.split("?")[0] ?? normalizedPath;
|
|
96
|
+
}
|
|
97
|
+
|
|
27
98
|
function isVersionedChatCompletionsPath(normalizedPath: string): boolean {
|
|
28
99
|
return (
|
|
29
100
|
normalizedPath === PATH_V1_CHAT_COMPLETIONS ||
|
|
@@ -32,6 +103,39 @@ function isVersionedChatCompletionsPath(normalizedPath: string): boolean {
|
|
|
32
103
|
);
|
|
33
104
|
}
|
|
34
105
|
|
|
106
|
+
function isChatCompletionsPath(endpointPath: string): boolean {
|
|
107
|
+
return (
|
|
108
|
+
endpointPath === PATH_V1_CHAT_COMPLETIONS ||
|
|
109
|
+
endpointPath.startsWith(`${PATH_V1_CHAT_COMPLETIONS}/`)
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function isResponsesPath(endpointPath: string): boolean {
|
|
114
|
+
return endpointPath === PATH_V1_RESPONSES || endpointPath.startsWith(`${PATH_V1_RESPONSES}/`);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function baseEndsWithChatEndpoint(base: string): boolean {
|
|
118
|
+
const normalized = base.toLowerCase();
|
|
119
|
+
return normalized.endsWith("/chat/completions");
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function baseEndsWithResponsesEndpoint(base: string): boolean {
|
|
123
|
+
const normalized = base.toLowerCase();
|
|
124
|
+
return normalized.endsWith("/responses");
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function isLikelyDeepSeekBase(base: string, providerName: string | undefined): boolean {
|
|
128
|
+
const provider = providerName?.toLowerCase();
|
|
129
|
+
if (provider !== undefined && provider.includes("deepseek")) return true;
|
|
130
|
+
|
|
131
|
+
try {
|
|
132
|
+
const parsed = new URL(base);
|
|
133
|
+
return parsed.hostname.toLowerCase().includes("deepseek");
|
|
134
|
+
} catch {
|
|
135
|
+
return base.toLowerCase().includes("deepseek");
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
35
139
|
function isZhipuVersionedOpenAIBase(base: string): boolean {
|
|
36
140
|
try {
|
|
37
141
|
const parsed = new URL(base);
|
package/src/mcp/toolHandlers.ts
CHANGED
|
@@ -907,7 +907,7 @@ function providerDiagnosticHints(result: unknown): string[] {
|
|
|
907
907
|
}
|
|
908
908
|
if (text.includes("nonstream") || text.includes("non-stream")) {
|
|
909
909
|
hints.push(
|
|
910
|
-
"The failure mentions non-streaming. Compare the
|
|
910
|
+
"The failure mentions non-streaming. Compare the Anthropic, OpenAI Chat, and OpenAI Responses non-streaming path and payload with the streaming request.",
|
|
911
911
|
);
|
|
912
912
|
}
|
|
913
913
|
return [...new Set(hints)];
|
|
@@ -6,6 +6,7 @@ import { buildProviderTestSessionId } from "../../proxy/sessionSupervisor";
|
|
|
6
6
|
import { buildProviderTestRequestBody } from "../../lib/providerTestPrompt";
|
|
7
7
|
import {
|
|
8
8
|
ProviderTestResultsSchema,
|
|
9
|
+
type ProviderModelTestResults,
|
|
9
10
|
type ProviderTestResult as TestResult,
|
|
10
11
|
type ProviderTestState,
|
|
11
12
|
} from "../../lib/providerTestContract";
|
|
@@ -55,11 +56,9 @@ async function logModelResults(
|
|
|
55
56
|
providerName: string,
|
|
56
57
|
providerSessionId: string,
|
|
57
58
|
anthropicUrl: string | undefined,
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
openai: { nonStreaming: ProviderTestState; streaming: ProviderTestState };
|
|
62
|
-
},
|
|
59
|
+
openaiChatUrl: string | undefined,
|
|
60
|
+
openaiResponsesUrl: string | undefined,
|
|
61
|
+
modelResults: ProviderModelTestResults,
|
|
63
62
|
captureFullDetails: boolean,
|
|
64
63
|
): Promise<void> {
|
|
65
64
|
const usageModel = getModelUsageName(displayName, providerName);
|
|
@@ -132,9 +131,9 @@ async function logModelResults(
|
|
|
132
131
|
}
|
|
133
132
|
}
|
|
134
133
|
|
|
135
|
-
if (
|
|
136
|
-
const nsResult = modelResults.
|
|
137
|
-
const sResult = modelResults.
|
|
134
|
+
if (openaiChatUrl !== undefined) {
|
|
135
|
+
const nsResult = modelResults.openaiChat.nonStreaming;
|
|
136
|
+
const sResult = modelResults.openaiChat.streaming;
|
|
138
137
|
if (hasSuccessField(nsResult) && hasSuccessField(sResult)) {
|
|
139
138
|
const nonStreamingResult = nsResult;
|
|
140
139
|
const streamingResult = sResult;
|
|
@@ -199,6 +198,76 @@ async function logModelResults(
|
|
|
199
198
|
});
|
|
200
199
|
}
|
|
201
200
|
}
|
|
201
|
+
|
|
202
|
+
if (openaiResponsesUrl !== undefined) {
|
|
203
|
+
const nsResult = modelResults.openaiResponses.nonStreaming;
|
|
204
|
+
const sResult = modelResults.openaiResponses.streaming;
|
|
205
|
+
if (hasSuccessField(nsResult) && hasSuccessField(sResult)) {
|
|
206
|
+
const nonStreamingResult = nsResult;
|
|
207
|
+
const streamingResult = sResult;
|
|
208
|
+
|
|
209
|
+
const requestBody = JSON.stringify(
|
|
210
|
+
buildProviderTestRequestBody(usageModel, "non-streaming", "responses"),
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
await addTestLogEntry({
|
|
214
|
+
timestamp: new Date().toISOString(),
|
|
215
|
+
method: "POST",
|
|
216
|
+
path: "/v1/responses",
|
|
217
|
+
model: nonStreamingResult.model ?? displayName,
|
|
218
|
+
sessionId: providerSessionId,
|
|
219
|
+
rawRequestBody: requestBody,
|
|
220
|
+
responseStatus: getResultResponseStatus(nonStreamingResult),
|
|
221
|
+
responseText: getResultResponseText(nonStreamingResult),
|
|
222
|
+
inputTokens: nonStreamingResult.inputTokens ?? null,
|
|
223
|
+
outputTokens: nonStreamingResult.outputTokens ?? null,
|
|
224
|
+
cacheCreationInputTokens: null,
|
|
225
|
+
cacheReadInputTokens: nonStreamingResult.cacheReadInputTokens ?? null,
|
|
226
|
+
elapsedMs: nonStreamingResult.latencyMs ?? 0,
|
|
227
|
+
firstChunkMs: null,
|
|
228
|
+
totalStreamMs: null,
|
|
229
|
+
tokensPerSecond: null,
|
|
230
|
+
streaming: false,
|
|
231
|
+
userAgent: "provider-test",
|
|
232
|
+
origin: null,
|
|
233
|
+
apiFormat: "openai",
|
|
234
|
+
isTest: true,
|
|
235
|
+
providerName,
|
|
236
|
+
headers: captureFullDetails ? getResultHeaders(nonStreamingResult) : undefined,
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
const streamingRequestBody = JSON.stringify(
|
|
240
|
+
buildProviderTestRequestBody(usageModel, "streaming", "responses"),
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
await addTestLogEntry({
|
|
244
|
+
timestamp: new Date().toISOString(),
|
|
245
|
+
method: "POST",
|
|
246
|
+
path: "/v1/responses",
|
|
247
|
+
model: streamingResult.model ?? displayName,
|
|
248
|
+
sessionId: providerSessionId,
|
|
249
|
+
rawRequestBody: streamingRequestBody,
|
|
250
|
+
responseStatus: getResultResponseStatus(streamingResult),
|
|
251
|
+
responseText: getResultResponseText(streamingResult),
|
|
252
|
+
inputTokens: streamingResult.inputTokens ?? null,
|
|
253
|
+
outputTokens: streamingResult.outputTokens ?? null,
|
|
254
|
+
cacheCreationInputTokens: null,
|
|
255
|
+
cacheReadInputTokens: streamingResult.cacheReadInputTokens ?? null,
|
|
256
|
+
elapsedMs: getResultTotalStreamMs(streamingResult) ?? 0,
|
|
257
|
+
firstChunkMs: getResultFirstChunkMs(streamingResult),
|
|
258
|
+
totalStreamMs: getResultTotalStreamMs(streamingResult),
|
|
259
|
+
tokensPerSecond: getResultTokensPerSecond(streamingResult),
|
|
260
|
+
streaming: true,
|
|
261
|
+
streamingChunks: captureFullDetails ? streamingResult.streamingChunks : undefined,
|
|
262
|
+
userAgent: "provider-test",
|
|
263
|
+
origin: null,
|
|
264
|
+
apiFormat: "openai",
|
|
265
|
+
isTest: true,
|
|
266
|
+
providerName,
|
|
267
|
+
headers: captureFullDetails ? getResultHeaders(streamingResult) : undefined,
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
}
|
|
202
271
|
}
|
|
203
272
|
|
|
204
273
|
export const Route = createFileRoute("/api/providers/$providerId/test/log")({
|
|
@@ -232,10 +301,15 @@ export const Route = createFileRoute("/api/providers/$providerId/test/log")({
|
|
|
232
301
|
provider.anthropicBaseUrl !== undefined && provider.anthropicBaseUrl.length > 0
|
|
233
302
|
? provider.anthropicBaseUrl
|
|
234
303
|
: undefined;
|
|
235
|
-
const
|
|
304
|
+
const openaiChatUrl =
|
|
236
305
|
provider.openaiBaseUrl !== undefined && provider.openaiBaseUrl.length > 0
|
|
237
306
|
? provider.openaiBaseUrl
|
|
238
307
|
: undefined;
|
|
308
|
+
const openaiResponsesUrl =
|
|
309
|
+
provider.openaiResponsesBaseUrl !== undefined &&
|
|
310
|
+
provider.openaiResponsesBaseUrl.length > 0
|
|
311
|
+
? provider.openaiResponsesBaseUrl
|
|
312
|
+
: undefined;
|
|
239
313
|
|
|
240
314
|
const entries: Promise<void>[] = [];
|
|
241
315
|
if (results.models !== undefined) {
|
|
@@ -246,7 +320,8 @@ export const Route = createFileRoute("/api/providers/$providerId/test/log")({
|
|
|
246
320
|
provider.name,
|
|
247
321
|
providerSessionId,
|
|
248
322
|
anthropicUrl,
|
|
249
|
-
|
|
323
|
+
openaiChatUrl,
|
|
324
|
+
openaiResponsesUrl,
|
|
250
325
|
modelResult,
|
|
251
326
|
captureFullDetails,
|
|
252
327
|
),
|