@aj-archipelago/cortex 1.3.63 → 1.3.64
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/lib/entityConstants.js +1 -1
- package/package.json +1 -1
- package/pathways/system/entity/memory/shared/sys_memory_helpers.js +1 -1
- package/pathways/system/entity/memory/sys_memory_manager.js +1 -1
- package/pathways/system/entity/sys_entity_agent.js +18 -5
- package/pathways/system/entity/sys_generator_results.js +1 -1
- package/server/plugins/gemini15VisionPlugin.js +3 -1
- package/server/plugins/openAiVisionPlugin.js +3 -1
- package/tests/helpers/sseClient.js +1 -1
- package/tests/unit/plugins/toolCallBufferFiltering.test.js +297 -0
package/lib/entityConstants.js
CHANGED
|
@@ -145,7 +145,7 @@ Privacy is critical. If asked to forget or delete something, always comply affir
|
|
|
145
145
|
AI_DATETIME: "# Time, Date, and Time Zone\n\nThe current time and date in GMT is {{now}}, but references like \"today\" or \"yesterday\" are relative to the user's time zone. If you remember the user's time zone, use it - it's possible that the day for the user is different than the day in GMT.",
|
|
146
146
|
|
|
147
147
|
AI_STYLE_OPENAI: "oai-gpt41",
|
|
148
|
-
AI_STYLE_OPENAI_RESEARCH: "oai-
|
|
148
|
+
AI_STYLE_OPENAI_RESEARCH: "oai-gpt5",
|
|
149
149
|
AI_STYLE_ANTHROPIC: "claude-4-sonnet-vertex",
|
|
150
150
|
AI_STYLE_ANTHROPIC_RESEARCH: "claude-41-opus-vertex",
|
|
151
151
|
AI_STYLE_XAI: "xai-grok-4-fast-reasoning",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aj-archipelago/cortex",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.64",
|
|
4
4
|
"description": "Cortex is a GraphQL API for AI. It provides a simple, extensible interface for using AI services from OpenAI, Azure and others.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"repository": {
|
|
@@ -34,7 +34,7 @@ const normalizeMemoryFormat = async (args, content) => {
|
|
|
34
34
|
formattedContent = [...validLines, ...formattedBlock.split('\n')];
|
|
35
35
|
}
|
|
36
36
|
} catch (error) {
|
|
37
|
-
logger.warn(
|
|
37
|
+
logger.warn(`Error formatting invalid memory lines: ${error?.message || 'Unknown error'}`);
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
|
|
@@ -123,7 +123,7 @@ export default {
|
|
|
123
123
|
return "";
|
|
124
124
|
|
|
125
125
|
} catch (e) {
|
|
126
|
-
logger.warn(
|
|
126
|
+
logger.warn(`sys_memory_required returned invalid JSON: ${JSON.stringify(memoryRequired)}`);
|
|
127
127
|
return "";
|
|
128
128
|
}
|
|
129
129
|
|
|
@@ -43,9 +43,12 @@ export default {
|
|
|
43
43
|
// Handle both CortexResponse objects and plain message objects
|
|
44
44
|
let tool_calls;
|
|
45
45
|
if (message instanceof CortexResponse) {
|
|
46
|
-
tool_calls = message.toolCalls ||
|
|
46
|
+
tool_calls = [...(message.toolCalls || [])];
|
|
47
|
+
if (message.functionCall) {
|
|
48
|
+
tool_calls.push(message.functionCall);
|
|
49
|
+
}
|
|
47
50
|
} else {
|
|
48
|
-
tool_calls = message.tool_calls;
|
|
51
|
+
tool_calls = [...(message.tool_calls || [])];
|
|
49
52
|
}
|
|
50
53
|
|
|
51
54
|
const pathwayResolver = resolver;
|
|
@@ -56,10 +59,20 @@ export default {
|
|
|
56
59
|
const preToolCallMessages = JSON.parse(JSON.stringify(args.chatHistory || []));
|
|
57
60
|
const finalMessages = JSON.parse(JSON.stringify(preToolCallMessages));
|
|
58
61
|
|
|
59
|
-
if (tool_calls) {
|
|
62
|
+
if (tool_calls && tool_calls.length > 0) {
|
|
60
63
|
if (pathwayResolver.toolCallCount < MAX_TOOL_CALLS) {
|
|
61
64
|
// Execute tool calls in parallel but with isolated message histories
|
|
62
|
-
|
|
65
|
+
// Filter out any undefined or invalid tool calls
|
|
66
|
+
const invalidToolCalls = tool_calls.filter(tc => !tc || !tc.function || !tc.function.name);
|
|
67
|
+
if (invalidToolCalls.length > 0) {
|
|
68
|
+
logger.warn(`Found ${invalidToolCalls.length} invalid tool calls: ${JSON.stringify(invalidToolCalls, null, 2)}`);
|
|
69
|
+
// bail out if we're getting invalid tool calls
|
|
70
|
+
pathwayResolver.toolCallCount = MAX_TOOL_CALLS;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const validToolCalls = tool_calls.filter(tc => tc && tc.function && tc.function.name);
|
|
74
|
+
|
|
75
|
+
const toolResults = await Promise.all(validToolCalls.map(async (toolCall) => {
|
|
63
76
|
try {
|
|
64
77
|
if (!toolCall?.function?.arguments) {
|
|
65
78
|
throw new Error('Invalid tool call structure: missing function arguments');
|
|
@@ -192,7 +205,7 @@ export default {
|
|
|
192
205
|
}
|
|
193
206
|
|
|
194
207
|
// Check if any tool calls failed
|
|
195
|
-
const failedTools = toolResults.filter(result => !result.success);
|
|
208
|
+
const failedTools = toolResults.filter(result => result && !result.success);
|
|
196
209
|
if (failedTools.length > 0) {
|
|
197
210
|
logger.warn(`Some tool calls failed: ${failedTools.map(t => t.error).join(', ')}`);
|
|
198
211
|
}
|
|
@@ -342,7 +342,7 @@ Here are the information sources that were found:
|
|
|
342
342
|
}
|
|
343
343
|
|
|
344
344
|
if (!args.voiceResponse) {
|
|
345
|
-
const referencedSources = extractReferencedSources(result);
|
|
345
|
+
const referencedSources = extractReferencedSources(result.toString());
|
|
346
346
|
searchResults = searchResults.length ? pruneSearchResults(searchResults, referencedSources) : [];
|
|
347
347
|
}
|
|
348
348
|
|
|
@@ -464,11 +464,13 @@ class Gemini15VisionPlugin extends Gemini15ChatPlugin {
|
|
|
464
464
|
const pathwayResolver = requestState[this.requestId]?.pathwayResolver;
|
|
465
465
|
|
|
466
466
|
if (finishReason === 'tool_calls' && this.toolCallsBuffer.length > 0 && this.pathwayToolCallback && pathwayResolver) {
|
|
467
|
+
// Filter out undefined elements from the tool calls buffer
|
|
468
|
+
const validToolCalls = this.toolCallsBuffer.filter(tc => tc && tc.function && tc.function.name);
|
|
467
469
|
// Execute tool callback and keep stream open
|
|
468
470
|
const toolMessage = {
|
|
469
471
|
role: 'assistant',
|
|
470
472
|
content: this.contentBuffer || '',
|
|
471
|
-
tool_calls:
|
|
473
|
+
tool_calls: validToolCalls,
|
|
472
474
|
};
|
|
473
475
|
this.pathwayToolCallback(pathwayResolver?.args, toolMessage, pathwayResolver);
|
|
474
476
|
// Clear tool buffer after processing; keep content for citations/continuations
|
|
@@ -280,10 +280,12 @@ class OpenAIVisionPlugin extends OpenAIChatPlugin {
|
|
|
280
280
|
case 'tool_calls':
|
|
281
281
|
// Process complete tool calls when we get the finish reason
|
|
282
282
|
if (this.pathwayToolCallback && this.toolCallsBuffer.length > 0 && pathwayResolver) {
|
|
283
|
+
// Filter out undefined elements from the tool calls buffer
|
|
284
|
+
const validToolCalls = this.toolCallsBuffer.filter(tc => tc && tc.function && tc.function.name);
|
|
283
285
|
const toolMessage = {
|
|
284
286
|
role: 'assistant',
|
|
285
287
|
content: delta?.content || '',
|
|
286
|
-
tool_calls:
|
|
288
|
+
tool_calls: validToolCalls,
|
|
287
289
|
};
|
|
288
290
|
this.pathwayToolCallback(pathwayResolver?.args, toolMessage, pathwayResolver);
|
|
289
291
|
}
|
|
@@ -7,7 +7,7 @@ export async function connectToSSEEndpoint(baseUrl, endpoint, payload, onEvent)
|
|
|
7
7
|
let sawDone = false;
|
|
8
8
|
const timeout = setTimeout(() => {
|
|
9
9
|
reject(new Error('SSE timeout waiting for [DONE]'));
|
|
10
|
-
},
|
|
10
|
+
}, 20000); // 20 second timeout
|
|
11
11
|
|
|
12
12
|
try {
|
|
13
13
|
const instance = axios.create({
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import test from 'ava';
|
|
2
|
+
import OpenAIVisionPlugin from '../../../server/plugins/openAiVisionPlugin.js';
|
|
3
|
+
import Gemini15VisionPlugin from '../../../server/plugins/gemini15VisionPlugin.js';
|
|
4
|
+
import { PathwayResolver } from '../../../server/pathwayResolver.js';
|
|
5
|
+
import { config } from '../../../config.js';
|
|
6
|
+
import { requestState } from '../../../server/requestState.js';
|
|
7
|
+
|
|
8
|
+
// Mock logger to prevent issues in tests
|
|
9
|
+
const mockLogger = {
|
|
10
|
+
debug: () => {},
|
|
11
|
+
info: () => {},
|
|
12
|
+
warn: () => {},
|
|
13
|
+
error: () => {}
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
// Mock the logger module globally
|
|
17
|
+
global.logger = mockLogger;
|
|
18
|
+
|
|
19
|
+
function createResolverWithPlugin(pluginClass, modelName = 'test-model') {
|
|
20
|
+
const pluginToModelType = {
|
|
21
|
+
OpenAIVisionPlugin: 'OPENAI-VISION',
|
|
22
|
+
Gemini15VisionPlugin: 'GEMINI-1.5-VISION'
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const modelType = pluginToModelType[pluginClass.name];
|
|
26
|
+
if (!modelType) {
|
|
27
|
+
throw new Error(`Unknown plugin class: ${pluginClass.name}`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const pathway = {
|
|
31
|
+
name: 'test-pathway',
|
|
32
|
+
model: modelName,
|
|
33
|
+
prompt: 'test prompt',
|
|
34
|
+
toolCallback: () => {} // Mock tool callback
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const model = {
|
|
38
|
+
name: modelName,
|
|
39
|
+
type: modelType
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const resolver = new PathwayResolver({
|
|
43
|
+
config,
|
|
44
|
+
pathway,
|
|
45
|
+
args: {},
|
|
46
|
+
endpoints: { [modelName]: model }
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
resolver.modelExecutor.plugin = new pluginClass(pathway, model);
|
|
50
|
+
return resolver;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
test('OpenAIVisionPlugin - filters undefined tool calls from buffer', async t => {
|
|
54
|
+
const resolver = createResolverWithPlugin(OpenAIVisionPlugin);
|
|
55
|
+
const plugin = resolver.modelExecutor.plugin;
|
|
56
|
+
|
|
57
|
+
// Simulate the scenario where tool calls start at index 1, leaving index 0 undefined
|
|
58
|
+
plugin.toolCallsBuffer[0] = undefined; // This is what causes the issue
|
|
59
|
+
plugin.toolCallsBuffer[1] = {
|
|
60
|
+
id: 'call_1_1234567890',
|
|
61
|
+
type: 'function',
|
|
62
|
+
function: {
|
|
63
|
+
name: 'test_tool',
|
|
64
|
+
arguments: '{"param": "value"}'
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
// Mock the tool callback to capture what gets passed
|
|
69
|
+
let capturedToolCalls = null;
|
|
70
|
+
plugin.pathwayToolCallback = (args, message, resolver) => {
|
|
71
|
+
capturedToolCalls = message.tool_calls;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// Mock requestProgress and pathwayResolver
|
|
75
|
+
const requestProgress = { progress: 0, started: true };
|
|
76
|
+
const pathwayResolver = { args: {} };
|
|
77
|
+
|
|
78
|
+
// Mock requestState to return our resolver
|
|
79
|
+
requestState[plugin.requestId] = { pathwayResolver };
|
|
80
|
+
|
|
81
|
+
// Simulate a tool_calls finish reason
|
|
82
|
+
const event = {
|
|
83
|
+
data: JSON.stringify({
|
|
84
|
+
choices: [{
|
|
85
|
+
finish_reason: 'tool_calls'
|
|
86
|
+
}]
|
|
87
|
+
})
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
// Process the stream event
|
|
91
|
+
plugin.processStreamEvent(event, requestProgress);
|
|
92
|
+
|
|
93
|
+
// Verify that undefined elements were filtered out
|
|
94
|
+
t.truthy(capturedToolCalls, 'Tool callback should have been called');
|
|
95
|
+
t.is(capturedToolCalls.length, 1, 'Should have filtered out undefined elements');
|
|
96
|
+
t.is(capturedToolCalls[0].function.name, 'test_tool', 'Valid tool call should be preserved');
|
|
97
|
+
|
|
98
|
+
// Clean up
|
|
99
|
+
delete requestState[plugin.requestId];
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test('OpenAIVisionPlugin - handles empty buffer gracefully', async t => {
|
|
103
|
+
const resolver = createResolverWithPlugin(OpenAIVisionPlugin);
|
|
104
|
+
const plugin = resolver.modelExecutor.plugin;
|
|
105
|
+
|
|
106
|
+
// Empty buffer
|
|
107
|
+
plugin.toolCallsBuffer = [];
|
|
108
|
+
|
|
109
|
+
// Mock the tool callback
|
|
110
|
+
let callbackCalled = false;
|
|
111
|
+
plugin.pathwayToolCallback = () => {
|
|
112
|
+
callbackCalled = true;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
// Mock requestProgress and pathwayResolver
|
|
116
|
+
const requestProgress = { progress: 0, started: true };
|
|
117
|
+
const pathwayResolver = { args: {} };
|
|
118
|
+
|
|
119
|
+
// Mock requestState
|
|
120
|
+
requestState[plugin.requestId] = { pathwayResolver };
|
|
121
|
+
|
|
122
|
+
// Simulate a tool_calls finish reason
|
|
123
|
+
const event = {
|
|
124
|
+
data: JSON.stringify({
|
|
125
|
+
choices: [{
|
|
126
|
+
finish_reason: 'tool_calls'
|
|
127
|
+
}]
|
|
128
|
+
})
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
// Process the stream event
|
|
132
|
+
plugin.processStreamEvent(event, requestProgress);
|
|
133
|
+
|
|
134
|
+
// Verify that callback was not called with empty buffer
|
|
135
|
+
t.falsy(callbackCalled, 'Tool callback should not be called with empty buffer');
|
|
136
|
+
|
|
137
|
+
// Clean up
|
|
138
|
+
delete requestState[plugin.requestId];
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
test('OpenAIVisionPlugin - filters invalid tool calls', async t => {
|
|
142
|
+
const resolver = createResolverWithPlugin(OpenAIVisionPlugin);
|
|
143
|
+
const plugin = resolver.modelExecutor.plugin;
|
|
144
|
+
|
|
145
|
+
// Create buffer with mixed valid and invalid tool calls
|
|
146
|
+
plugin.toolCallsBuffer[0] = undefined;
|
|
147
|
+
plugin.toolCallsBuffer[1] = {
|
|
148
|
+
id: 'call_1_1234567890',
|
|
149
|
+
type: 'function',
|
|
150
|
+
function: {
|
|
151
|
+
name: 'valid_tool',
|
|
152
|
+
arguments: '{"param": "value"}'
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
plugin.toolCallsBuffer[2] = {
|
|
156
|
+
id: 'call_2_1234567890',
|
|
157
|
+
type: 'function',
|
|
158
|
+
function: {
|
|
159
|
+
name: '', // Invalid: empty name
|
|
160
|
+
arguments: '{"param": "value"}'
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
plugin.toolCallsBuffer[3] = {
|
|
164
|
+
id: 'call_3_1234567890',
|
|
165
|
+
type: 'function',
|
|
166
|
+
function: {
|
|
167
|
+
name: 'another_valid_tool',
|
|
168
|
+
arguments: '{"param": "value"}'
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
// Mock the tool callback
|
|
173
|
+
let capturedToolCalls = null;
|
|
174
|
+
plugin.pathwayToolCallback = (args, message, resolver) => {
|
|
175
|
+
capturedToolCalls = message.tool_calls;
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
// Mock requestProgress and pathwayResolver
|
|
179
|
+
const requestProgress = { progress: 0, started: true };
|
|
180
|
+
const pathwayResolver = { args: {} };
|
|
181
|
+
|
|
182
|
+
// Mock requestState
|
|
183
|
+
requestState[plugin.requestId] = { pathwayResolver };
|
|
184
|
+
|
|
185
|
+
// Simulate a tool_calls finish reason
|
|
186
|
+
const event = {
|
|
187
|
+
data: JSON.stringify({
|
|
188
|
+
choices: [{
|
|
189
|
+
finish_reason: 'tool_calls'
|
|
190
|
+
}]
|
|
191
|
+
})
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
// Process the stream event
|
|
195
|
+
plugin.processStreamEvent(event, requestProgress);
|
|
196
|
+
|
|
197
|
+
// Verify that only valid tool calls were passed
|
|
198
|
+
t.truthy(capturedToolCalls, 'Tool callback should have been called');
|
|
199
|
+
t.is(capturedToolCalls.length, 2, 'Should have filtered out invalid elements');
|
|
200
|
+
t.is(capturedToolCalls[0].function.name, 'valid_tool', 'First valid tool call should be preserved');
|
|
201
|
+
t.is(capturedToolCalls[1].function.name, 'another_valid_tool', 'Second valid tool call should be preserved');
|
|
202
|
+
|
|
203
|
+
// Clean up
|
|
204
|
+
delete requestState[plugin.requestId];
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
test('Gemini15VisionPlugin - filters undefined tool calls from buffer', async t => {
|
|
208
|
+
const resolver = createResolverWithPlugin(Gemini15VisionPlugin);
|
|
209
|
+
const plugin = resolver.modelExecutor.plugin;
|
|
210
|
+
|
|
211
|
+
// Simulate buffer with undefined elements (though less likely with push method)
|
|
212
|
+
plugin.toolCallsBuffer = [
|
|
213
|
+
undefined,
|
|
214
|
+
{
|
|
215
|
+
id: 'call_1_1234567890',
|
|
216
|
+
type: 'function',
|
|
217
|
+
function: {
|
|
218
|
+
name: 'test_tool',
|
|
219
|
+
arguments: '{"param": "value"}'
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
];
|
|
223
|
+
|
|
224
|
+
// Mock the tool callback
|
|
225
|
+
let capturedToolCalls = null;
|
|
226
|
+
plugin.pathwayToolCallback = (args, message, resolver) => {
|
|
227
|
+
capturedToolCalls = message.tool_calls;
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
// Mock requestProgress and pathwayResolver
|
|
231
|
+
const requestProgress = { progress: 0, started: true };
|
|
232
|
+
const pathwayResolver = { args: {} };
|
|
233
|
+
|
|
234
|
+
// Mock requestState
|
|
235
|
+
requestState[plugin.requestId] = { pathwayResolver };
|
|
236
|
+
|
|
237
|
+
// Simulate a tool_calls finish reason
|
|
238
|
+
const eventData = {
|
|
239
|
+
candidates: [{
|
|
240
|
+
finishReason: 'STOP'
|
|
241
|
+
}]
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
// Set hadToolCalls to true to trigger tool_calls finish reason
|
|
245
|
+
plugin.hadToolCalls = true;
|
|
246
|
+
|
|
247
|
+
// Process the stream event
|
|
248
|
+
plugin.processStreamEvent({ data: JSON.stringify(eventData) }, requestProgress);
|
|
249
|
+
|
|
250
|
+
// Verify that undefined elements were filtered out
|
|
251
|
+
t.truthy(capturedToolCalls, 'Tool callback should have been called');
|
|
252
|
+
t.is(capturedToolCalls.length, 1, 'Should have filtered out undefined elements');
|
|
253
|
+
t.is(capturedToolCalls[0].function.name, 'test_tool', 'Valid tool call should be preserved');
|
|
254
|
+
|
|
255
|
+
// Clean up
|
|
256
|
+
delete requestState[plugin.requestId];
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
test('Gemini15VisionPlugin - handles empty buffer gracefully', async t => {
|
|
260
|
+
const resolver = createResolverWithPlugin(Gemini15VisionPlugin);
|
|
261
|
+
const plugin = resolver.modelExecutor.plugin;
|
|
262
|
+
|
|
263
|
+
// Empty buffer
|
|
264
|
+
plugin.toolCallsBuffer = [];
|
|
265
|
+
|
|
266
|
+
// Mock the tool callback
|
|
267
|
+
let callbackCalled = false;
|
|
268
|
+
plugin.pathwayToolCallback = () => {
|
|
269
|
+
callbackCalled = true;
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
// Mock requestProgress and pathwayResolver
|
|
273
|
+
const requestProgress = { progress: 0, started: true };
|
|
274
|
+
const pathwayResolver = { args: {} };
|
|
275
|
+
|
|
276
|
+
// Mock requestState
|
|
277
|
+
requestState[plugin.requestId] = { pathwayResolver };
|
|
278
|
+
|
|
279
|
+
// Simulate a tool_calls finish reason
|
|
280
|
+
const eventData = {
|
|
281
|
+
candidates: [{
|
|
282
|
+
finishReason: 'STOP'
|
|
283
|
+
}]
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
// Set hadToolCalls to true
|
|
287
|
+
plugin.hadToolCalls = true;
|
|
288
|
+
|
|
289
|
+
// Process the stream event
|
|
290
|
+
plugin.processStreamEvent({ data: JSON.stringify(eventData) }, requestProgress);
|
|
291
|
+
|
|
292
|
+
// Verify that callback was not called with empty buffer
|
|
293
|
+
t.falsy(callbackCalled, 'Tool callback should not be called with empty buffer');
|
|
294
|
+
|
|
295
|
+
// Clean up
|
|
296
|
+
delete requestState[plugin.requestId];
|
|
297
|
+
});
|