@copilotkit/runtime 1.8.12-next.2 → 1.8.12-next.4
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/CHANGELOG.md +13 -0
- package/dist/{chunk-OZLQ2A5E.mjs → chunk-FA3E4I4W.mjs} +4 -3
- package/dist/chunk-FA3E4I4W.mjs.map +1 -0
- package/dist/{chunk-FDGTTGQU.mjs → chunk-KGZF7KSR.mjs} +2 -2
- package/dist/{chunk-VQSVMSXZ.mjs → chunk-MG576PIZ.mjs} +2 -2
- package/dist/{chunk-Y4H3U52G.mjs → chunk-MVKCCH5U.mjs} +216 -173
- package/dist/chunk-MVKCCH5U.mjs.map +1 -0
- package/dist/{chunk-V6IQU4D2.mjs → chunk-S5U6J5X2.mjs} +2 -2
- package/dist/index.js +217 -173
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +5 -5
- package/dist/lib/index.js +109 -82
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/index.mjs +5 -5
- package/dist/lib/integrations/index.js +2 -1
- package/dist/lib/integrations/index.js.map +1 -1
- package/dist/lib/integrations/index.mjs +5 -5
- package/dist/lib/integrations/nest/index.js +2 -1
- package/dist/lib/integrations/nest/index.js.map +1 -1
- package/dist/lib/integrations/nest/index.mjs +3 -3
- package/dist/lib/integrations/node-express/index.js +2 -1
- package/dist/lib/integrations/node-express/index.js.map +1 -1
- package/dist/lib/integrations/node-express/index.mjs +3 -3
- package/dist/lib/integrations/node-http/index.js +2 -1
- package/dist/lib/integrations/node-http/index.js.map +1 -1
- package/dist/lib/integrations/node-http/index.mjs +2 -2
- package/dist/service-adapters/index.js +215 -172
- package/dist/service-adapters/index.js.map +1 -1
- package/dist/service-adapters/index.mjs +1 -1
- package/jest.config.js +8 -3
- package/package.json +3 -2
- package/src/service-adapters/anthropic/anthropic-adapter.ts +124 -66
- package/src/service-adapters/anthropic/utils.ts +0 -19
- package/src/service-adapters/openai/openai-adapter.ts +107 -69
- package/tests/global.d.ts +13 -0
- package/tests/service-adapters/anthropic/allowlist-approach.test.ts +226 -0
- package/tests/service-adapters/anthropic/anthropic-adapter.test.ts +604 -0
- package/tests/service-adapters/openai/allowlist-approach.test.ts +238 -0
- package/tests/service-adapters/openai/openai-adapter.test.ts +301 -0
- package/tests/setup.jest.ts +21 -0
- package/tests/tsconfig.json +10 -0
- package/tsconfig.json +1 -1
- package/dist/chunk-OZLQ2A5E.mjs.map +0 -1
- package/dist/chunk-Y4H3U52G.mjs.map +0 -1
- /package/dist/{chunk-FDGTTGQU.mjs.map → chunk-KGZF7KSR.mjs.map} +0 -0
- /package/dist/{chunk-VQSVMSXZ.mjs.map → chunk-MG576PIZ.mjs.map} +0 -0
- /package/dist/{chunk-V6IQU4D2.mjs.map → chunk-S5U6J5X2.mjs.map} +0 -0
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment node
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect } from "@jest/globals";
|
|
6
|
+
|
|
7
|
+
describe("Anthropic Adapter - Allowlist Approach", () => {
|
|
8
|
+
it("should filter out tool_result messages with no corresponding tool_use ID", () => {
|
|
9
|
+
// Setup test data
|
|
10
|
+
const validToolUseIds = new Set<string>(["valid-id-1", "valid-id-2"]);
|
|
11
|
+
|
|
12
|
+
// Messages to filter - valid and invalid ones
|
|
13
|
+
const messages = [
|
|
14
|
+
{ type: "text", role: "user", content: "Hello" },
|
|
15
|
+
{ type: "tool_result", actionExecutionId: "valid-id-1", result: "result1" },
|
|
16
|
+
{ type: "tool_result", actionExecutionId: "invalid-id", result: "invalid" },
|
|
17
|
+
{ type: "tool_result", actionExecutionId: "valid-id-2", result: "result2" },
|
|
18
|
+
{ type: "tool_result", actionExecutionId: "valid-id-1", result: "duplicate" }, // Duplicate ID
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
// Apply the allowlist filter approach
|
|
22
|
+
const filteredMessages = [];
|
|
23
|
+
const processedIds = new Set<string>();
|
|
24
|
+
|
|
25
|
+
for (const message of messages) {
|
|
26
|
+
if (message.type === "tool_result") {
|
|
27
|
+
// Skip if no corresponding valid tool_use ID
|
|
28
|
+
if (!validToolUseIds.has(message.actionExecutionId)) {
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Skip if we've already processed this ID
|
|
33
|
+
if (processedIds.has(message.actionExecutionId)) {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Mark this ID as processed
|
|
38
|
+
processedIds.add(message.actionExecutionId);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Include all non-tool-result messages and valid tool results
|
|
42
|
+
filteredMessages.push(message);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Verify results
|
|
46
|
+
expect(filteredMessages.length).toBe(3); // text + 2 valid tool results (no duplicates or invalid)
|
|
47
|
+
|
|
48
|
+
// Valid results should be included
|
|
49
|
+
expect(
|
|
50
|
+
filteredMessages.some(
|
|
51
|
+
(m) => m.type === "tool_result" && m.actionExecutionId === "valid-id-1",
|
|
52
|
+
),
|
|
53
|
+
).toBe(true);
|
|
54
|
+
|
|
55
|
+
expect(
|
|
56
|
+
filteredMessages.some(
|
|
57
|
+
(m) => m.type === "tool_result" && m.actionExecutionId === "valid-id-2",
|
|
58
|
+
),
|
|
59
|
+
).toBe(true);
|
|
60
|
+
|
|
61
|
+
// Invalid result should be excluded
|
|
62
|
+
expect(
|
|
63
|
+
filteredMessages.some(
|
|
64
|
+
(m) => m.type === "tool_result" && m.actionExecutionId === "invalid-id",
|
|
65
|
+
),
|
|
66
|
+
).toBe(false);
|
|
67
|
+
|
|
68
|
+
// Duplicate should be excluded
|
|
69
|
+
const validId1Count = filteredMessages.filter(
|
|
70
|
+
(m) => m.type === "tool_result" && m.actionExecutionId === "valid-id-1",
|
|
71
|
+
).length;
|
|
72
|
+
|
|
73
|
+
expect(validId1Count).toBe(1);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("should maintain correct order of messages when filtering", () => {
|
|
77
|
+
// Setup test data with specific ordering
|
|
78
|
+
const validToolUseIds = new Set<string>(["tool-1", "tool-2", "tool-3"]);
|
|
79
|
+
|
|
80
|
+
// Messages in a specific order, with some invalid/duplicate results
|
|
81
|
+
const messages = [
|
|
82
|
+
{ type: "text", role: "user", content: "Initial message" },
|
|
83
|
+
{ type: "text", role: "assistant", content: "I'll help with that" },
|
|
84
|
+
{ type: "tool_use", id: "tool-1", name: "firstTool" },
|
|
85
|
+
{ type: "tool_result", actionExecutionId: "tool-1", result: "result1" },
|
|
86
|
+
{ type: "text", role: "assistant", content: "Got the first result" },
|
|
87
|
+
{ type: "tool_use", id: "tool-2", name: "secondTool" },
|
|
88
|
+
{ type: "tool_result", actionExecutionId: "tool-2", result: "result2" },
|
|
89
|
+
{ type: "tool_result", actionExecutionId: "invalid-id", result: "invalid-result" },
|
|
90
|
+
{ type: "tool_use", id: "tool-3", name: "thirdTool" },
|
|
91
|
+
{ type: "tool_result", actionExecutionId: "tool-1", result: "duplicate-result" }, // Duplicate
|
|
92
|
+
{ type: "tool_result", actionExecutionId: "tool-3", result: "result3" },
|
|
93
|
+
{ type: "text", role: "user", content: "Final message" },
|
|
94
|
+
];
|
|
95
|
+
|
|
96
|
+
// Apply the allowlist filter approach
|
|
97
|
+
const filteredMessages = [];
|
|
98
|
+
const processedIds = new Set<string>();
|
|
99
|
+
|
|
100
|
+
for (const message of messages) {
|
|
101
|
+
if (message.type === "tool_result") {
|
|
102
|
+
// Skip if no corresponding valid tool_use ID
|
|
103
|
+
if (!validToolUseIds.has(message.actionExecutionId)) {
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Skip if we've already processed this ID
|
|
108
|
+
if (processedIds.has(message.actionExecutionId)) {
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Mark this ID as processed
|
|
113
|
+
processedIds.add(message.actionExecutionId);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Include all non-tool-result messages and valid tool results
|
|
117
|
+
filteredMessages.push(message);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Verify results
|
|
121
|
+
expect(filteredMessages.length).toBe(10); // 12 original - 2 filtered out
|
|
122
|
+
|
|
123
|
+
// Check that the order is preserved
|
|
124
|
+
expect(filteredMessages[0].type).toBe("text"); // Initial user message
|
|
125
|
+
expect(filteredMessages[1].type).toBe("text"); // Assistant response
|
|
126
|
+
expect(filteredMessages[2].type).toBe("tool_use"); // First tool
|
|
127
|
+
expect(filteredMessages[3].type).toBe("tool_result"); // First result
|
|
128
|
+
expect(filteredMessages[3].actionExecutionId).toBe("tool-1"); // First result
|
|
129
|
+
expect(filteredMessages[4].type).toBe("text"); // Assistant comment
|
|
130
|
+
expect(filteredMessages[5].type).toBe("tool_use"); // Second tool
|
|
131
|
+
expect(filteredMessages[6].type).toBe("tool_result"); // Second result
|
|
132
|
+
expect(filteredMessages[6].actionExecutionId).toBe("tool-2"); // Second result
|
|
133
|
+
expect(filteredMessages[7].type).toBe("tool_use"); // Third tool
|
|
134
|
+
expect(filteredMessages[8].type).toBe("tool_result"); // Third result
|
|
135
|
+
expect(filteredMessages[8].actionExecutionId).toBe("tool-3"); // Third result
|
|
136
|
+
expect(filteredMessages[9].type).toBe("text"); // Final user message
|
|
137
|
+
|
|
138
|
+
// Each valid tool ID should appear exactly once in the results
|
|
139
|
+
const toolResultCounts = {
|
|
140
|
+
"tool-1": 0,
|
|
141
|
+
"tool-2": 0,
|
|
142
|
+
"tool-3": 0,
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
filteredMessages.forEach((message) => {
|
|
146
|
+
if (message.type === "tool_result" && message.actionExecutionId in toolResultCounts) {
|
|
147
|
+
toolResultCounts[message.actionExecutionId]++;
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
expect(toolResultCounts["tool-1"]).toBe(1);
|
|
152
|
+
expect(toolResultCounts["tool-2"]).toBe(1);
|
|
153
|
+
expect(toolResultCounts["tool-3"]).toBe(1);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it("should handle an empty message array", () => {
|
|
157
|
+
const validToolUseIds = new Set<string>(["valid-id-1", "valid-id-2"]);
|
|
158
|
+
const messages = [];
|
|
159
|
+
|
|
160
|
+
// Apply the filtering logic
|
|
161
|
+
const filteredMessages = [];
|
|
162
|
+
const processedIds = new Set<string>();
|
|
163
|
+
|
|
164
|
+
for (const message of messages) {
|
|
165
|
+
if (message.type === "tool_result") {
|
|
166
|
+
if (
|
|
167
|
+
!validToolUseIds.has(message.actionExecutionId) ||
|
|
168
|
+
processedIds.has(message.actionExecutionId)
|
|
169
|
+
) {
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
processedIds.add(message.actionExecutionId);
|
|
173
|
+
}
|
|
174
|
+
filteredMessages.push(message);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
expect(filteredMessages.length).toBe(0);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it("should handle edge cases with mixed message types", () => {
|
|
181
|
+
// Setup with mixed message types
|
|
182
|
+
const validToolUseIds = new Set<string>(["valid-id-1"]);
|
|
183
|
+
|
|
184
|
+
const messages = [
|
|
185
|
+
{ type: "text", role: "user", content: "Hello" },
|
|
186
|
+
{ type: "image", url: "https://example.com/image.jpg" }, // Non-tool message type
|
|
187
|
+
{ type: "tool_result", actionExecutionId: "valid-id-1", result: "result1" },
|
|
188
|
+
{ type: "custom", data: { key: "value" } }, // Another custom type
|
|
189
|
+
{ type: "tool_result", actionExecutionId: "valid-id-1", result: "duplicate" }, // Duplicate
|
|
190
|
+
{ type: "null", value: null }, // Edge case
|
|
191
|
+
{ type: "undefined" }, // Edge case
|
|
192
|
+
];
|
|
193
|
+
|
|
194
|
+
// Apply the filtering logic
|
|
195
|
+
const filteredMessages = [];
|
|
196
|
+
const processedIds = new Set<string>();
|
|
197
|
+
|
|
198
|
+
for (const message of messages) {
|
|
199
|
+
if (message.type === "tool_result") {
|
|
200
|
+
if (
|
|
201
|
+
!validToolUseIds.has(message.actionExecutionId) ||
|
|
202
|
+
processedIds.has(message.actionExecutionId)
|
|
203
|
+
) {
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
processedIds.add(message.actionExecutionId);
|
|
207
|
+
}
|
|
208
|
+
filteredMessages.push(message);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Should have all non-tool_result messages + 1 valid tool_result
|
|
212
|
+
expect(filteredMessages.length).toBe(6); // 7 original - 1 duplicate
|
|
213
|
+
|
|
214
|
+
// Valid tool_result should be included exactly once
|
|
215
|
+
const toolResults = filteredMessages.filter((m) => m.type === "tool_result");
|
|
216
|
+
expect(toolResults.length).toBe(1);
|
|
217
|
+
expect(toolResults[0].actionExecutionId).toBe("valid-id-1");
|
|
218
|
+
|
|
219
|
+
// All other message types should be preserved
|
|
220
|
+
expect(filteredMessages.filter((m) => m.type === "text").length).toBe(1);
|
|
221
|
+
expect(filteredMessages.filter((m) => m.type === "image").length).toBe(1);
|
|
222
|
+
expect(filteredMessages.filter((m) => m.type === "custom").length).toBe(1);
|
|
223
|
+
expect(filteredMessages.filter((m) => m.type === "null").length).toBe(1);
|
|
224
|
+
expect(filteredMessages.filter((m) => m.type === "undefined").length).toBe(1);
|
|
225
|
+
});
|
|
226
|
+
});
|