@tanstack/ai 0.42.0 → 0.43.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/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.d.ts +3 -1
- package/dist/esm/middlewares/otel.js +599 -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 +23 -5
- 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,156 +1,150 @@
|
|
|
1
|
+
//#region src/middlewares/content-guard.ts
|
|
2
|
+
/**
|
|
3
|
+
* Apply all rules to a string, returning the transformed result.
|
|
4
|
+
*/
|
|
1
5
|
function applyRules(text, rules) {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
} else {
|
|
7
|
-
result = rule.fn(result);
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
return result;
|
|
6
|
+
let result = text;
|
|
7
|
+
for (const rule of rules) if ("pattern" in rule) result = result.replace(rule.pattern, rule.replacement);
|
|
8
|
+
else result = rule.fn(result);
|
|
9
|
+
return result;
|
|
11
10
|
}
|
|
11
|
+
/**
|
|
12
|
+
* Creates a middleware that filters or transforms streamed text content.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* import { contentGuardMiddleware } from '@tanstack/ai/middlewares'
|
|
17
|
+
*
|
|
18
|
+
* const guard = contentGuardMiddleware({
|
|
19
|
+
* rules: [
|
|
20
|
+
* { pattern: /\b\d{3}-\d{2}-\d{4}\b/g, replacement: '[SSN REDACTED]' },
|
|
21
|
+
* ],
|
|
22
|
+
* strategy: 'buffered',
|
|
23
|
+
* })
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
12
26
|
function contentGuardMiddleware(options) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
bufferSize = 50,
|
|
17
|
-
blockOnMatch = false,
|
|
18
|
-
onFiltered
|
|
19
|
-
} = options;
|
|
20
|
-
if (strategy === "delta") {
|
|
21
|
-
return createDeltaStrategy(rules, blockOnMatch, onFiltered);
|
|
22
|
-
}
|
|
23
|
-
return createBufferedStrategy(rules, bufferSize, blockOnMatch, onFiltered);
|
|
27
|
+
const { rules, strategy = "buffered", bufferSize = 50, blockOnMatch = false, onFiltered } = options;
|
|
28
|
+
if (strategy === "delta") return createDeltaStrategy(rules, blockOnMatch, onFiltered);
|
|
29
|
+
return createBufferedStrategy(rules, bufferSize, blockOnMatch, onFiltered);
|
|
24
30
|
}
|
|
25
31
|
function createDeltaStrategy(rules, blockOnMatch, onFiltered) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
};
|
|
32
|
+
return {
|
|
33
|
+
name: "content-guard",
|
|
34
|
+
onChunk(_ctx, chunk) {
|
|
35
|
+
if (chunk.type !== "TEXT_MESSAGE_CONTENT") return;
|
|
36
|
+
const original = chunk.delta;
|
|
37
|
+
const filtered = applyRules(original, rules);
|
|
38
|
+
if (filtered === original) return;
|
|
39
|
+
if (onFiltered) onFiltered({
|
|
40
|
+
messageId: chunk.messageId,
|
|
41
|
+
original,
|
|
42
|
+
filtered,
|
|
43
|
+
strategy: "delta"
|
|
44
|
+
});
|
|
45
|
+
if (blockOnMatch) return null;
|
|
46
|
+
const { content: _strippedContent, ...rest } = chunk;
|
|
47
|
+
return {
|
|
48
|
+
...rest,
|
|
49
|
+
delta: filtered
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
};
|
|
49
53
|
}
|
|
50
54
|
function createBufferedStrategy(rules, bufferSize, blockOnMatch, onFiltered) {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
const emitChunk = {
|
|
143
|
-
...chunk,
|
|
144
|
-
delta: newDelta,
|
|
145
|
-
content: filtered.slice(0, safeFilteredEnd)
|
|
146
|
-
};
|
|
147
|
-
const wasEmpty = pending.length === 0;
|
|
148
|
-
pending.push(emitChunk);
|
|
149
|
-
return wasEmpty ? emitChunk : pending;
|
|
150
|
-
}
|
|
151
|
-
};
|
|
55
|
+
let rawAccumulated = "";
|
|
56
|
+
let emittedFilteredLength = 0;
|
|
57
|
+
let lastMessageId = "";
|
|
58
|
+
function resetState() {
|
|
59
|
+
rawAccumulated = "";
|
|
60
|
+
emittedFilteredLength = 0;
|
|
61
|
+
lastMessageId = "";
|
|
62
|
+
}
|
|
63
|
+
function flushBuffer() {
|
|
64
|
+
if (rawAccumulated.length === 0) return null;
|
|
65
|
+
const filtered = applyRules(rawAccumulated, rules);
|
|
66
|
+
if (blockOnMatch && filtered !== rawAccumulated) {
|
|
67
|
+
if (onFiltered) onFiltered({
|
|
68
|
+
messageId: lastMessageId,
|
|
69
|
+
original: rawAccumulated,
|
|
70
|
+
filtered,
|
|
71
|
+
strategy: "buffered"
|
|
72
|
+
});
|
|
73
|
+
resetState();
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
const remaining = filtered.slice(emittedFilteredLength);
|
|
77
|
+
if (remaining.length > 0) {
|
|
78
|
+
if (filtered !== rawAccumulated && onFiltered) onFiltered({
|
|
79
|
+
messageId: lastMessageId,
|
|
80
|
+
original: rawAccumulated,
|
|
81
|
+
filtered,
|
|
82
|
+
strategy: "buffered"
|
|
83
|
+
});
|
|
84
|
+
const flushed = {
|
|
85
|
+
type: "TEXT_MESSAGE_CONTENT",
|
|
86
|
+
messageId: lastMessageId,
|
|
87
|
+
delta: remaining,
|
|
88
|
+
content: filtered,
|
|
89
|
+
timestamp: Date.now()
|
|
90
|
+
};
|
|
91
|
+
resetState();
|
|
92
|
+
return flushed;
|
|
93
|
+
}
|
|
94
|
+
resetState();
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
name: "content-guard",
|
|
99
|
+
onStart() {
|
|
100
|
+
resetState();
|
|
101
|
+
},
|
|
102
|
+
onChunk(_ctx, chunk) {
|
|
103
|
+
if (chunk.type === "TEXT_MESSAGE_END" || chunk.type === "RUN_FINISHED") {
|
|
104
|
+
const flushed = flushBuffer();
|
|
105
|
+
if (flushed) return [flushed, chunk];
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if (chunk.type !== "TEXT_MESSAGE_CONTENT") return;
|
|
109
|
+
const pending = [];
|
|
110
|
+
if (lastMessageId && chunk.messageId !== lastMessageId) {
|
|
111
|
+
const flushed = flushBuffer();
|
|
112
|
+
if (flushed) pending.push(flushed);
|
|
113
|
+
}
|
|
114
|
+
rawAccumulated += chunk.delta;
|
|
115
|
+
lastMessageId = chunk.messageId;
|
|
116
|
+
const filtered = applyRules(rawAccumulated, rules);
|
|
117
|
+
const safeFilteredEnd = Math.max(0, filtered.length - bufferSize);
|
|
118
|
+
if (safeFilteredEnd <= emittedFilteredLength) return pending.length > 0 ? pending : null;
|
|
119
|
+
if (blockOnMatch && filtered !== rawAccumulated) {
|
|
120
|
+
if (onFiltered) onFiltered({
|
|
121
|
+
messageId: chunk.messageId,
|
|
122
|
+
original: rawAccumulated,
|
|
123
|
+
filtered,
|
|
124
|
+
strategy: "buffered"
|
|
125
|
+
});
|
|
126
|
+
return pending.length > 0 ? pending : null;
|
|
127
|
+
}
|
|
128
|
+
const newDelta = filtered.slice(emittedFilteredLength, safeFilteredEnd);
|
|
129
|
+
if (filtered !== rawAccumulated && onFiltered) onFiltered({
|
|
130
|
+
messageId: chunk.messageId,
|
|
131
|
+
original: rawAccumulated,
|
|
132
|
+
filtered,
|
|
133
|
+
strategy: "buffered"
|
|
134
|
+
});
|
|
135
|
+
emittedFilteredLength = safeFilteredEnd;
|
|
136
|
+
const emitChunk = {
|
|
137
|
+
...chunk,
|
|
138
|
+
delta: newDelta,
|
|
139
|
+
content: filtered.slice(0, safeFilteredEnd)
|
|
140
|
+
};
|
|
141
|
+
const wasEmpty = pending.length === 0;
|
|
142
|
+
pending.push(emitChunk);
|
|
143
|
+
return wasEmpty ? emitChunk : pending;
|
|
144
|
+
}
|
|
145
|
+
};
|
|
152
146
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
//# sourceMappingURL=content-guard.js.map
|
|
147
|
+
//#endregion
|
|
148
|
+
export { contentGuardMiddleware };
|
|
149
|
+
|
|
150
|
+
//# sourceMappingURL=content-guard.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"content-guard.js","sources":["../../../src/middlewares/content-guard.ts"],"sourcesContent":["import type {\n ChatMiddleware,\n ChatMiddlewareContext,\n} from '../activities/chat/middleware/types'\nimport type { StreamChunk } from '../types'\n\n/**\n * A content guard rule — either a regex pattern with replacement, or a transform function.\n */\nexport type ContentGuardRule =\n | { pattern: RegExp; replacement: string }\n | { fn: (text: string) => string }\n\n/**\n * Information passed to the onFiltered callback.\n */\nexport interface ContentFilteredInfo {\n /** The message ID being filtered */\n messageId: string\n /** The original text before filtering */\n original: string\n /** The filtered text after rules applied */\n filtered: string\n /** Which strategy was used */\n strategy: 'delta' | 'buffered'\n}\n\n/**\n * Options for the content guard middleware.\n */\nexport interface ContentGuardMiddlewareOptions {\n /**\n * Rules to apply to text content. Each rule is either a regex pattern\n * with a replacement string, or a custom transform function.\n * Rules are applied in order. Each rule receives the output of the previous.\n */\n rules: Array<ContentGuardRule>\n\n /**\n * Matching strategy:\n * - 'delta': Apply rules to each delta as it arrives. Fast, real-time,\n * but patterns spanning chunk boundaries may be missed.\n * - 'buffered': Accumulate content and apply rules to settled portions,\n * holding back a look-behind buffer to catch cross-boundary patterns.\n *\n * @default 'buffered'\n */\n strategy?: 'delta' | 'buffered'\n\n /**\n * Number of characters to hold back before emitting (buffered strategy only).\n * Should be at least as long as the longest pattern you expect to match.\n * Buffer is flushed when the stream ends.\n *\n * @default 50\n */\n bufferSize?: number\n\n /**\n * If true, drop the entire chunk when any rule changes the content.\n * @default false\n */\n blockOnMatch?: boolean\n\n /**\n * Callback when content is filtered by any rule.\n */\n onFiltered?: (info: ContentFilteredInfo) => void\n}\n\n/**\n * Apply all rules to a string, returning the transformed result.\n */\nfunction applyRules(text: string, rules: Array<ContentGuardRule>): string {\n let result = text\n for (const rule of rules) {\n if ('pattern' in rule) {\n result = result.replace(rule.pattern, rule.replacement)\n } else {\n result = rule.fn(result)\n }\n }\n return result\n}\n\n/**\n * Creates a middleware that filters or transforms streamed text content.\n *\n * @example\n * ```ts\n * import { contentGuardMiddleware } from '@tanstack/ai/middlewares'\n *\n * const guard = contentGuardMiddleware({\n * rules: [\n * { pattern: /\\b\\d{3}-\\d{2}-\\d{4}\\b/g, replacement: '[SSN REDACTED]' },\n * ],\n * strategy: 'buffered',\n * })\n * ```\n */\nexport function contentGuardMiddleware(\n options: ContentGuardMiddlewareOptions,\n): ChatMiddleware {\n const {\n rules,\n strategy = 'buffered',\n bufferSize = 50,\n blockOnMatch = false,\n onFiltered,\n } = options\n\n if (strategy === 'delta') {\n return createDeltaStrategy(rules, blockOnMatch, onFiltered)\n }\n return createBufferedStrategy(rules, bufferSize, blockOnMatch, onFiltered)\n}\n\nfunction createDeltaStrategy(\n rules: Array<ContentGuardRule>,\n blockOnMatch: boolean,\n onFiltered?: (info: ContentFilteredInfo) => void,\n): ChatMiddleware {\n return {\n name: 'content-guard',\n\n onChunk(_ctx: ChatMiddlewareContext, chunk: StreamChunk) {\n if (chunk.type !== 'TEXT_MESSAGE_CONTENT') return\n\n const original = chunk.delta\n const filtered = applyRules(original, rules)\n\n if (filtered === original) return // unchanged, pass through\n\n if (onFiltered) {\n onFiltered({\n messageId: chunk.messageId,\n original,\n filtered,\n strategy: 'delta',\n })\n }\n\n if (blockOnMatch) return null // drop chunk\n\n // Strip out the previous `content` field by destructuring it away — with\n // `exactOptionalPropertyTypes` we can't assign `content: undefined`\n // against `content?: string`. The replacement event carries only the\n // filtered delta.\n const { content: _strippedContent, ...rest } = chunk\n void _strippedContent\n return {\n ...rest,\n delta: filtered,\n }\n },\n }\n}\n\nfunction createBufferedStrategy(\n rules: Array<ContentGuardRule>,\n bufferSize: number,\n blockOnMatch: boolean,\n onFiltered?: (info: ContentFilteredInfo) => void,\n): ChatMiddleware {\n let rawAccumulated = ''\n let emittedFilteredLength = 0\n let lastMessageId = ''\n\n function resetState() {\n rawAccumulated = ''\n emittedFilteredLength = 0\n lastMessageId = ''\n }\n\n function flushBuffer(): StreamChunk | null {\n if (rawAccumulated.length === 0) return null\n\n const filtered = applyRules(rawAccumulated, rules)\n\n if (blockOnMatch && filtered !== rawAccumulated) {\n if (onFiltered) {\n onFiltered({\n messageId: lastMessageId,\n original: rawAccumulated,\n filtered,\n strategy: 'buffered',\n })\n }\n resetState()\n return null\n }\n\n const remaining = filtered.slice(emittedFilteredLength)\n if (remaining.length > 0) {\n if (filtered !== rawAccumulated && onFiltered) {\n onFiltered({\n messageId: lastMessageId,\n original: rawAccumulated,\n filtered,\n strategy: 'buffered',\n })\n }\n\n const flushed = {\n type: 'TEXT_MESSAGE_CONTENT',\n messageId: lastMessageId,\n delta: remaining,\n content: filtered,\n timestamp: Date.now(),\n } as StreamChunk\n\n resetState()\n return flushed\n }\n\n resetState()\n return null\n }\n\n return {\n name: 'content-guard',\n\n onStart() {\n resetState()\n },\n\n onChunk(_ctx: ChatMiddlewareContext, chunk: StreamChunk) {\n // Flush buffer on stream end events\n if (chunk.type === 'TEXT_MESSAGE_END' || chunk.type === 'RUN_FINISHED') {\n const flushed = flushBuffer()\n if (flushed) return [flushed, chunk]\n return // pass through end event\n }\n\n if (chunk.type !== 'TEXT_MESSAGE_CONTENT') return // pass through\n\n // Flush buffer on message boundary change\n const pending: Array<StreamChunk> = []\n if (lastMessageId && chunk.messageId !== lastMessageId) {\n const flushed = flushBuffer()\n if (flushed) pending.push(flushed)\n }\n\n rawAccumulated += chunk.delta\n lastMessageId = chunk.messageId\n\n // Apply rules to full accumulated text, buffer in filtered space\n const filtered = applyRules(rawAccumulated, rules)\n const safeFilteredEnd = Math.max(0, filtered.length - bufferSize)\n\n if (safeFilteredEnd <= emittedFilteredLength) {\n return pending.length > 0 ? pending : null\n }\n\n if (blockOnMatch && filtered !== rawAccumulated) {\n if (onFiltered) {\n onFiltered({\n messageId: chunk.messageId,\n original: rawAccumulated,\n filtered,\n strategy: 'buffered',\n })\n }\n return pending.length > 0 ? pending : null\n }\n\n const newDelta = filtered.slice(emittedFilteredLength, safeFilteredEnd)\n\n if (filtered !== rawAccumulated && onFiltered) {\n onFiltered({\n messageId: chunk.messageId,\n original: rawAccumulated,\n filtered,\n strategy: 'buffered',\n })\n }\n\n emittedFilteredLength = safeFilteredEnd\n\n const emitChunk = {\n ...chunk,\n delta: newDelta,\n content: filtered.slice(0, safeFilteredEnd),\n } as StreamChunk\n\n // `pending` was empty before this push iff `emitChunk` is now the only\n // entry — return it directly without re-indexing through `pending[0]`.\n const wasEmpty = pending.length === 0\n pending.push(emitChunk)\n return wasEmpty ? emitChunk : pending\n },\n }\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"content-guard.js","names":[],"sources":["../../../src/middlewares/content-guard.ts"],"sourcesContent":["import type {\n ChatMiddleware,\n ChatMiddlewareContext,\n} from '../activities/chat/middleware/types'\nimport type { StreamChunk } from '../types'\n\n/**\n * A content guard rule — either a regex pattern with replacement, or a transform function.\n */\nexport type ContentGuardRule =\n | { pattern: RegExp; replacement: string }\n | { fn: (text: string) => string }\n\n/**\n * Information passed to the onFiltered callback.\n */\nexport interface ContentFilteredInfo {\n /** The message ID being filtered */\n messageId: string\n /** The original text before filtering */\n original: string\n /** The filtered text after rules applied */\n filtered: string\n /** Which strategy was used */\n strategy: 'delta' | 'buffered'\n}\n\n/**\n * Options for the content guard middleware.\n */\nexport interface ContentGuardMiddlewareOptions {\n /**\n * Rules to apply to text content. Each rule is either a regex pattern\n * with a replacement string, or a custom transform function.\n * Rules are applied in order. Each rule receives the output of the previous.\n */\n rules: Array<ContentGuardRule>\n\n /**\n * Matching strategy:\n * - 'delta': Apply rules to each delta as it arrives. Fast, real-time,\n * but patterns spanning chunk boundaries may be missed.\n * - 'buffered': Accumulate content and apply rules to settled portions,\n * holding back a look-behind buffer to catch cross-boundary patterns.\n *\n * @default 'buffered'\n */\n strategy?: 'delta' | 'buffered'\n\n /**\n * Number of characters to hold back before emitting (buffered strategy only).\n * Should be at least as long as the longest pattern you expect to match.\n * Buffer is flushed when the stream ends.\n *\n * @default 50\n */\n bufferSize?: number\n\n /**\n * If true, drop the entire chunk when any rule changes the content.\n * @default false\n */\n blockOnMatch?: boolean\n\n /**\n * Callback when content is filtered by any rule.\n */\n onFiltered?: (info: ContentFilteredInfo) => void\n}\n\n/**\n * Apply all rules to a string, returning the transformed result.\n */\nfunction applyRules(text: string, rules: Array<ContentGuardRule>): string {\n let result = text\n for (const rule of rules) {\n if ('pattern' in rule) {\n result = result.replace(rule.pattern, rule.replacement)\n } else {\n result = rule.fn(result)\n }\n }\n return result\n}\n\n/**\n * Creates a middleware that filters or transforms streamed text content.\n *\n * @example\n * ```ts\n * import { contentGuardMiddleware } from '@tanstack/ai/middlewares'\n *\n * const guard = contentGuardMiddleware({\n * rules: [\n * { pattern: /\\b\\d{3}-\\d{2}-\\d{4}\\b/g, replacement: '[SSN REDACTED]' },\n * ],\n * strategy: 'buffered',\n * })\n * ```\n */\nexport function contentGuardMiddleware(\n options: ContentGuardMiddlewareOptions,\n): ChatMiddleware {\n const {\n rules,\n strategy = 'buffered',\n bufferSize = 50,\n blockOnMatch = false,\n onFiltered,\n } = options\n\n if (strategy === 'delta') {\n return createDeltaStrategy(rules, blockOnMatch, onFiltered)\n }\n return createBufferedStrategy(rules, bufferSize, blockOnMatch, onFiltered)\n}\n\nfunction createDeltaStrategy(\n rules: Array<ContentGuardRule>,\n blockOnMatch: boolean,\n onFiltered?: (info: ContentFilteredInfo) => void,\n): ChatMiddleware {\n return {\n name: 'content-guard',\n\n onChunk(_ctx: ChatMiddlewareContext, chunk: StreamChunk) {\n if (chunk.type !== 'TEXT_MESSAGE_CONTENT') return\n\n const original = chunk.delta\n const filtered = applyRules(original, rules)\n\n if (filtered === original) return // unchanged, pass through\n\n if (onFiltered) {\n onFiltered({\n messageId: chunk.messageId,\n original,\n filtered,\n strategy: 'delta',\n })\n }\n\n if (blockOnMatch) return null // drop chunk\n\n // Strip out the previous `content` field by destructuring it away — with\n // `exactOptionalPropertyTypes` we can't assign `content: undefined`\n // against `content?: string`. The replacement event carries only the\n // filtered delta.\n const { content: _strippedContent, ...rest } = chunk\n void _strippedContent\n return {\n ...rest,\n delta: filtered,\n }\n },\n }\n}\n\nfunction createBufferedStrategy(\n rules: Array<ContentGuardRule>,\n bufferSize: number,\n blockOnMatch: boolean,\n onFiltered?: (info: ContentFilteredInfo) => void,\n): ChatMiddleware {\n let rawAccumulated = ''\n let emittedFilteredLength = 0\n let lastMessageId = ''\n\n function resetState() {\n rawAccumulated = ''\n emittedFilteredLength = 0\n lastMessageId = ''\n }\n\n function flushBuffer(): StreamChunk | null {\n if (rawAccumulated.length === 0) return null\n\n const filtered = applyRules(rawAccumulated, rules)\n\n if (blockOnMatch && filtered !== rawAccumulated) {\n if (onFiltered) {\n onFiltered({\n messageId: lastMessageId,\n original: rawAccumulated,\n filtered,\n strategy: 'buffered',\n })\n }\n resetState()\n return null\n }\n\n const remaining = filtered.slice(emittedFilteredLength)\n if (remaining.length > 0) {\n if (filtered !== rawAccumulated && onFiltered) {\n onFiltered({\n messageId: lastMessageId,\n original: rawAccumulated,\n filtered,\n strategy: 'buffered',\n })\n }\n\n const flushed = {\n type: 'TEXT_MESSAGE_CONTENT',\n messageId: lastMessageId,\n delta: remaining,\n content: filtered,\n timestamp: Date.now(),\n } as StreamChunk\n\n resetState()\n return flushed\n }\n\n resetState()\n return null\n }\n\n return {\n name: 'content-guard',\n\n onStart() {\n resetState()\n },\n\n onChunk(_ctx: ChatMiddlewareContext, chunk: StreamChunk) {\n // Flush buffer on stream end events\n if (chunk.type === 'TEXT_MESSAGE_END' || chunk.type === 'RUN_FINISHED') {\n const flushed = flushBuffer()\n if (flushed) return [flushed, chunk]\n return // pass through end event\n }\n\n if (chunk.type !== 'TEXT_MESSAGE_CONTENT') return // pass through\n\n // Flush buffer on message boundary change\n const pending: Array<StreamChunk> = []\n if (lastMessageId && chunk.messageId !== lastMessageId) {\n const flushed = flushBuffer()\n if (flushed) pending.push(flushed)\n }\n\n rawAccumulated += chunk.delta\n lastMessageId = chunk.messageId\n\n // Apply rules to full accumulated text, buffer in filtered space\n const filtered = applyRules(rawAccumulated, rules)\n const safeFilteredEnd = Math.max(0, filtered.length - bufferSize)\n\n if (safeFilteredEnd <= emittedFilteredLength) {\n return pending.length > 0 ? pending : null\n }\n\n if (blockOnMatch && filtered !== rawAccumulated) {\n if (onFiltered) {\n onFiltered({\n messageId: chunk.messageId,\n original: rawAccumulated,\n filtered,\n strategy: 'buffered',\n })\n }\n return pending.length > 0 ? pending : null\n }\n\n const newDelta = filtered.slice(emittedFilteredLength, safeFilteredEnd)\n\n if (filtered !== rawAccumulated && onFiltered) {\n onFiltered({\n messageId: chunk.messageId,\n original: rawAccumulated,\n filtered,\n strategy: 'buffered',\n })\n }\n\n emittedFilteredLength = safeFilteredEnd\n\n const emitChunk = {\n ...chunk,\n delta: newDelta,\n content: filtered.slice(0, safeFilteredEnd),\n } as StreamChunk\n\n // `pending` was empty before this push iff `emitChunk` is now the only\n // entry — return it directly without re-indexing through `pending[0]`.\n const wasEmpty = pending.length === 0\n pending.push(emitChunk)\n return wasEmpty ? emitChunk : pending\n },\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
import { contentGuardMiddleware } from "./content-guard.js";
|
|
2
1
|
import { toolCacheMiddleware } from "../activities/chat/middleware/tool-cache-middleware.js";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
toolCacheMiddleware
|
|
6
|
-
};
|
|
7
|
-
//# sourceMappingURL=index.js.map
|
|
2
|
+
import { contentGuardMiddleware } from "./content-guard.js";
|
|
3
|
+
export { contentGuardMiddleware, toolCacheMiddleware };
|
|
@@ -5,7 +5,9 @@ import { GenerationMiddleware, GenerationMiddlewareContext } from '../activities
|
|
|
5
5
|
* Scope (role) of an OTel span emitted by this middleware.
|
|
6
6
|
*
|
|
7
7
|
* - `chat` — the root span for a single `chat()` call
|
|
8
|
-
* - `iteration` — one per agent-loop
|
|
8
|
+
* - `iteration` — one per provider model call (agent-loop `beforeModel`
|
|
9
|
+
* turn, or the separate `structuredOutput` finalization when
|
|
10
|
+
* `outputSchema` skips the agent loop — see #1054)
|
|
9
11
|
* - `tool` — one per tool execution inside an iteration
|
|
10
12
|
* - `generation` — the single span for a media activity call
|
|
11
13
|
* (`generateImage`, `generateVideo`, `generateSpeech`, …)
|