@mcpmesh/sdk 0.8.0-beta.1
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/dist/__tests__/claude-handler.test.d.ts +7 -0
- package/dist/__tests__/claude-handler.test.d.ts.map +1 -0
- package/dist/__tests__/claude-handler.test.js +455 -0
- package/dist/__tests__/claude-handler.test.js.map +1 -0
- package/dist/__tests__/config.test.d.ts +7 -0
- package/dist/__tests__/config.test.d.ts.map +1 -0
- package/dist/__tests__/config.test.js +156 -0
- package/dist/__tests__/config.test.js.map +1 -0
- package/dist/__tests__/errors.test.d.ts +7 -0
- package/dist/__tests__/errors.test.d.ts.map +1 -0
- package/dist/__tests__/errors.test.js +170 -0
- package/dist/__tests__/errors.test.js.map +1 -0
- package/dist/__tests__/generic-handler.test.d.ts +7 -0
- package/dist/__tests__/generic-handler.test.d.ts.map +1 -0
- package/dist/__tests__/generic-handler.test.js +243 -0
- package/dist/__tests__/generic-handler.test.js.map +1 -0
- package/dist/__tests__/llm-provider.test.d.ts +7 -0
- package/dist/__tests__/llm-provider.test.d.ts.map +1 -0
- package/dist/__tests__/llm-provider.test.js +217 -0
- package/dist/__tests__/llm-provider.test.js.map +1 -0
- package/dist/__tests__/openai-handler.test.d.ts +7 -0
- package/dist/__tests__/openai-handler.test.d.ts.map +1 -0
- package/dist/__tests__/openai-handler.test.js +359 -0
- package/dist/__tests__/openai-handler.test.js.map +1 -0
- package/dist/__tests__/provider-handler-registry.test.d.ts +9 -0
- package/dist/__tests__/provider-handler-registry.test.d.ts.map +1 -0
- package/dist/__tests__/provider-handler-registry.test.js +187 -0
- package/dist/__tests__/provider-handler-registry.test.js.map +1 -0
- package/dist/__tests__/response-parser.test.d.ts +7 -0
- package/dist/__tests__/response-parser.test.d.ts.map +1 -0
- package/dist/__tests__/response-parser.test.js +360 -0
- package/dist/__tests__/response-parser.test.js.map +1 -0
- package/dist/__tests__/route.test.d.ts +7 -0
- package/dist/__tests__/route.test.d.ts.map +1 -0
- package/dist/__tests__/route.test.js +281 -0
- package/dist/__tests__/route.test.js.map +1 -0
- package/dist/__tests__/sse.test.d.ts +7 -0
- package/dist/__tests__/sse.test.d.ts.map +1 -0
- package/dist/__tests__/sse.test.js +172 -0
- package/dist/__tests__/sse.test.js.map +1 -0
- package/dist/__tests__/template.test.d.ts +7 -0
- package/dist/__tests__/template.test.d.ts.map +1 -0
- package/dist/__tests__/template.test.js +176 -0
- package/dist/__tests__/template.test.js.map +1 -0
- package/dist/__tests__/tracing.test.d.ts +7 -0
- package/dist/__tests__/tracing.test.d.ts.map +1 -0
- package/dist/__tests__/tracing.test.js +264 -0
- package/dist/__tests__/tracing.test.js.map +1 -0
- package/dist/agent.d.ts +165 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +626 -0
- package/dist/agent.js.map +1 -0
- package/dist/api-runtime.d.ts +166 -0
- package/dist/api-runtime.d.ts.map +1 -0
- package/dist/api-runtime.js +459 -0
- package/dist/api-runtime.js.map +1 -0
- package/dist/config.d.ts +31 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +60 -0
- package/dist/config.js.map +1 -0
- package/dist/debug.d.ts +47 -0
- package/dist/debug.d.ts.map +1 -0
- package/dist/debug.js +86 -0
- package/dist/debug.js.map +1 -0
- package/dist/errors.d.ts +99 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +110 -0
- package/dist/errors.js.map +1 -0
- package/dist/express.d.ts +146 -0
- package/dist/express.d.ts.map +1 -0
- package/dist/express.js +371 -0
- package/dist/express.js.map +1 -0
- package/dist/index.d.ts +96 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +107 -0
- package/dist/index.js.map +1 -0
- package/dist/llm-agent.d.ts +193 -0
- package/dist/llm-agent.d.ts.map +1 -0
- package/dist/llm-agent.js +634 -0
- package/dist/llm-agent.js.map +1 -0
- package/dist/llm-provider.d.ts +323 -0
- package/dist/llm-provider.d.ts.map +1 -0
- package/dist/llm-provider.js +446 -0
- package/dist/llm-provider.js.map +1 -0
- package/dist/llm.d.ts +194 -0
- package/dist/llm.d.ts.map +1 -0
- package/dist/llm.js +304 -0
- package/dist/llm.js.map +1 -0
- package/dist/provider-handlers/claude-handler.d.ts +98 -0
- package/dist/provider-handlers/claude-handler.d.ts.map +1 -0
- package/dist/provider-handlers/claude-handler.js +268 -0
- package/dist/provider-handlers/claude-handler.js.map +1 -0
- package/dist/provider-handlers/gemini-handler.d.ts +82 -0
- package/dist/provider-handlers/gemini-handler.d.ts.map +1 -0
- package/dist/provider-handlers/gemini-handler.js +152 -0
- package/dist/provider-handlers/gemini-handler.js.map +1 -0
- package/dist/provider-handlers/generic-handler.d.ts +78 -0
- package/dist/provider-handlers/generic-handler.d.ts.map +1 -0
- package/dist/provider-handlers/generic-handler.js +152 -0
- package/dist/provider-handlers/generic-handler.js.map +1 -0
- package/dist/provider-handlers/index.d.ts +29 -0
- package/dist/provider-handlers/index.d.ts.map +1 -0
- package/dist/provider-handlers/index.js +32 -0
- package/dist/provider-handlers/index.js.map +1 -0
- package/dist/provider-handlers/openai-handler.d.ts +86 -0
- package/dist/provider-handlers/openai-handler.d.ts.map +1 -0
- package/dist/provider-handlers/openai-handler.js +160 -0
- package/dist/provider-handlers/openai-handler.js.map +1 -0
- package/dist/provider-handlers/provider-handler-registry.d.ts +124 -0
- package/dist/provider-handlers/provider-handler-registry.d.ts.map +1 -0
- package/dist/provider-handlers/provider-handler-registry.js +180 -0
- package/dist/provider-handlers/provider-handler-registry.js.map +1 -0
- package/dist/provider-handlers/provider-handler.d.ts +245 -0
- package/dist/provider-handlers/provider-handler.d.ts.map +1 -0
- package/dist/provider-handlers/provider-handler.js +238 -0
- package/dist/provider-handlers/provider-handler.js.map +1 -0
- package/dist/proxy.d.ts +44 -0
- package/dist/proxy.d.ts.map +1 -0
- package/dist/proxy.js +324 -0
- package/dist/proxy.js.map +1 -0
- package/dist/response-parser.d.ts +103 -0
- package/dist/response-parser.d.ts.map +1 -0
- package/dist/response-parser.js +232 -0
- package/dist/response-parser.js.map +1 -0
- package/dist/route.d.ts +185 -0
- package/dist/route.d.ts.map +1 -0
- package/dist/route.js +310 -0
- package/dist/route.js.map +1 -0
- package/dist/sse.d.ts +45 -0
- package/dist/sse.d.ts.map +1 -0
- package/dist/sse.js +77 -0
- package/dist/sse.js.map +1 -0
- package/dist/template.d.ts +86 -0
- package/dist/template.d.ts.map +1 -0
- package/dist/template.js +206 -0
- package/dist/template.js.map +1 -0
- package/dist/tracing.d.ts +88 -0
- package/dist/tracing.d.ts.map +1 -0
- package/dist/tracing.js +193 -0
- package/dist/tracing.js.map +1 -0
- package/dist/types.d.ts +618 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/package.json +68 -0
package/dist/proxy.js
ADDED
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Mesh proxy implementation for dependency injection.
|
|
3
|
+
*
|
|
4
|
+
* Provides HTTP client proxies for calling remote MCP agents.
|
|
5
|
+
*/
|
|
6
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
7
|
+
import { generateSpanId, publishTraceSpan, createTraceHeaders, } from "./tracing.js";
|
|
8
|
+
/**
|
|
9
|
+
* AsyncLocalStorage for trace context - provides async-safe context propagation.
|
|
10
|
+
* Unlike module-level variables, this correctly handles concurrent requests
|
|
11
|
+
* without trace context bleeding between them.
|
|
12
|
+
*/
|
|
13
|
+
const traceContextStorage = new AsyncLocalStorage();
|
|
14
|
+
/**
|
|
15
|
+
* Run a function with trace context.
|
|
16
|
+
* The context is automatically propagated to all async operations within the callback.
|
|
17
|
+
* This is the preferred way to set trace context for tool execution.
|
|
18
|
+
*/
|
|
19
|
+
export function runWithTraceContext(ctx, fn) {
|
|
20
|
+
return traceContextStorage.run(ctx, fn);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Get the current trace context.
|
|
24
|
+
* Returns the trace context for the current async execution context,
|
|
25
|
+
* or null if not within a traced context.
|
|
26
|
+
*/
|
|
27
|
+
export function getCurrentTraceContext() {
|
|
28
|
+
return traceContextStorage.getStore() ?? null;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* @deprecated Use runWithTraceContext() instead for async-safe context propagation.
|
|
32
|
+
* This function is kept for backward compatibility but does nothing.
|
|
33
|
+
*/
|
|
34
|
+
export function setCurrentTraceContext(_ctx) {
|
|
35
|
+
// No-op - use runWithTraceContext() instead
|
|
36
|
+
// This is kept for backward compatibility with any external code
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Create an McpMeshAgent proxy for a resolved dependency.
|
|
40
|
+
*
|
|
41
|
+
* The returned object is callable (invokes the bound function)
|
|
42
|
+
* and also has methods for calling other tools on the agent.
|
|
43
|
+
*/
|
|
44
|
+
export function createProxy(endpoint, capability, functionName, kwargs) {
|
|
45
|
+
const timeout = (kwargs?.timeout ?? 30) * 1000; // Convert to ms
|
|
46
|
+
const maxAttempts = kwargs?.maxAttempts ?? 1;
|
|
47
|
+
// The proxy function that calls the bound tool
|
|
48
|
+
// Returns parsed object (like Python) or raw string if not JSON
|
|
49
|
+
const proxyFn = async (args) => {
|
|
50
|
+
const result = await callMcpTool(endpoint, functionName, args, timeout, maxAttempts, capability);
|
|
51
|
+
// Parse JSON if possible, otherwise return raw string (matches Python behavior)
|
|
52
|
+
try {
|
|
53
|
+
return JSON.parse(result);
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return result;
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
// Attach properties and methods
|
|
60
|
+
Object.defineProperties(proxyFn, {
|
|
61
|
+
endpoint: { value: endpoint, writable: false },
|
|
62
|
+
capability: { value: capability, writable: false },
|
|
63
|
+
functionName: { value: functionName, writable: false },
|
|
64
|
+
isAvailable: { value: true, writable: false },
|
|
65
|
+
callTool: {
|
|
66
|
+
value: async (toolName, args) => {
|
|
67
|
+
const result = await callMcpTool(endpoint, toolName, args, timeout, maxAttempts, capability);
|
|
68
|
+
try {
|
|
69
|
+
return JSON.parse(result);
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return result;
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
writable: false,
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
return proxyFn;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Call an MCP tool via HTTP POST.
|
|
82
|
+
*
|
|
83
|
+
* Uses the MCP HTTP Streamable protocol:
|
|
84
|
+
* POST /mcp with JSON-RPC 2.0 payload.
|
|
85
|
+
* Includes distributed tracing: propagates trace context and publishes spans.
|
|
86
|
+
*/
|
|
87
|
+
async function callMcpTool(endpoint, toolName, args, timeout, maxAttempts, capability) {
|
|
88
|
+
// Ensure endpoint ends with /mcp
|
|
89
|
+
const mcpEndpoint = endpoint.endsWith("/mcp")
|
|
90
|
+
? endpoint
|
|
91
|
+
: `${endpoint.replace(/\/$/, "")}/mcp`;
|
|
92
|
+
// Tracing: create span for this outgoing proxy call
|
|
93
|
+
// Use AsyncLocalStorage to get trace context for the current async execution
|
|
94
|
+
const traceCtx = getCurrentTraceContext();
|
|
95
|
+
const spanId = traceCtx ? generateSpanId() : null;
|
|
96
|
+
const startTime = Date.now() / 1000;
|
|
97
|
+
// Build arguments with trace context injection (for downstream agents)
|
|
98
|
+
// This is the fallback mechanism since fastmcp doesn't expose HTTP headers
|
|
99
|
+
const argsWithTrace = { ...(args ?? {}) };
|
|
100
|
+
if (traceCtx && spanId) {
|
|
101
|
+
// Inject trace context into arguments - downstream agent will extract these
|
|
102
|
+
// spanId is the proxy span we're about to publish, which becomes child's parent
|
|
103
|
+
argsWithTrace._trace_id = traceCtx.traceId;
|
|
104
|
+
argsWithTrace._parent_span = spanId;
|
|
105
|
+
}
|
|
106
|
+
const payload = {
|
|
107
|
+
jsonrpc: "2.0",
|
|
108
|
+
id: generateRequestId(),
|
|
109
|
+
method: "tools/call",
|
|
110
|
+
params: {
|
|
111
|
+
name: toolName,
|
|
112
|
+
arguments: argsWithTrace,
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
let lastError = null;
|
|
116
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
117
|
+
try {
|
|
118
|
+
const controller = new AbortController();
|
|
119
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
120
|
+
// Build headers with trace context propagation
|
|
121
|
+
const headers = {
|
|
122
|
+
"Content-Type": "application/json",
|
|
123
|
+
Accept: "application/json, text/event-stream",
|
|
124
|
+
};
|
|
125
|
+
// Propagate trace context to downstream agent
|
|
126
|
+
if (traceCtx && spanId) {
|
|
127
|
+
Object.assign(headers, createTraceHeaders(traceCtx.traceId, spanId));
|
|
128
|
+
}
|
|
129
|
+
const response = await fetch(mcpEndpoint, {
|
|
130
|
+
method: "POST",
|
|
131
|
+
headers,
|
|
132
|
+
body: JSON.stringify(payload),
|
|
133
|
+
signal: controller.signal,
|
|
134
|
+
});
|
|
135
|
+
clearTimeout(timeoutId);
|
|
136
|
+
if (!response.ok) {
|
|
137
|
+
throw new Error(`MCP call failed: ${response.status} ${response.statusText}`);
|
|
138
|
+
}
|
|
139
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
140
|
+
// Handle SSE streaming response
|
|
141
|
+
if (contentType.includes("text/event-stream")) {
|
|
142
|
+
const sseResult = await parseSSEResponse(response);
|
|
143
|
+
// Publish success span
|
|
144
|
+
publishProxySpan(traceCtx, spanId, startTime, toolName, capability, endpoint, true, null, typeof sseResult);
|
|
145
|
+
return sseResult;
|
|
146
|
+
}
|
|
147
|
+
// Handle JSON response
|
|
148
|
+
const result = (await response.json());
|
|
149
|
+
if (result.error) {
|
|
150
|
+
const errorMsg = result.error.message ?? JSON.stringify(result.error);
|
|
151
|
+
// Publish error span
|
|
152
|
+
publishProxySpan(traceCtx, spanId, startTime, toolName, capability, endpoint, false, errorMsg, "error");
|
|
153
|
+
throw new Error(`MCP error: ${errorMsg}`);
|
|
154
|
+
}
|
|
155
|
+
// Extract content from result
|
|
156
|
+
const content = extractContent(result.result);
|
|
157
|
+
// Publish success span
|
|
158
|
+
publishProxySpan(traceCtx, spanId, startTime, toolName, capability, endpoint, true, null, typeof content);
|
|
159
|
+
return content;
|
|
160
|
+
}
|
|
161
|
+
catch (err) {
|
|
162
|
+
lastError = err instanceof Error ? err : new Error(String(err));
|
|
163
|
+
// Don't retry on abort (timeout)
|
|
164
|
+
if (lastError.name === "AbortError") {
|
|
165
|
+
publishProxySpan(traceCtx, spanId, startTime, toolName, capability, endpoint, false, "timeout", "error");
|
|
166
|
+
throw new Error(`MCP call timed out after ${timeout}ms`);
|
|
167
|
+
}
|
|
168
|
+
// Retry on network errors
|
|
169
|
+
if (attempt < maxAttempts - 1) {
|
|
170
|
+
await sleep(100 * (attempt + 1)); // Exponential backoff
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
// All retries failed
|
|
176
|
+
publishProxySpan(traceCtx, spanId, startTime, toolName, capability, endpoint, false, lastError?.message ?? "unknown", "error");
|
|
177
|
+
throw lastError ?? new Error("MCP call failed");
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Helper to publish a proxy call span (fire and forget).
|
|
181
|
+
*/
|
|
182
|
+
function publishProxySpan(traceCtx, spanId, startTime, _toolName, _capability, endpoint, success, error, resultType) {
|
|
183
|
+
if (!traceCtx || !spanId)
|
|
184
|
+
return;
|
|
185
|
+
const endTime = Date.now() / 1000;
|
|
186
|
+
const durationMs = (endTime - startTime) * 1000;
|
|
187
|
+
// Fire and forget - don't await
|
|
188
|
+
publishTraceSpan({
|
|
189
|
+
traceId: traceCtx.traceId,
|
|
190
|
+
spanId,
|
|
191
|
+
parentSpan: traceCtx.parentSpanId,
|
|
192
|
+
functionName: "proxy_call_wrapper",
|
|
193
|
+
startTime,
|
|
194
|
+
endTime,
|
|
195
|
+
durationMs,
|
|
196
|
+
success,
|
|
197
|
+
error,
|
|
198
|
+
resultType,
|
|
199
|
+
argsCount: 0,
|
|
200
|
+
kwargsCount: 0,
|
|
201
|
+
dependencies: [endpoint],
|
|
202
|
+
injectedDependencies: 0,
|
|
203
|
+
meshPositions: [],
|
|
204
|
+
}).catch(() => {
|
|
205
|
+
// Silently ignore publish errors
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Parse SSE response from MCP HTTP Streamable transport.
|
|
210
|
+
*/
|
|
211
|
+
async function parseSSEResponse(response) {
|
|
212
|
+
const reader = response.body?.getReader();
|
|
213
|
+
if (!reader) {
|
|
214
|
+
throw new Error("No response body");
|
|
215
|
+
}
|
|
216
|
+
const decoder = new TextDecoder();
|
|
217
|
+
let buffer = "";
|
|
218
|
+
let result = "";
|
|
219
|
+
while (true) {
|
|
220
|
+
const { done, value } = await reader.read();
|
|
221
|
+
if (done)
|
|
222
|
+
break;
|
|
223
|
+
buffer += decoder.decode(value, { stream: true });
|
|
224
|
+
// Parse SSE events
|
|
225
|
+
const lines = buffer.split("\n");
|
|
226
|
+
buffer = lines.pop() ?? ""; // Keep incomplete line
|
|
227
|
+
for (const line of lines) {
|
|
228
|
+
if (line.startsWith("data: ")) {
|
|
229
|
+
const data = line.slice(6);
|
|
230
|
+
if (data === "[DONE]")
|
|
231
|
+
continue;
|
|
232
|
+
try {
|
|
233
|
+
const event = JSON.parse(data);
|
|
234
|
+
// Handle JSON-RPC response
|
|
235
|
+
const jsonRpcEvent = event;
|
|
236
|
+
if (jsonRpcEvent.result) {
|
|
237
|
+
result = extractContent(jsonRpcEvent.result);
|
|
238
|
+
}
|
|
239
|
+
else if (jsonRpcEvent.error) {
|
|
240
|
+
throw new Error(`MCP error: ${jsonRpcEvent.error.message ?? JSON.stringify(jsonRpcEvent.error)}`);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
catch (e) {
|
|
244
|
+
// Ignore parse errors for non-JSON data events
|
|
245
|
+
if (!(e instanceof SyntaxError))
|
|
246
|
+
throw e;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return result;
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Extract content from MCP CallToolResult.
|
|
255
|
+
*
|
|
256
|
+
* Handles various content formats:
|
|
257
|
+
* - { content: [{ type: "text", text: "..." }] }
|
|
258
|
+
* - { content: "..." }
|
|
259
|
+
* - Direct string
|
|
260
|
+
*/
|
|
261
|
+
function extractContent(result) {
|
|
262
|
+
if (typeof result === "string") {
|
|
263
|
+
return result;
|
|
264
|
+
}
|
|
265
|
+
if (result && typeof result === "object") {
|
|
266
|
+
const obj = result;
|
|
267
|
+
// Handle { content: [...] } format
|
|
268
|
+
if (Array.isArray(obj.content)) {
|
|
269
|
+
const textParts = [];
|
|
270
|
+
for (const item of obj.content) {
|
|
271
|
+
if (item && typeof item === "object" && "text" in item) {
|
|
272
|
+
textParts.push(String(item.text));
|
|
273
|
+
}
|
|
274
|
+
else if (typeof item === "string") {
|
|
275
|
+
textParts.push(item);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
const text = textParts.join("");
|
|
279
|
+
// Try to parse as JSON if it looks like JSON
|
|
280
|
+
if (text.startsWith("{") || text.startsWith("[")) {
|
|
281
|
+
try {
|
|
282
|
+
return JSON.stringify(JSON.parse(text));
|
|
283
|
+
}
|
|
284
|
+
catch {
|
|
285
|
+
return text;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return text;
|
|
289
|
+
}
|
|
290
|
+
// Handle { content: "..." } format
|
|
291
|
+
if (typeof obj.content === "string") {
|
|
292
|
+
return obj.content;
|
|
293
|
+
}
|
|
294
|
+
// Return JSON stringified
|
|
295
|
+
return JSON.stringify(result);
|
|
296
|
+
}
|
|
297
|
+
return String(result);
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Generate a unique request ID.
|
|
301
|
+
*/
|
|
302
|
+
function generateRequestId() {
|
|
303
|
+
return `req_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Sleep for a given duration.
|
|
307
|
+
*/
|
|
308
|
+
function sleep(ms) {
|
|
309
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Normalize a DependencySpec to canonical form.
|
|
313
|
+
*/
|
|
314
|
+
export function normalizeDependency(dep) {
|
|
315
|
+
if (typeof dep === "string") {
|
|
316
|
+
return { capability: dep, tags: [] };
|
|
317
|
+
}
|
|
318
|
+
return {
|
|
319
|
+
capability: dep.capability,
|
|
320
|
+
tags: dep.tags ?? [],
|
|
321
|
+
version: dep.version,
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
//# sourceMappingURL=proxy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy.js","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Response parser for LLM outputs with Zod schema validation.
|
|
3
|
+
*
|
|
4
|
+
* Handles extraction of structured data from LLM responses, including:
|
|
5
|
+
* - JSON extraction from code blocks or raw text
|
|
6
|
+
* - Schema validation using Zod
|
|
7
|
+
* - Error handling with informative messages
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* const schema = z.object({
|
|
12
|
+
* answer: z.string(),
|
|
13
|
+
* confidence: z.number(),
|
|
14
|
+
* });
|
|
15
|
+
*
|
|
16
|
+
* const parser = new ResponseParser(schema);
|
|
17
|
+
* const result = parser.parse('{"answer": "hello", "confidence": 0.95}');
|
|
18
|
+
* // => { answer: "hello", confidence: 0.95 }
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
import type { ZodType, ZodError as ZodErrorType } from "zod";
|
|
22
|
+
import { ResponseParseError } from "./errors.js";
|
|
23
|
+
export { ResponseParseError };
|
|
24
|
+
/**
|
|
25
|
+
* Extract JSON from a string that may contain markdown code blocks.
|
|
26
|
+
*
|
|
27
|
+
* Handles:
|
|
28
|
+
* - ```json ... ``` code blocks
|
|
29
|
+
* - ``` ... ``` code blocks (no language)
|
|
30
|
+
* - Raw JSON (object or array) using progressive JSON.parse
|
|
31
|
+
*/
|
|
32
|
+
export declare function extractJson(content: string): string | null;
|
|
33
|
+
/**
|
|
34
|
+
* Parser for LLM responses with optional Zod schema validation.
|
|
35
|
+
*
|
|
36
|
+
* @template T - The output type (inferred from Zod schema)
|
|
37
|
+
*/
|
|
38
|
+
export declare class ResponseParser<T = string> {
|
|
39
|
+
private schema;
|
|
40
|
+
/**
|
|
41
|
+
* Create a new ResponseParser.
|
|
42
|
+
*
|
|
43
|
+
* @param schema - Optional Zod schema for validation. If not provided, returns raw string.
|
|
44
|
+
*/
|
|
45
|
+
constructor(schema?: ZodType<T>);
|
|
46
|
+
/**
|
|
47
|
+
* Parse an LLM response string.
|
|
48
|
+
*
|
|
49
|
+
* If a schema is provided, attempts to extract and validate JSON.
|
|
50
|
+
* If no schema, returns the raw string content.
|
|
51
|
+
*
|
|
52
|
+
* @param content - Raw LLM response content
|
|
53
|
+
* @returns Parsed and validated result
|
|
54
|
+
* @throws ResponseParseError if parsing or validation fails
|
|
55
|
+
*/
|
|
56
|
+
parse(content: string): T;
|
|
57
|
+
/**
|
|
58
|
+
* Try to parse, returning null on failure instead of throwing.
|
|
59
|
+
*
|
|
60
|
+
* @param content - Raw LLM response content
|
|
61
|
+
* @returns Parsed result or null if parsing fails
|
|
62
|
+
*/
|
|
63
|
+
tryParse(content: string): T | null;
|
|
64
|
+
/**
|
|
65
|
+
* Get the Zod schema (if any).
|
|
66
|
+
*/
|
|
67
|
+
getSchema(): ZodType<T> | null;
|
|
68
|
+
/**
|
|
69
|
+
* Check if this parser has a schema.
|
|
70
|
+
*/
|
|
71
|
+
hasSchema(): boolean;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Create a response parser with an optional Zod schema.
|
|
75
|
+
*
|
|
76
|
+
* @param schema - Optional Zod schema for validation
|
|
77
|
+
* @returns ResponseParser instance
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* ```typescript
|
|
81
|
+
* // No schema - returns raw string
|
|
82
|
+
* const stringParser = createResponseParser();
|
|
83
|
+
* const str = stringParser.parse("Hello world");
|
|
84
|
+
*
|
|
85
|
+
* // With schema - validates and returns typed object
|
|
86
|
+
* const objectParser = createResponseParser(z.object({
|
|
87
|
+
* name: z.string(),
|
|
88
|
+
* age: z.number(),
|
|
89
|
+
* }));
|
|
90
|
+
* const obj = objectParser.parse('{"name": "John", "age": 30}');
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
93
|
+
export declare function createResponseParser<T = string>(schema?: ZodType<T>): ResponseParser<T>;
|
|
94
|
+
/**
|
|
95
|
+
* Convert a Zod schema to a human-readable description for LLM prompting.
|
|
96
|
+
* Uses zod-to-json-schema for forward compatibility with Zod v4.
|
|
97
|
+
*/
|
|
98
|
+
export declare function zodSchemaToPromptDescription(schema: ZodType): string;
|
|
99
|
+
/**
|
|
100
|
+
* Format a Zod error into a human-readable string.
|
|
101
|
+
*/
|
|
102
|
+
export declare function formatZodError(error: ZodErrorType): string;
|
|
103
|
+
//# sourceMappingURL=response-parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"response-parser.d.ts","sourceRoot":"","sources":["../src/response-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,IAAI,YAAY,EAAE,MAAM,KAAK,CAAC;AAE7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAGjD,OAAO,EAAE,kBAAkB,EAAE,CAAC;AAE9B;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAuB1D;AA2CD;;;;GAIG;AACH,qBAAa,cAAc,CAAC,CAAC,GAAG,MAAM;IACpC,OAAO,CAAC,MAAM,CAAoB;IAElC;;;;OAIG;gBACS,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;IAI/B;;;;;;;;;OASG;IACH,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,CAAC;IA0CzB;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,CAAC,GAAG,IAAI;IAQnC;;OAEG;IACH,SAAS,IAAI,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI;IAI9B;;OAEG;IACH,SAAS,IAAI,OAAO;CAGrB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,GAAG,MAAM,EAC7C,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,GAClB,cAAc,CAAC,CAAC,CAAC,CAEnB;AAED;;;GAGG;AACH,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CA2BpE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,CAO1D"}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Response parser for LLM outputs with Zod schema validation.
|
|
3
|
+
*
|
|
4
|
+
* Handles extraction of structured data from LLM responses, including:
|
|
5
|
+
* - JSON extraction from code blocks or raw text
|
|
6
|
+
* - Schema validation using Zod
|
|
7
|
+
* - Error handling with informative messages
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* const schema = z.object({
|
|
12
|
+
* answer: z.string(),
|
|
13
|
+
* confidence: z.number(),
|
|
14
|
+
* });
|
|
15
|
+
*
|
|
16
|
+
* const parser = new ResponseParser(schema);
|
|
17
|
+
* const result = parser.parse('{"answer": "hello", "confidence": 0.95}');
|
|
18
|
+
* // => { answer: "hello", confidence: 0.95 }
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
22
|
+
import { ResponseParseError } from "./errors.js";
|
|
23
|
+
// Re-export for backwards compatibility
|
|
24
|
+
export { ResponseParseError };
|
|
25
|
+
/**
|
|
26
|
+
* Extract JSON from a string that may contain markdown code blocks.
|
|
27
|
+
*
|
|
28
|
+
* Handles:
|
|
29
|
+
* - ```json ... ``` code blocks
|
|
30
|
+
* - ``` ... ``` code blocks (no language)
|
|
31
|
+
* - Raw JSON (object or array) using progressive JSON.parse
|
|
32
|
+
*/
|
|
33
|
+
export function extractJson(content) {
|
|
34
|
+
// Strategy 1: Try to extract from markdown code blocks first
|
|
35
|
+
const codeBlockMatch = content.match(/```(?:json)?\s*([\s\S]*?)```/);
|
|
36
|
+
if (codeBlockMatch) {
|
|
37
|
+
return codeBlockMatch[1].trim();
|
|
38
|
+
}
|
|
39
|
+
// Strategy 2: Try to find JSON object using progressive JSON.parse
|
|
40
|
+
// This handles braces inside strings correctly
|
|
41
|
+
const braceStart = content.indexOf("{");
|
|
42
|
+
if (braceStart !== -1) {
|
|
43
|
+
const result = tryProgressiveParse(content, braceStart, "{", "}");
|
|
44
|
+
if (result)
|
|
45
|
+
return result;
|
|
46
|
+
}
|
|
47
|
+
// Strategy 3: Try to find JSON array using progressive JSON.parse
|
|
48
|
+
const bracketStart = content.indexOf("[");
|
|
49
|
+
if (bracketStart !== -1) {
|
|
50
|
+
const result = tryProgressiveParse(content, bracketStart, "[", "]");
|
|
51
|
+
if (result)
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Try to extract valid JSON by progressively extending the end position.
|
|
58
|
+
* This correctly handles braces/brackets inside string values.
|
|
59
|
+
*/
|
|
60
|
+
function tryProgressiveParse(content, start, openChar, closeChar) {
|
|
61
|
+
// Quick check: count characters to find potential end positions
|
|
62
|
+
let depth = 0;
|
|
63
|
+
const potentialEnds = [];
|
|
64
|
+
for (let i = start; i < content.length; i++) {
|
|
65
|
+
const char = content[i];
|
|
66
|
+
if (char === openChar) {
|
|
67
|
+
depth++;
|
|
68
|
+
}
|
|
69
|
+
else if (char === closeChar) {
|
|
70
|
+
depth--;
|
|
71
|
+
if (depth === 0) {
|
|
72
|
+
potentialEnds.push(i);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// Try each potential end position (shortest first for efficiency)
|
|
77
|
+
for (const end of potentialEnds) {
|
|
78
|
+
const candidate = content.slice(start, end + 1);
|
|
79
|
+
try {
|
|
80
|
+
JSON.parse(candidate);
|
|
81
|
+
return candidate;
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
// Not valid JSON, try next potential end
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Parser for LLM responses with optional Zod schema validation.
|
|
92
|
+
*
|
|
93
|
+
* @template T - The output type (inferred from Zod schema)
|
|
94
|
+
*/
|
|
95
|
+
export class ResponseParser {
|
|
96
|
+
schema;
|
|
97
|
+
/**
|
|
98
|
+
* Create a new ResponseParser.
|
|
99
|
+
*
|
|
100
|
+
* @param schema - Optional Zod schema for validation. If not provided, returns raw string.
|
|
101
|
+
*/
|
|
102
|
+
constructor(schema) {
|
|
103
|
+
this.schema = schema ?? null;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Parse an LLM response string.
|
|
107
|
+
*
|
|
108
|
+
* If a schema is provided, attempts to extract and validate JSON.
|
|
109
|
+
* If no schema, returns the raw string content.
|
|
110
|
+
*
|
|
111
|
+
* @param content - Raw LLM response content
|
|
112
|
+
* @returns Parsed and validated result
|
|
113
|
+
* @throws ResponseParseError if parsing or validation fails
|
|
114
|
+
*/
|
|
115
|
+
parse(content) {
|
|
116
|
+
// No schema - return raw string
|
|
117
|
+
if (!this.schema) {
|
|
118
|
+
return content;
|
|
119
|
+
}
|
|
120
|
+
// Extract JSON from content
|
|
121
|
+
const jsonStr = extractJson(content);
|
|
122
|
+
if (!jsonStr) {
|
|
123
|
+
throw new ResponseParseError("Could not extract JSON from response. Expected JSON object or array.", content);
|
|
124
|
+
}
|
|
125
|
+
// Parse JSON
|
|
126
|
+
let parsed;
|
|
127
|
+
try {
|
|
128
|
+
parsed = JSON.parse(jsonStr);
|
|
129
|
+
}
|
|
130
|
+
catch (err) {
|
|
131
|
+
throw new ResponseParseError(`Invalid JSON: ${err instanceof Error ? err.message : String(err)}`, content);
|
|
132
|
+
}
|
|
133
|
+
// Validate with Zod schema
|
|
134
|
+
const result = this.schema.safeParse(parsed);
|
|
135
|
+
if (!result.success) {
|
|
136
|
+
const issues = result.error.issues
|
|
137
|
+
.map((i) => `${i.path.join(".")}: ${i.message}`)
|
|
138
|
+
.join("; ");
|
|
139
|
+
throw new ResponseParseError(`Schema validation failed: ${issues}`, content, result.error);
|
|
140
|
+
}
|
|
141
|
+
return result.data;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Try to parse, returning null on failure instead of throwing.
|
|
145
|
+
*
|
|
146
|
+
* @param content - Raw LLM response content
|
|
147
|
+
* @returns Parsed result or null if parsing fails
|
|
148
|
+
*/
|
|
149
|
+
tryParse(content) {
|
|
150
|
+
try {
|
|
151
|
+
return this.parse(content);
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Get the Zod schema (if any).
|
|
159
|
+
*/
|
|
160
|
+
getSchema() {
|
|
161
|
+
return this.schema;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Check if this parser has a schema.
|
|
165
|
+
*/
|
|
166
|
+
hasSchema() {
|
|
167
|
+
return this.schema !== null;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Create a response parser with an optional Zod schema.
|
|
172
|
+
*
|
|
173
|
+
* @param schema - Optional Zod schema for validation
|
|
174
|
+
* @returns ResponseParser instance
|
|
175
|
+
*
|
|
176
|
+
* @example
|
|
177
|
+
* ```typescript
|
|
178
|
+
* // No schema - returns raw string
|
|
179
|
+
* const stringParser = createResponseParser();
|
|
180
|
+
* const str = stringParser.parse("Hello world");
|
|
181
|
+
*
|
|
182
|
+
* // With schema - validates and returns typed object
|
|
183
|
+
* const objectParser = createResponseParser(z.object({
|
|
184
|
+
* name: z.string(),
|
|
185
|
+
* age: z.number(),
|
|
186
|
+
* }));
|
|
187
|
+
* const obj = objectParser.parse('{"name": "John", "age": 30}');
|
|
188
|
+
* ```
|
|
189
|
+
*/
|
|
190
|
+
export function createResponseParser(schema) {
|
|
191
|
+
return new ResponseParser(schema);
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Convert a Zod schema to a human-readable description for LLM prompting.
|
|
195
|
+
* Uses zod-to-json-schema for forward compatibility with Zod v4.
|
|
196
|
+
*/
|
|
197
|
+
export function zodSchemaToPromptDescription(schema) {
|
|
198
|
+
// Convert to JSON Schema using the public API (Zod v4 compatible)
|
|
199
|
+
const jsonSchema = zodToJsonSchema(schema, { $refStrategy: "none" });
|
|
200
|
+
// Check for top-level description
|
|
201
|
+
if (jsonSchema.description && typeof jsonSchema.description === "string") {
|
|
202
|
+
return jsonSchema.description;
|
|
203
|
+
}
|
|
204
|
+
// If it's an object type, describe the fields
|
|
205
|
+
if (jsonSchema.type === "object" && jsonSchema.properties) {
|
|
206
|
+
const properties = jsonSchema.properties;
|
|
207
|
+
const fields = Object.keys(properties).map((key) => {
|
|
208
|
+
const prop = properties[key];
|
|
209
|
+
const fieldType = prop.type || "unknown";
|
|
210
|
+
const fieldDesc = prop.description ? ` (${prop.description})` : "";
|
|
211
|
+
return ` - ${key}: ${fieldType}${fieldDesc}`;
|
|
212
|
+
});
|
|
213
|
+
return `JSON object with fields:\n${fields.join("\n")}`;
|
|
214
|
+
}
|
|
215
|
+
// Fallback for other types
|
|
216
|
+
if (jsonSchema.type) {
|
|
217
|
+
return `JSON ${jsonSchema.type}`;
|
|
218
|
+
}
|
|
219
|
+
return "JSON";
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Format a Zod error into a human-readable string.
|
|
223
|
+
*/
|
|
224
|
+
export function formatZodError(error) {
|
|
225
|
+
return error.issues
|
|
226
|
+
.map((issue) => {
|
|
227
|
+
const path = issue.path.length > 0 ? issue.path.join(".") + ": " : "";
|
|
228
|
+
return `${path}${issue.message}`;
|
|
229
|
+
})
|
|
230
|
+
.join("\n");
|
|
231
|
+
}
|
|
232
|
+
//# sourceMappingURL=response-parser.js.map
|