@tanstack/ai 0.42.0 → 0.43.0
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/README.md +15 -1
- package/dist/esm/activities/chat/adapter.js +23 -16
- package/dist/esm/activities/chat/adapter.js.map +1 -1
- package/dist/esm/activities/chat/agent-loop-strategies.d.ts +5 -36
- package/dist/esm/activities/chat/agent-loop-strategies.js +75 -21
- package/dist/esm/activities/chat/agent-loop-strategies.js.map +1 -1
- package/dist/esm/activities/chat/cancel.d.ts +40 -0
- package/dist/esm/activities/chat/cancel.js +54 -0
- package/dist/esm/activities/chat/cancel.js.map +1 -0
- package/dist/esm/activities/chat/index.d.ts +28 -21
- package/dist/esm/activities/chat/index.js +2100 -1813
- package/dist/esm/activities/chat/index.js.map +1 -1
- package/dist/esm/activities/chat/mcp/manager.d.ts +2 -2
- package/dist/esm/activities/chat/mcp/manager.js +90 -77
- package/dist/esm/activities/chat/mcp/manager.js.map +1 -1
- package/dist/esm/activities/chat/mcp/types.d.ts +2 -2
- package/dist/esm/activities/chat/messages.js +397 -346
- package/dist/esm/activities/chat/messages.js.map +1 -1
- package/dist/esm/activities/chat/middleware/builder.js +17 -15
- package/dist/esm/activities/chat/middleware/builder.js.map +1 -1
- package/dist/esm/activities/chat/middleware/capabilities.js +78 -43
- package/dist/esm/activities/chat/middleware/capabilities.js.map +1 -1
- package/dist/esm/activities/chat/middleware/compose.d.ts +94 -1
- package/dist/esm/activities/chat/middleware/compose.js +623 -531
- package/dist/esm/activities/chat/middleware/compose.js.map +1 -1
- package/dist/esm/activities/chat/middleware/define.js +12 -5
- package/dist/esm/activities/chat/middleware/define.js.map +1 -1
- package/dist/esm/activities/chat/middleware/index.d.ts +5 -1
- package/dist/esm/activities/chat/middleware/locks.d.ts +50 -0
- package/dist/esm/activities/chat/middleware/locks.js +71 -0
- package/dist/esm/activities/chat/middleware/locks.js.map +1 -0
- package/dist/esm/activities/chat/middleware/pending-turn.d.ts +15 -0
- package/dist/esm/activities/chat/middleware/pending-turn.js +35 -0
- package/dist/esm/activities/chat/middleware/pending-turn.js.map +1 -0
- package/dist/esm/activities/chat/middleware/run-disconnect.d.ts +23 -0
- package/dist/esm/activities/chat/middleware/run-disconnect.js +42 -0
- package/dist/esm/activities/chat/middleware/run-disconnect.js.map +1 -0
- package/dist/esm/activities/chat/middleware/run-store.d.ts +283 -0
- package/dist/esm/activities/chat/middleware/run-store.js +176 -0
- package/dist/esm/activities/chat/middleware/run-store.js.map +1 -0
- package/dist/esm/activities/chat/middleware/sandbox-runtime.js +14 -8
- package/dist/esm/activities/chat/middleware/sandbox-runtime.js.map +1 -1
- package/dist/esm/activities/chat/middleware/tool-cache-middleware.js +79 -70
- package/dist/esm/activities/chat/middleware/tool-cache-middleware.js.map +1 -1
- package/dist/esm/activities/chat/middleware/types.d.ts +59 -2
- package/dist/esm/activities/chat/middleware/validate.js +23 -28
- package/dist/esm/activities/chat/middleware/validate.js.map +1 -1
- package/dist/esm/activities/chat/stream/json-parser.js +39 -25
- package/dist/esm/activities/chat/stream/json-parser.js.map +1 -1
- package/dist/esm/activities/chat/stream/message-updaters.js +275 -234
- package/dist/esm/activities/chat/stream/message-updaters.js.map +1 -1
- package/dist/esm/activities/chat/stream/processor.d.ts +24 -4
- package/dist/esm/activities/chat/stream/processor.js +1341 -1542
- package/dist/esm/activities/chat/stream/processor.js.map +1 -1
- package/dist/esm/activities/chat/stream/strategies.js +69 -53
- package/dist/esm/activities/chat/stream/strategies.js.map +1 -1
- package/dist/esm/activities/chat/tools/approval-schema.d.ts +19 -0
- package/dist/esm/activities/chat/tools/approval-schema.js +117 -0
- package/dist/esm/activities/chat/tools/approval-schema.js.map +1 -0
- package/dist/esm/activities/chat/tools/lazy-tool-manager.js +164 -191
- package/dist/esm/activities/chat/tools/lazy-tool-manager.js.map +1 -1
- package/dist/esm/activities/chat/tools/lazy-tools.js +24 -12
- package/dist/esm/activities/chat/tools/lazy-tools.js.map +1 -1
- package/dist/esm/activities/chat/tools/schema-converter.js +293 -146
- package/dist/esm/activities/chat/tools/schema-converter.js.map +1 -1
- package/dist/esm/activities/chat/tools/tool-calls.d.ts +18 -2
- package/dist/esm/activities/chat/tools/tool-calls.js +522 -531
- package/dist/esm/activities/chat/tools/tool-calls.js.map +1 -1
- package/dist/esm/activities/chat/tools/tool-definition.d.ts +75 -16
- package/dist/esm/activities/chat/tools/tool-definition.js +95 -23
- package/dist/esm/activities/chat/tools/tool-definition.js.map +1 -1
- package/dist/esm/activities/error-payload.js +85 -47
- package/dist/esm/activities/error-payload.js.map +1 -1
- package/dist/esm/activities/generateAudio/adapter.js +22 -15
- package/dist/esm/activities/generateAudio/adapter.js.map +1 -1
- package/dist/esm/activities/generateAudio/index.d.ts +4 -0
- package/dist/esm/activities/generateAudio/index.js +141 -105
- package/dist/esm/activities/generateAudio/index.js.map +1 -1
- package/dist/esm/activities/generateImage/adapter.js +22 -15
- package/dist/esm/activities/generateImage/adapter.js.map +1 -1
- package/dist/esm/activities/generateImage/index.d.ts +4 -0
- package/dist/esm/activities/generateImage/index.js +155 -111
- package/dist/esm/activities/generateImage/index.js.map +1 -1
- package/dist/esm/activities/generateSpeech/adapter.js +22 -15
- package/dist/esm/activities/generateSpeech/adapter.js.map +1 -1
- package/dist/esm/activities/generateSpeech/index.d.ts +4 -0
- package/dist/esm/activities/generateSpeech/index.js +159 -110
- package/dist/esm/activities/generateSpeech/index.js.map +1 -1
- package/dist/esm/activities/generateTranscription/adapter.js +22 -15
- package/dist/esm/activities/generateTranscription/adapter.js.map +1 -1
- package/dist/esm/activities/generateTranscription/index.d.ts +4 -0
- package/dist/esm/activities/generateTranscription/index.js +159 -100
- package/dist/esm/activities/generateTranscription/index.js.map +1 -1
- package/dist/esm/activities/generateVideo/adapter.js +36 -29
- package/dist/esm/activities/generateVideo/adapter.js.map +1 -1
- package/dist/esm/activities/generateVideo/index.d.ts +143 -19
- package/dist/esm/activities/generateVideo/index.js +456 -279
- package/dist/esm/activities/generateVideo/index.js.map +1 -1
- package/dist/esm/activities/generateVideo/snap.js +60 -48
- package/dist/esm/activities/generateVideo/snap.js.map +1 -1
- package/dist/esm/activities/index.js +8 -34
- package/dist/esm/activities/middleware/index.d.ts +1 -1
- package/dist/esm/activities/middleware/run.d.ts +10 -0
- package/dist/esm/activities/middleware/run.js +53 -29
- package/dist/esm/activities/middleware/run.js.map +1 -1
- package/dist/esm/activities/middleware/types.d.ts +44 -6
- package/dist/esm/activities/stream-generation-result.d.ts +4 -1
- package/dist/esm/activities/stream-generation-result.js +79 -44
- package/dist/esm/activities/stream-generation-result.js.map +1 -1
- package/dist/esm/activities/summarize/adapter.js +22 -15
- package/dist/esm/activities/summarize/adapter.js.map +1 -1
- package/dist/esm/activities/summarize/chat-stream-summarize.js +252 -202
- package/dist/esm/activities/summarize/chat-stream-summarize.js.map +1 -1
- package/dist/esm/activities/summarize/index.d.ts +27 -0
- package/dist/esm/activities/summarize/index.js +268 -102
- package/dist/esm/activities/summarize/index.js.map +1 -1
- package/dist/esm/adapter-internals.d.ts +2 -1
- package/dist/esm/adapter-internals.js +4 -11
- package/dist/esm/client.d.ts +25 -3
- package/dist/esm/client.js +131 -64
- package/dist/esm/client.js.map +1 -1
- package/dist/esm/custom-events.d.ts +76 -0
- package/dist/esm/custom-events.js +37 -0
- package/dist/esm/custom-events.js.map +1 -0
- package/dist/esm/delivery-detach.d.ts +50 -0
- package/dist/esm/delivery-detach.js +71 -0
- package/dist/esm/delivery-detach.js.map +1 -0
- package/dist/esm/delivery-disconnect.d.ts +62 -0
- package/dist/esm/delivery-disconnect.js +81 -0
- package/dist/esm/delivery-disconnect.js.map +1 -0
- package/dist/esm/extend-adapter.js +19 -17
- package/dist/esm/extend-adapter.js.map +1 -1
- package/dist/esm/index.d.ts +24 -6
- package/dist/esm/index.js +30 -98
- package/dist/esm/interrupt-resume.d.ts +71 -0
- package/dist/esm/interrupt-resume.js +438 -0
- package/dist/esm/interrupt-resume.js.map +1 -0
- package/dist/esm/interrupt-serialization.d.ts +12 -0
- package/dist/esm/interrupt-serialization.js +178 -0
- package/dist/esm/interrupt-serialization.js.map +1 -0
- package/dist/esm/interrupts.d.ts +84 -0
- package/dist/esm/interrupts.js +31 -0
- package/dist/esm/interrupts.js.map +1 -0
- package/dist/esm/locks.d.ts +10 -0
- package/dist/esm/locks.js +2 -0
- package/dist/esm/logger/console-logger.js +101 -78
- package/dist/esm/logger/console-logger.js.map +1 -1
- package/dist/esm/logger/internal-logger.js +104 -89
- package/dist/esm/logger/internal-logger.js.map +1 -1
- package/dist/esm/logger/resolve.js +54 -49
- package/dist/esm/logger/resolve.js.map +1 -1
- package/dist/esm/logger/types.d.ts +1 -1
- package/dist/esm/middlewares/content-guard.js +142 -148
- package/dist/esm/middlewares/content-guard.js.map +1 -1
- package/dist/esm/middlewares/index.js +2 -6
- package/dist/esm/middlewares/otel.js +598 -732
- package/dist/esm/middlewares/otel.js.map +1 -1
- package/dist/esm/middlewares/usage-attributes.js +47 -40
- package/dist/esm/middlewares/usage-attributes.js.map +1 -1
- package/dist/esm/realtime/event-emitter.js +24 -25
- package/dist/esm/realtime/event-emitter.js.map +1 -1
- package/dist/esm/realtime/index.d.ts +5 -9
- package/dist/esm/realtime/index.js +29 -6
- package/dist/esm/realtime/index.js.map +1 -1
- package/dist/esm/scope.d.ts +47 -0
- package/dist/esm/stream-durability.d.ts +171 -0
- package/dist/esm/stream-durability.js +295 -0
- package/dist/esm/stream-durability.js.map +1 -0
- package/dist/esm/stream-to-response.d.ts +178 -13
- package/dist/esm/stream-to-response.js +663 -115
- package/dist/esm/stream-to-response.js.map +1 -1
- package/dist/esm/strip-to-spec-middleware.js +30 -16
- package/dist/esm/strip-to-spec-middleware.js.map +1 -1
- package/dist/esm/system-prompts.js +27 -21
- package/dist/esm/system-prompts.js.map +1 -1
- package/dist/esm/tool-registry.js +72 -45
- package/dist/esm/tool-registry.js.map +1 -1
- package/dist/esm/tools/provider-tool.js +14 -5
- package/dist/esm/tools/provider-tool.js.map +1 -1
- package/dist/esm/types.d.ts +321 -42
- package/dist/esm/types.js +2 -0
- package/dist/esm/utilities/ag-ui-wire.js +79 -93
- package/dist/esm/utilities/ag-ui-wire.js.map +1 -1
- package/dist/esm/utilities/chat-params.d.ts +26 -4
- package/dist/esm/utilities/chat-params.js +218 -92
- package/dist/esm/utilities/chat-params.js.map +1 -1
- package/dist/esm/utilities/errors.js +28 -18
- package/dist/esm/utilities/errors.js.map +1 -1
- package/dist/esm/utilities/media-prompt.js +46 -41
- package/dist/esm/utilities/media-prompt.js.map +1 -1
- package/dist/esm/utilities/numbers.js +13 -10
- package/dist/esm/utilities/numbers.js.map +1 -1
- package/dist/esm/utilities/provider-executed.js +20 -11
- package/dist/esm/utilities/provider-executed.js.map +1 -1
- package/dist/esm/utilities/sampling-keys.js +31 -19
- package/dist/esm/utilities/sampling-keys.js.map +1 -1
- package/dist/esm/utilities/tool-result.js +42 -30
- package/dist/esm/utilities/tool-result.js.map +1 -1
- package/dist/esm/utilities/usage.js +27 -9
- package/dist/esm/utilities/usage.js.map +1 -1
- package/dist/esm/utils.js +26 -18
- package/dist/esm/utils.js.map +1 -1
- package/package.json +10 -6
- package/skills/ai-core/SKILL.md +69 -18
- package/skills/ai-core/adapter-configuration/SKILL.md +44 -21
- package/skills/ai-core/adapter-configuration/references/anthropic-adapter.md +1 -3
- package/skills/ai-core/adapter-configuration/references/byteplus-adapter.md +148 -0
- package/skills/ai-core/adapter-configuration/references/gemini-adapter.md +2 -6
- package/skills/ai-core/adapter-configuration/references/groq-adapter.md +2 -6
- package/skills/ai-core/adapter-configuration/references/openai-adapter.md +1 -3
- package/skills/ai-core/ag-ui-protocol/SKILL.md +1 -1
- package/skills/ai-core/chat-experience/SKILL.md +98 -11
- package/skills/ai-core/client-persistence/SKILL.md +277 -0
- package/skills/ai-core/custom-backend-integration/SKILL.md +1 -1
- package/skills/ai-core/debug-logging/SKILL.md +1 -1
- package/skills/ai-core/locks/SKILL.md +143 -0
- package/skills/ai-core/media-generation/SKILL.md +144 -12
- package/skills/ai-core/middleware/SKILL.md +258 -33
- package/skills/ai-core/structured-outputs/SKILL.md +1 -1
- package/skills/ai-core/tool-calling/SKILL.md +54 -61
- package/src/activities/chat/agent-loop-strategies.ts +5 -39
- package/src/activities/chat/cancel.ts +81 -0
- package/src/activities/chat/index.ts +1091 -200
- package/src/activities/chat/mcp/manager.ts +4 -4
- package/src/activities/chat/mcp/types.ts +2 -2
- package/src/activities/chat/messages.ts +5 -3
- package/src/activities/chat/middleware/builder.ts +1 -1
- package/src/activities/chat/middleware/compose.ts +186 -9
- package/src/activities/chat/middleware/index.ts +26 -0
- package/src/activities/chat/middleware/locks.ts +102 -0
- package/src/activities/chat/middleware/pending-turn.ts +47 -0
- package/src/activities/chat/middleware/run-disconnect.ts +62 -0
- package/src/activities/chat/middleware/run-store.ts +412 -0
- package/src/activities/chat/middleware/types.ts +62 -1
- package/src/activities/chat/stream/processor.ts +189 -5
- package/src/activities/chat/tools/approval-schema.ts +205 -0
- package/src/activities/chat/tools/tool-calls.ts +106 -13
- package/src/activities/chat/tools/tool-definition.ts +210 -39
- package/src/activities/generateAudio/index.ts +20 -3
- package/src/activities/generateImage/index.ts +20 -3
- package/src/activities/generateSpeech/index.ts +25 -3
- package/src/activities/generateTranscription/index.ts +26 -3
- package/src/activities/generateVideo/index.ts +345 -82
- package/src/activities/middleware/index.ts +2 -0
- package/src/activities/middleware/run.ts +31 -0
- package/src/activities/middleware/types.ts +49 -5
- package/src/activities/stream-generation-result.ts +30 -2
- package/src/activities/summarize/chat-stream-summarize.ts +5 -0
- package/src/activities/summarize/index.ts +200 -10
- package/src/adapter-internals.ts +10 -1
- package/src/client.ts +244 -0
- package/src/custom-events.ts +107 -0
- package/src/delivery-detach.ts +72 -0
- package/src/delivery-disconnect.ts +84 -0
- package/src/index.ts +138 -1
- package/src/interrupt-resume.ts +824 -0
- package/src/interrupt-serialization.ts +183 -0
- package/src/interrupts.ts +146 -0
- package/src/locks.ts +17 -0
- package/src/logger/types.ts +1 -1
- package/src/middlewares/otel.ts +1 -0
- package/src/realtime/index.ts +5 -9
- package/src/scope.ts +47 -0
- package/src/stream-durability.ts +598 -0
- package/src/stream-to-response.ts +1051 -95
- package/src/strip-to-spec-middleware.ts +3 -3
- package/src/types.ts +405 -45
- package/src/utilities/chat-params.ts +245 -55
- package/dist/esm/activities/index.js.map +0 -1
- package/dist/esm/adapter-internals.js.map +0 -1
- package/dist/esm/index.js.map +0 -1
- package/dist/esm/middlewares/index.js.map +0 -1
|
@@ -1,76 +1,85 @@
|
|
|
1
|
+
//#region src/activities/chat/middleware/tool-cache-middleware.ts
|
|
1
2
|
function defaultKeyFn(toolName, args) {
|
|
2
|
-
|
|
3
|
+
return JSON.stringify([toolName, args]);
|
|
3
4
|
}
|
|
4
5
|
function createDefaultStorage(maxSize) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
cache.delete(key);
|
|
28
|
-
}
|
|
29
|
-
};
|
|
6
|
+
const cache = /* @__PURE__ */ new Map();
|
|
7
|
+
return {
|
|
8
|
+
getItem: (key) => {
|
|
9
|
+
const entry = cache.get(key);
|
|
10
|
+
if (entry !== void 0) {
|
|
11
|
+
cache.delete(key);
|
|
12
|
+
cache.set(key, entry);
|
|
13
|
+
}
|
|
14
|
+
return entry;
|
|
15
|
+
},
|
|
16
|
+
setItem: (key, value) => {
|
|
17
|
+
if (cache.has(key)) cache.delete(key);
|
|
18
|
+
else if (cache.size >= maxSize) {
|
|
19
|
+
const firstKey = cache.keys().next().value;
|
|
20
|
+
if (firstKey !== void 0) cache.delete(firstKey);
|
|
21
|
+
}
|
|
22
|
+
cache.set(key, value);
|
|
23
|
+
},
|
|
24
|
+
deleteItem: (key) => {
|
|
25
|
+
cache.delete(key);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
30
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* Creates a middleware that caches tool call results based on tool name + arguments.
|
|
31
|
+
*
|
|
32
|
+
* When a tool is called with the same name and arguments as a previous call,
|
|
33
|
+
* the cached result is returned immediately without executing the tool.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```ts
|
|
37
|
+
* import { chat, toolCacheMiddleware } from '@tanstack/ai'
|
|
38
|
+
*
|
|
39
|
+
* const stream = chat({
|
|
40
|
+
* adapter,
|
|
41
|
+
* messages,
|
|
42
|
+
* tools: [weatherTool, stockTool],
|
|
43
|
+
* middleware: [
|
|
44
|
+
* toolCacheMiddleware({ ttl: 60_000, toolNames: ['getWeather'] }),
|
|
45
|
+
* ],
|
|
46
|
+
* })
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
31
49
|
function toolCacheMiddleware(options = {}) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
const key = keyFn(info.toolName, parsedArgs);
|
|
66
|
-
await storage.setItem(key, {
|
|
67
|
-
result: info.result,
|
|
68
|
-
timestamp: Date.now()
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
};
|
|
50
|
+
const { maxSize = 100, ttl = Infinity, toolNames, keyFn = defaultKeyFn, storage = createDefaultStorage(maxSize) } = options;
|
|
51
|
+
return {
|
|
52
|
+
name: "tool-cache-middleware",
|
|
53
|
+
onBeforeToolCall: async (_ctx, hookCtx) => {
|
|
54
|
+
if (toolNames && !toolNames.includes(hookCtx.toolName)) return;
|
|
55
|
+
const key = keyFn(hookCtx.toolName, hookCtx.args);
|
|
56
|
+
const entry = await storage.getItem(key);
|
|
57
|
+
if (entry) {
|
|
58
|
+
if (Date.now() - entry.timestamp < ttl) return {
|
|
59
|
+
type: "skip",
|
|
60
|
+
result: entry.result
|
|
61
|
+
};
|
|
62
|
+
await storage.deleteItem(key);
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
onAfterToolCall: async (_ctx, info) => {
|
|
66
|
+
if (!info.ok) return;
|
|
67
|
+
if (toolNames && !toolNames.includes(info.toolName)) return;
|
|
68
|
+
let parsedArgs;
|
|
69
|
+
try {
|
|
70
|
+
parsedArgs = JSON.parse(info.toolCall.function.arguments.trim() || "{}");
|
|
71
|
+
} catch {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const key = keyFn(info.toolName, parsedArgs);
|
|
75
|
+
await storage.setItem(key, {
|
|
76
|
+
result: info.result,
|
|
77
|
+
timestamp: Date.now()
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
};
|
|
72
81
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
//# sourceMappingURL=tool-cache-middleware.js.map
|
|
82
|
+
//#endregion
|
|
83
|
+
export { toolCacheMiddleware };
|
|
84
|
+
|
|
85
|
+
//# sourceMappingURL=tool-cache-middleware.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-cache-middleware.js","sources":["../../../../../src/activities/chat/middleware/tool-cache-middleware.ts"],"sourcesContent":["import type { ChatMiddleware } from './types'\n\n/**\n * A cache entry stored by the tool cache middleware.\n */\nexport interface ToolCacheEntry {\n result: unknown\n timestamp: number\n}\n\n/**\n * Custom storage backend for the tool cache middleware.\n *\n * When provided, the middleware delegates all cache operations to this storage\n * instead of using the built-in in-memory Map. This enables external storage\n * backends like Redis, localStorage, databases, etc.\n *\n * All methods may return a Promise for async storage backends.\n */\nexport interface ToolCacheStorage {\n getItem: (\n key: string,\n ) => ToolCacheEntry | undefined | Promise<ToolCacheEntry | undefined>\n setItem: (key: string, value: ToolCacheEntry) => void | Promise<void>\n deleteItem: (key: string) => void | Promise<void>\n}\n\n/**\n * Options for the tool cache middleware.\n */\nexport interface ToolCacheMiddlewareOptions {\n /**\n * Maximum number of entries in the cache.\n * When exceeded, the oldest entry is evicted (LRU).\n *\n * Only applies to the default in-memory storage.\n * When a custom `storage` is provided, capacity management is the storage's responsibility.\n *\n * @default 100\n */\n maxSize?: number\n\n /**\n * Time-to-live in milliseconds. Entries older than this are not served from cache.\n * @default Infinity (no expiry)\n */\n ttl?: number\n\n /**\n * Tool names to cache. If not provided, all tools are cached.\n */\n toolNames?: Array<string>\n\n /**\n * Custom function to generate a cache key from tool name and args.\n * Defaults to `JSON.stringify([toolName, args])`.\n */\n keyFn?: (toolName: string, args: unknown) => string\n\n /**\n * Custom storage backend. When provided, the middleware uses this instead of\n * the built-in in-memory Map. The storage is responsible for its own capacity\n * management — the `maxSize` option is ignored.\n *\n * @example\n * ```ts\n * toolCacheMiddleware({\n * storage: {\n * getItem: (key) => redisClient.get(key).then(v => v ? JSON.parse(v) : undefined),\n * setItem: (key, value) => redisClient.set(key, JSON.stringify(value)),\n * deleteItem: (key) => redisClient.del(key),\n * },\n * })\n * ```\n */\n storage?: ToolCacheStorage\n}\n\nfunction defaultKeyFn(toolName: string, args: unknown): string {\n return JSON.stringify([toolName, args])\n}\n\nfunction createDefaultStorage(maxSize: number): ToolCacheStorage {\n const cache = new Map<string, ToolCacheEntry>()\n\n return {\n getItem: (key) => {\n const entry = cache.get(key)\n if (entry !== undefined) {\n // Refresh recency: delete and re-insert so this key becomes newest\n cache.delete(key)\n cache.set(key, entry)\n }\n return entry\n },\n setItem: (key, value) => {\n // Delete first so re-inserts also refresh recency\n if (cache.has(key)) {\n cache.delete(key)\n } else if (cache.size >= maxSize) {\n // LRU eviction: Map iteration order is insertion order — first key is least recently used\n const firstKey = cache.keys().next().value\n if (firstKey !== undefined) {\n cache.delete(firstKey)\n }\n }\n cache.set(key, value)\n },\n deleteItem: (key) => {\n cache.delete(key)\n },\n }\n}\n\n/**\n * Creates a middleware that caches tool call results based on tool name + arguments.\n *\n * When a tool is called with the same name and arguments as a previous call,\n * the cached result is returned immediately without executing the tool.\n *\n * @example\n * ```ts\n * import { chat, toolCacheMiddleware } from '@tanstack/ai'\n *\n * const stream = chat({\n * adapter,\n * messages,\n * tools: [weatherTool, stockTool],\n * middleware: [\n * toolCacheMiddleware({ ttl: 60_000, toolNames: ['getWeather'] }),\n * ],\n * })\n * ```\n */\nexport function toolCacheMiddleware(\n options: ToolCacheMiddlewareOptions = {},\n): ChatMiddleware {\n const {\n maxSize = 100,\n ttl = Infinity,\n toolNames,\n keyFn = defaultKeyFn,\n storage = createDefaultStorage(maxSize),\n } = options\n\n return {\n name: 'tool-cache-middleware',\n\n onBeforeToolCall: async (_ctx, hookCtx) => {\n if (toolNames && !toolNames.includes(hookCtx.toolName)) {\n return undefined\n }\n\n const key = keyFn(hookCtx.toolName, hookCtx.args)\n const entry = await storage.getItem(key)\n\n if (entry) {\n const age = Date.now() - entry.timestamp\n if (age < ttl) {\n return { type: 'skip', result: entry.result }\n }\n // Expired — remove\n await storage.deleteItem(key)\n }\n\n return undefined\n },\n\n onAfterToolCall: async (_ctx, info) => {\n if (!info.ok) return\n if (toolNames && !toolNames.includes(info.toolName)) return\n\n // Re-derive the key from the raw arguments to match what onBeforeToolCall produces\n let parsedArgs: unknown\n try {\n parsedArgs = JSON.parse(info.toolCall.function.arguments.trim() || '{}')\n } catch {\n return\n }\n\n const key = keyFn(info.toolName, parsedArgs)\n\n await storage.setItem(key, {\n result: info.result,\n timestamp: Date.now(),\n })\n },\n }\n}\n"],"
|
|
1
|
+
{"version":3,"file":"tool-cache-middleware.js","names":[],"sources":["../../../../../src/activities/chat/middleware/tool-cache-middleware.ts"],"sourcesContent":["import type { ChatMiddleware } from './types'\n\n/**\n * A cache entry stored by the tool cache middleware.\n */\nexport interface ToolCacheEntry {\n result: unknown\n timestamp: number\n}\n\n/**\n * Custom storage backend for the tool cache middleware.\n *\n * When provided, the middleware delegates all cache operations to this storage\n * instead of using the built-in in-memory Map. This enables external storage\n * backends like Redis, localStorage, databases, etc.\n *\n * All methods may return a Promise for async storage backends.\n */\nexport interface ToolCacheStorage {\n getItem: (\n key: string,\n ) => ToolCacheEntry | undefined | Promise<ToolCacheEntry | undefined>\n setItem: (key: string, value: ToolCacheEntry) => void | Promise<void>\n deleteItem: (key: string) => void | Promise<void>\n}\n\n/**\n * Options for the tool cache middleware.\n */\nexport interface ToolCacheMiddlewareOptions {\n /**\n * Maximum number of entries in the cache.\n * When exceeded, the oldest entry is evicted (LRU).\n *\n * Only applies to the default in-memory storage.\n * When a custom `storage` is provided, capacity management is the storage's responsibility.\n *\n * @default 100\n */\n maxSize?: number\n\n /**\n * Time-to-live in milliseconds. Entries older than this are not served from cache.\n * @default Infinity (no expiry)\n */\n ttl?: number\n\n /**\n * Tool names to cache. If not provided, all tools are cached.\n */\n toolNames?: Array<string>\n\n /**\n * Custom function to generate a cache key from tool name and args.\n * Defaults to `JSON.stringify([toolName, args])`.\n */\n keyFn?: (toolName: string, args: unknown) => string\n\n /**\n * Custom storage backend. When provided, the middleware uses this instead of\n * the built-in in-memory Map. The storage is responsible for its own capacity\n * management — the `maxSize` option is ignored.\n *\n * @example\n * ```ts\n * toolCacheMiddleware({\n * storage: {\n * getItem: (key) => redisClient.get(key).then(v => v ? JSON.parse(v) : undefined),\n * setItem: (key, value) => redisClient.set(key, JSON.stringify(value)),\n * deleteItem: (key) => redisClient.del(key),\n * },\n * })\n * ```\n */\n storage?: ToolCacheStorage\n}\n\nfunction defaultKeyFn(toolName: string, args: unknown): string {\n return JSON.stringify([toolName, args])\n}\n\nfunction createDefaultStorage(maxSize: number): ToolCacheStorage {\n const cache = new Map<string, ToolCacheEntry>()\n\n return {\n getItem: (key) => {\n const entry = cache.get(key)\n if (entry !== undefined) {\n // Refresh recency: delete and re-insert so this key becomes newest\n cache.delete(key)\n cache.set(key, entry)\n }\n return entry\n },\n setItem: (key, value) => {\n // Delete first so re-inserts also refresh recency\n if (cache.has(key)) {\n cache.delete(key)\n } else if (cache.size >= maxSize) {\n // LRU eviction: Map iteration order is insertion order — first key is least recently used\n const firstKey = cache.keys().next().value\n if (firstKey !== undefined) {\n cache.delete(firstKey)\n }\n }\n cache.set(key, value)\n },\n deleteItem: (key) => {\n cache.delete(key)\n },\n }\n}\n\n/**\n * Creates a middleware that caches tool call results based on tool name + arguments.\n *\n * When a tool is called with the same name and arguments as a previous call,\n * the cached result is returned immediately without executing the tool.\n *\n * @example\n * ```ts\n * import { chat, toolCacheMiddleware } from '@tanstack/ai'\n *\n * const stream = chat({\n * adapter,\n * messages,\n * tools: [weatherTool, stockTool],\n * middleware: [\n * toolCacheMiddleware({ ttl: 60_000, toolNames: ['getWeather'] }),\n * ],\n * })\n * ```\n */\nexport function toolCacheMiddleware(\n options: ToolCacheMiddlewareOptions = {},\n): ChatMiddleware {\n const {\n maxSize = 100,\n ttl = Infinity,\n toolNames,\n keyFn = defaultKeyFn,\n storage = createDefaultStorage(maxSize),\n } = options\n\n return {\n name: 'tool-cache-middleware',\n\n onBeforeToolCall: async (_ctx, hookCtx) => {\n if (toolNames && !toolNames.includes(hookCtx.toolName)) {\n return undefined\n }\n\n const key = keyFn(hookCtx.toolName, hookCtx.args)\n const entry = await storage.getItem(key)\n\n if (entry) {\n const age = Date.now() - entry.timestamp\n if (age < ttl) {\n return { type: 'skip', result: entry.result }\n }\n // Expired — remove\n await storage.deleteItem(key)\n }\n\n return undefined\n },\n\n onAfterToolCall: async (_ctx, info) => {\n if (!info.ok) return\n if (toolNames && !toolNames.includes(info.toolName)) return\n\n // Re-derive the key from the raw arguments to match what onBeforeToolCall produces\n let parsedArgs: unknown\n try {\n parsedArgs = JSON.parse(info.toolCall.function.arguments.trim() || '{}')\n } catch {\n return\n }\n\n const key = keyFn(info.toolName, parsedArgs)\n\n await storage.setItem(key, {\n result: info.result,\n timestamp: Date.now(),\n })\n },\n }\n}\n"],"mappings":";AA8EA,SAAS,aAAa,UAAkB,MAAuB;CAC7D,OAAO,KAAK,UAAU,CAAC,UAAU,IAAI,CAAC;AACxC;AAEA,SAAS,qBAAqB,SAAmC;CAC/D,MAAM,wBAAQ,IAAI,IAA4B;CAE9C,OAAO;EACL,UAAU,QAAQ;GAChB,MAAM,QAAQ,MAAM,IAAI,GAAG;GAC3B,IAAI,UAAU,KAAA,GAAW;IAEvB,MAAM,OAAO,GAAG;IAChB,MAAM,IAAI,KAAK,KAAK;GACtB;GACA,OAAO;EACT;EACA,UAAU,KAAK,UAAU;GAEvB,IAAI,MAAM,IAAI,GAAG,GACf,MAAM,OAAO,GAAG;QACX,IAAI,MAAM,QAAQ,SAAS;IAEhC,MAAM,WAAW,MAAM,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC;IACrC,IAAI,aAAa,KAAA,GACf,MAAM,OAAO,QAAQ;GAEzB;GACA,MAAM,IAAI,KAAK,KAAK;EACtB;EACA,aAAa,QAAQ;GACnB,MAAM,OAAO,GAAG;EAClB;CACF;AACF;;;;;;;;;;;;;;;;;;;;;AAsBA,SAAgB,oBACd,UAAsC,CAAC,GACvB;CAChB,MAAM,EACJ,UAAU,KACV,MAAM,UACN,WACA,QAAQ,cACR,UAAU,qBAAqB,OAAO,MACpC;CAEJ,OAAO;EACL,MAAM;EAEN,kBAAkB,OAAO,MAAM,YAAY;GACzC,IAAI,aAAa,CAAC,UAAU,SAAS,QAAQ,QAAQ,GACnD;GAGF,MAAM,MAAM,MAAM,QAAQ,UAAU,QAAQ,IAAI;GAChD,MAAM,QAAQ,MAAM,QAAQ,QAAQ,GAAG;GAEvC,IAAI,OAAO;IAET,IADY,KAAK,IAAI,IAAI,MAAM,YACrB,KACR,OAAO;KAAE,MAAM;KAAQ,QAAQ,MAAM;IAAO;IAG9C,MAAM,QAAQ,WAAW,GAAG;GAC9B;EAGF;EAEA,iBAAiB,OAAO,MAAM,SAAS;GACrC,IAAI,CAAC,KAAK,IAAI;GACd,IAAI,aAAa,CAAC,UAAU,SAAS,KAAK,QAAQ,GAAG;GAGrD,IAAI;GACJ,IAAI;IACF,aAAa,KAAK,MAAM,KAAK,SAAS,SAAS,UAAU,KAAK,KAAK,IAAI;GACzE,QAAQ;IACN;GACF;GAEA,MAAM,MAAM,MAAM,KAAK,UAAU,UAAU;GAE3C,MAAM,QAAQ,QAAQ,KAAK;IACzB,QAAQ,KAAK;IACb,WAAW,KAAK,IAAI;GACtB,CAAC;EACH;CACF;AACF"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { JSONSchema, ModelMessage, StreamChunk, TokenUsage, Tool, ToolCall } from '../../../types.js';
|
|
1
|
+
import { AgentLoopState, JSONSchema, ModelMessage, RunAgentResumeItem, StreamChunk, TokenUsage, Tool, ToolCall } from '../../../types.js';
|
|
2
2
|
import { SystemPrompt } from '../../../system-prompts.js';
|
|
3
|
+
import { ToolApprovalResolution } from '../../../interrupts.js';
|
|
3
4
|
import { Capability, CapabilityHandle, CapabilityRegistry } from './capabilities.js';
|
|
4
5
|
/** A file change observed inside a sandbox during a chat run. */
|
|
5
6
|
export interface SandboxFileEvent {
|
|
@@ -52,6 +53,8 @@ export interface ChatMiddlewareContext<TContext = unknown> {
|
|
|
52
53
|
streamId: string;
|
|
53
54
|
/** AG-UI run identifier for correlating client and server events */
|
|
54
55
|
runId: string;
|
|
56
|
+
/** Interrupted or parent run correlated with this continuation. */
|
|
57
|
+
parentRunId?: string;
|
|
55
58
|
/**
|
|
56
59
|
* AG-UI thread identifier — a stable per-conversation ID used to
|
|
57
60
|
* correlate client and server devtools events. Resolves to the
|
|
@@ -92,7 +95,7 @@ export interface ChatMiddlewareContext<TContext = unknown> {
|
|
|
92
95
|
activity: 'chat';
|
|
93
96
|
/** Provider name (e.g., 'openai', 'anthropic') */
|
|
94
97
|
provider: string;
|
|
95
|
-
/** Model identifier (e.g., 'gpt-
|
|
98
|
+
/** Model identifier (e.g., 'gpt-5.5') */
|
|
96
99
|
model: string;
|
|
97
100
|
/** Source of the chat invocation — always 'server' for server-side chat */
|
|
98
101
|
source: 'client' | 'server';
|
|
@@ -150,9 +153,32 @@ export interface ChatMiddlewareConfig {
|
|
|
150
153
|
messages: Array<ModelMessage>;
|
|
151
154
|
systemPrompts: Array<SystemPrompt>;
|
|
152
155
|
tools: Array<Tool>;
|
|
156
|
+
resume?: Array<RunAgentResumeItem> | undefined;
|
|
157
|
+
resumeToolState?: ChatResumeToolState | undefined;
|
|
153
158
|
metadata?: Record<string, unknown> | undefined;
|
|
154
159
|
modelOptions?: Record<string, unknown> | undefined;
|
|
155
160
|
}
|
|
161
|
+
/**
|
|
162
|
+
* Tool decisions reconstructed by server-side middleware from validated resume
|
|
163
|
+
* entries. This lets empty-message interrupt resumes continue tool execution
|
|
164
|
+
* without relying on client message history.
|
|
165
|
+
*/
|
|
166
|
+
export interface ChatResumeToolState {
|
|
167
|
+
approvals?: ReadonlyMap<string, ToolApprovalResolution> | undefined;
|
|
168
|
+
clientToolResults?: ReadonlyMap<string, unknown> | undefined;
|
|
169
|
+
genericInterrupts?: ReadonlyMap<string, ChatResumeGenericResolution> | undefined;
|
|
170
|
+
deniedToolResults?: ReadonlyMap<string, unknown> | undefined;
|
|
171
|
+
cancelledToolCallIds?: ReadonlySet<string> | undefined;
|
|
172
|
+
}
|
|
173
|
+
export type ChatResumeGenericResolution = {
|
|
174
|
+
interruptId: string;
|
|
175
|
+
status: 'resolved';
|
|
176
|
+
payload: unknown;
|
|
177
|
+
} | {
|
|
178
|
+
interruptId: string;
|
|
179
|
+
status: 'cancelled';
|
|
180
|
+
payload?: never;
|
|
181
|
+
};
|
|
156
182
|
/**
|
|
157
183
|
* Config passed to onStructuredOutputConfig.
|
|
158
184
|
*
|
|
@@ -287,6 +313,22 @@ export interface AbortInfo {
|
|
|
287
313
|
reason?: string;
|
|
288
314
|
/** Duration until abort in milliseconds */
|
|
289
315
|
duration: number;
|
|
316
|
+
/**
|
|
317
|
+
* True only when the abort came from an explicit, out-of-band cancel (e.g. a
|
|
318
|
+
* cancel endpoint setting `RunRecord.cancelRequested`), never from a mere
|
|
319
|
+
* client disconnect.
|
|
320
|
+
*
|
|
321
|
+
* A disconnect and a user pressing "stop" are the SAME connection close on
|
|
322
|
+
* the wire, so consumers must not infer intent from an abort alone. Middleware
|
|
323
|
+
* that tears down expensive resources reads this to distinguish "the viewer
|
|
324
|
+
* left, keep going" from "the user wants this stopped". Populated from the
|
|
325
|
+
* abort reason: `true` exactly when the run was aborted with `RUN_CANCEL_REASON`
|
|
326
|
+
* (matched with `===`, so an arbitrary error message can never be read as a
|
|
327
|
+
* deliberate cancel), `false` for every other abort. The durable channel is
|
|
328
|
+
* separate — middleware that must also catch a cancel recorded on a different
|
|
329
|
+
* host reads `RunRecord.cancelRequested` in addition to this flag.
|
|
330
|
+
*/
|
|
331
|
+
cancelRequested?: boolean;
|
|
290
332
|
}
|
|
291
333
|
/**
|
|
292
334
|
* Information passed to onError.
|
|
@@ -388,6 +430,21 @@ export interface ChatMiddleware<TContext = unknown> {
|
|
|
388
430
|
* is created. Use this to observe iteration boundaries.
|
|
389
431
|
*/
|
|
390
432
|
onIteration?: (ctx: ChatMiddlewareContext<TContext>, info: IterationInfo) => void | Promise<void>;
|
|
433
|
+
/**
|
|
434
|
+
* Called when the engine is deciding whether to start another agent-loop
|
|
435
|
+
* iteration (after a tool phase or between model turns).
|
|
436
|
+
*
|
|
437
|
+
* Return `false` to stop further iterations. Return `true`, `void`, or
|
|
438
|
+
* `undefined` to allow continuation. Combined with AND semantics across
|
|
439
|
+
* middleware and with `agentLoopStrategy` — any `false` stops the loop.
|
|
440
|
+
*
|
|
441
|
+
* Does not abort the run: the stream finishes normally with the current
|
|
442
|
+
* messages. Use `ctx.abort()` only when you need a hard abort.
|
|
443
|
+
*
|
|
444
|
+
* Receives the same {@link AgentLoopState} passed to strategies
|
|
445
|
+
* (`iterationCount`, `toolCallCount`, `lastTurnToolCallCount`, etc.).
|
|
446
|
+
*/
|
|
447
|
+
onShouldContinue?: (ctx: ChatMiddlewareContext<TContext>, state: AgentLoopState) => boolean | void | Promise<boolean | void>;
|
|
391
448
|
/**
|
|
392
449
|
* Called for every chunk yielded by chat().
|
|
393
450
|
* Can observe, transform, expand, or drop chunks.
|
|
@@ -1,30 +1,25 @@
|
|
|
1
|
+
//#region src/activities/chat/middleware/validate.ts
|
|
2
|
+
/**
|
|
3
|
+
* Runtime validation: every required capability (from middleware `requires` and
|
|
4
|
+
* the adapter's `requires`) must be provided by some middleware's `provides`.
|
|
5
|
+
* `optionalRequires` is never gating. Throws a clear error otherwise.
|
|
6
|
+
*
|
|
7
|
+
* Presence only — ORDER is not validated here (a provider may appear after its
|
|
8
|
+
* consumer in the array and still pass). Use the `createChatMiddleware()`
|
|
9
|
+
* builder for compile-time order enforcement; at runtime, a consumer that reads
|
|
10
|
+
* a not-yet-provided capability during `setup` fails loud via its getter.
|
|
11
|
+
*/
|
|
1
12
|
function validateCapabilities(middlewares, adapter) {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
for (const handle of adapter.requires ?? []) {
|
|
11
|
-
if (!provided.has(handle)) {
|
|
12
|
-
throw new Error(
|
|
13
|
-
`Adapter "${adapter.name}" requires capability "${handle.capabilityName}". Provided capabilities: ${providedNames()}. Add a middleware that provides "${handle.capabilityName}".`
|
|
14
|
-
);
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
for (const mw of middlewares) {
|
|
18
|
-
for (const handle of mw.requires ?? []) {
|
|
19
|
-
if (!provided.has(handle)) {
|
|
20
|
-
throw new Error(
|
|
21
|
-
`Middleware "${mw.name ?? "unnamed"}" requires capability "${handle.capabilityName}". Provided capabilities: ${providedNames()}. Add a middleware that provides "${handle.capabilityName}".`
|
|
22
|
-
);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
}
|
|
13
|
+
const provided = /* @__PURE__ */ new Set();
|
|
14
|
+
for (const mw of middlewares) for (const handle of mw.provides ?? []) provided.add(handle);
|
|
15
|
+
const providedNames = () => {
|
|
16
|
+
const names = [...provided].map((h) => h.capabilityName);
|
|
17
|
+
return names.length ? names.join(", ") : "none";
|
|
18
|
+
};
|
|
19
|
+
for (const handle of adapter.requires ?? []) if (!provided.has(handle)) throw new Error(`Adapter "${adapter.name}" requires capability "${handle.capabilityName}". Provided capabilities: ${providedNames()}. Add a middleware that provides "${handle.capabilityName}".`);
|
|
20
|
+
for (const mw of middlewares) for (const handle of mw.requires ?? []) if (!provided.has(handle)) throw new Error(`Middleware "${mw.name ?? "unnamed"}" requires capability "${handle.capabilityName}". Provided capabilities: ${providedNames()}. Add a middleware that provides "${handle.capabilityName}".`);
|
|
26
21
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
//# sourceMappingURL=validate.js.map
|
|
22
|
+
//#endregion
|
|
23
|
+
export { validateCapabilities };
|
|
24
|
+
|
|
25
|
+
//# sourceMappingURL=validate.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate.js","sources":["../../../../../src/activities/chat/middleware/validate.ts"],"sourcesContent":["import type { CapabilityHandle } from './capabilities'\nimport type { AnyChatMiddleware } from './types'\n\n/** Minimal adapter shape needed for capability validation. */\ninterface CapabilityRequiringAdapter {\n name: string\n requires?: ReadonlyArray<CapabilityHandle>\n}\n\n/**\n * Runtime validation: every required capability (from middleware `requires` and\n * the adapter's `requires`) must be provided by some middleware's `provides`.\n * `optionalRequires` is never gating. Throws a clear error otherwise.\n *\n * Presence only — ORDER is not validated here (a provider may appear after its\n * consumer in the array and still pass). Use the `createChatMiddleware()`\n * builder for compile-time order enforcement; at runtime, a consumer that reads\n * a not-yet-provided capability during `setup` fails loud via its getter.\n */\nexport function validateCapabilities(\n middlewares: ReadonlyArray<AnyChatMiddleware>,\n adapter: CapabilityRequiringAdapter,\n): void {\n const provided = new Set<CapabilityHandle>()\n for (const mw of middlewares) {\n for (const handle of mw.provides ?? []) provided.add(handle)\n }\n\n const providedNames = (): string => {\n const names = [...provided].map((h) => h.capabilityName)\n return names.length ? names.join(', ') : 'none'\n }\n\n for (const handle of adapter.requires ?? []) {\n if (!provided.has(handle)) {\n throw new Error(\n `Adapter \"${adapter.name}\" requires capability \"${handle.capabilityName}\". ` +\n `Provided capabilities: ${providedNames()}. ` +\n `Add a middleware that provides \"${handle.capabilityName}\".`,\n )\n }\n }\n\n for (const mw of middlewares) {\n for (const handle of mw.requires ?? []) {\n if (!provided.has(handle)) {\n throw new Error(\n `Middleware \"${mw.name ?? 'unnamed'}\" requires capability ` +\n `\"${handle.capabilityName}\". Provided capabilities: ${providedNames()}. ` +\n `Add a middleware that provides \"${handle.capabilityName}\".`,\n )\n }\n }\n }\n}\n"],"
|
|
1
|
+
{"version":3,"file":"validate.js","names":[],"sources":["../../../../../src/activities/chat/middleware/validate.ts"],"sourcesContent":["import type { CapabilityHandle } from './capabilities'\nimport type { AnyChatMiddleware } from './types'\n\n/** Minimal adapter shape needed for capability validation. */\ninterface CapabilityRequiringAdapter {\n name: string\n requires?: ReadonlyArray<CapabilityHandle>\n}\n\n/**\n * Runtime validation: every required capability (from middleware `requires` and\n * the adapter's `requires`) must be provided by some middleware's `provides`.\n * `optionalRequires` is never gating. Throws a clear error otherwise.\n *\n * Presence only — ORDER is not validated here (a provider may appear after its\n * consumer in the array and still pass). Use the `createChatMiddleware()`\n * builder for compile-time order enforcement; at runtime, a consumer that reads\n * a not-yet-provided capability during `setup` fails loud via its getter.\n */\nexport function validateCapabilities(\n middlewares: ReadonlyArray<AnyChatMiddleware>,\n adapter: CapabilityRequiringAdapter,\n): void {\n const provided = new Set<CapabilityHandle>()\n for (const mw of middlewares) {\n for (const handle of mw.provides ?? []) provided.add(handle)\n }\n\n const providedNames = (): string => {\n const names = [...provided].map((h) => h.capabilityName)\n return names.length ? names.join(', ') : 'none'\n }\n\n for (const handle of adapter.requires ?? []) {\n if (!provided.has(handle)) {\n throw new Error(\n `Adapter \"${adapter.name}\" requires capability \"${handle.capabilityName}\". ` +\n `Provided capabilities: ${providedNames()}. ` +\n `Add a middleware that provides \"${handle.capabilityName}\".`,\n )\n }\n }\n\n for (const mw of middlewares) {\n for (const handle of mw.requires ?? []) {\n if (!provided.has(handle)) {\n throw new Error(\n `Middleware \"${mw.name ?? 'unnamed'}\" requires capability ` +\n `\"${handle.capabilityName}\". Provided capabilities: ${providedNames()}. ` +\n `Add a middleware that provides \"${handle.capabilityName}\".`,\n )\n }\n }\n }\n}\n"],"mappings":";;;;;;;;;;;AAmBA,SAAgB,qBACd,aACA,SACM;CACN,MAAM,2BAAW,IAAI,IAAsB;CAC3C,KAAK,MAAM,MAAM,aACf,KAAK,MAAM,UAAU,GAAG,YAAY,CAAC,GAAG,SAAS,IAAI,MAAM;CAG7D,MAAM,sBAA8B;EAClC,MAAM,QAAQ,CAAC,GAAG,QAAQ,CAAC,CAAC,KAAK,MAAM,EAAE,cAAc;EACvD,OAAO,MAAM,SAAS,MAAM,KAAK,IAAI,IAAI;CAC3C;CAEA,KAAK,MAAM,UAAU,QAAQ,YAAY,CAAC,GACxC,IAAI,CAAC,SAAS,IAAI,MAAM,GACtB,MAAM,IAAI,MACR,YAAY,QAAQ,KAAK,yBAAyB,OAAO,eAAe,4BAC5C,cAAc,EAAE,oCACP,OAAO,eAAe,GAC7D;CAIJ,KAAK,MAAM,MAAM,aACf,KAAK,MAAM,UAAU,GAAG,YAAY,CAAC,GACnC,IAAI,CAAC,SAAS,IAAI,MAAM,GACtB,MAAM,IAAI,MACR,eAAe,GAAG,QAAQ,UAAU,yBAC9B,OAAO,eAAe,4BAA4B,cAAc,EAAE,oCACnC,OAAO,eAAe,GAC7D;AAIR"}
|
|
@@ -1,28 +1,42 @@
|
|
|
1
1
|
import { parse } from "partial-json";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
2
|
+
//#region src/activities/chat/stream/json-parser.ts
|
|
3
|
+
/**
|
|
4
|
+
* JSON Parser for partial/incomplete JSON strings
|
|
5
|
+
*
|
|
6
|
+
* Used during streaming to parse tool call arguments that may be incomplete.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Partial JSON Parser implementation using the partial-json library
|
|
10
|
+
* This parser can handle incomplete JSON strings during streaming
|
|
11
|
+
*/
|
|
12
|
+
var PartialJSONParser = class {
|
|
13
|
+
/**
|
|
14
|
+
* Parse a potentially incomplete JSON string
|
|
15
|
+
* @param jsonString - The JSON string to parse (may be incomplete)
|
|
16
|
+
* @returns The parsed object, or undefined if parsing fails
|
|
17
|
+
*/
|
|
18
|
+
parse(jsonString) {
|
|
19
|
+
if (!jsonString || jsonString.trim() === "") return;
|
|
20
|
+
try {
|
|
21
|
+
return parse(jsonString);
|
|
22
|
+
} catch {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Default parser instance
|
|
29
|
+
*/
|
|
30
|
+
var defaultJSONParser = new PartialJSONParser();
|
|
31
|
+
/**
|
|
32
|
+
* Parse partial JSON string (convenience function)
|
|
33
|
+
* @param jsonString - The JSON string to parse (may be incomplete)
|
|
34
|
+
* @returns The parsed object, or undefined if parsing fails
|
|
35
|
+
*/
|
|
20
36
|
function parsePartialJSON(jsonString) {
|
|
21
|
-
|
|
37
|
+
return defaultJSONParser.parse(jsonString);
|
|
22
38
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
};
|
|
28
|
-
//# sourceMappingURL=json-parser.js.map
|
|
39
|
+
//#endregion
|
|
40
|
+
export { PartialJSONParser, defaultJSONParser, parsePartialJSON };
|
|
41
|
+
|
|
42
|
+
//# sourceMappingURL=json-parser.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"json-parser.js","sources":["../../../../../src/activities/chat/stream/json-parser.ts"],"sourcesContent":["/**\n * JSON Parser for partial/incomplete JSON strings\n *\n * Used during streaming to parse tool call arguments that may be incomplete.\n */\n\nimport { parse as parsePartialJSONLib } from 'partial-json'\n\n/**\n * JSON Parser interface - allows for custom parser implementations\n */\nexport interface JSONParser {\n /**\n * Parse a JSON string (may be incomplete/partial)\n * @param jsonString - The JSON string to parse\n * @returns The parsed object, or undefined if parsing fails\n */\n parse: (jsonString: string) => any\n}\n\n/**\n * Partial JSON Parser implementation using the partial-json library\n * This parser can handle incomplete JSON strings during streaming\n */\nexport class PartialJSONParser implements JSONParser {\n /**\n * Parse a potentially incomplete JSON string\n * @param jsonString - The JSON string to parse (may be incomplete)\n * @returns The parsed object, or undefined if parsing fails\n */\n parse(jsonString: string): any {\n if (!jsonString || jsonString.trim() === '') {\n return undefined\n }\n\n try {\n return parsePartialJSONLib(jsonString)\n } catch {\n // If partial parsing fails, return undefined\n // This is expected during early streaming when we have very little data\n return undefined\n }\n }\n}\n\n/**\n * Default parser instance\n */\nexport const defaultJSONParser = new PartialJSONParser()\n\n/**\n * Parse partial JSON string (convenience function)\n * @param jsonString - The JSON string to parse (may be incomplete)\n * @returns The parsed object, or undefined if parsing fails\n */\nexport function parsePartialJSON(jsonString: string): any {\n return defaultJSONParser.parse(jsonString)\n}\n"],"
|
|
1
|
+
{"version":3,"file":"json-parser.js","names":[],"sources":["../../../../../src/activities/chat/stream/json-parser.ts"],"sourcesContent":["/**\n * JSON Parser for partial/incomplete JSON strings\n *\n * Used during streaming to parse tool call arguments that may be incomplete.\n */\n\nimport { parse as parsePartialJSONLib } from 'partial-json'\n\n/**\n * JSON Parser interface - allows for custom parser implementations\n */\nexport interface JSONParser {\n /**\n * Parse a JSON string (may be incomplete/partial)\n * @param jsonString - The JSON string to parse\n * @returns The parsed object, or undefined if parsing fails\n */\n parse: (jsonString: string) => any\n}\n\n/**\n * Partial JSON Parser implementation using the partial-json library\n * This parser can handle incomplete JSON strings during streaming\n */\nexport class PartialJSONParser implements JSONParser {\n /**\n * Parse a potentially incomplete JSON string\n * @param jsonString - The JSON string to parse (may be incomplete)\n * @returns The parsed object, or undefined if parsing fails\n */\n parse(jsonString: string): any {\n if (!jsonString || jsonString.trim() === '') {\n return undefined\n }\n\n try {\n return parsePartialJSONLib(jsonString)\n } catch {\n // If partial parsing fails, return undefined\n // This is expected during early streaming when we have very little data\n return undefined\n }\n }\n}\n\n/**\n * Default parser instance\n */\nexport const defaultJSONParser = new PartialJSONParser()\n\n/**\n * Parse partial JSON string (convenience function)\n * @param jsonString - The JSON string to parse (may be incomplete)\n * @returns The parsed object, or undefined if parsing fails\n */\nexport function parsePartialJSON(jsonString: string): any {\n return defaultJSONParser.parse(jsonString)\n}\n"],"mappings":";;;;;;;;;;;AAwBA,IAAa,oBAAb,MAAqD;;;;;;CAMnD,MAAM,YAAyB;EAC7B,IAAI,CAAC,cAAc,WAAW,KAAK,MAAM,IACvC;EAGF,IAAI;GACF,OAAO,MAAoB,UAAU;EACvC,QAAQ;GAGN;EACF;CACF;AACF;;;;AAKA,IAAa,oBAAoB,IAAI,kBAAkB;;;;;;AAOvD,SAAgB,iBAAiB,YAAyB;CACxD,OAAO,kBAAkB,MAAM,UAAU;AAC3C"}
|