illuma-agents 1.0.9 → 1.0.11
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/LICENSE +1 -1
- package/dist/cjs/agents/AgentContext.cjs +228 -27
- package/dist/cjs/agents/AgentContext.cjs.map +1 -1
- package/dist/cjs/common/enum.cjs +2 -0
- package/dist/cjs/common/enum.cjs.map +1 -1
- package/dist/cjs/events.cjs +3 -0
- package/dist/cjs/events.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +29 -19
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/instrumentation.cjs +1 -1
- package/dist/cjs/instrumentation.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/index.cjs +1 -1
- package/dist/cjs/llm/anthropic/index.cjs.map +1 -1
- package/dist/cjs/llm/bedrock/index.cjs +122 -7
- package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
- package/dist/cjs/llm/google/index.cjs +1 -1
- package/dist/cjs/llm/google/index.cjs.map +1 -1
- package/dist/cjs/llm/openai/index.cjs +108 -6
- package/dist/cjs/llm/openai/index.cjs.map +1 -1
- package/dist/cjs/llm/openai/utils/index.cjs +87 -1
- package/dist/cjs/llm/openai/utils/index.cjs.map +1 -1
- package/dist/cjs/llm/openrouter/index.cjs +176 -2
- package/dist/cjs/llm/openrouter/index.cjs.map +1 -1
- package/dist/cjs/main.cjs +18 -0
- package/dist/cjs/main.cjs.map +1 -1
- package/dist/cjs/messages/cache.cjs +149 -54
- package/dist/cjs/messages/cache.cjs.map +1 -1
- package/dist/cjs/messages/tools.cjs +85 -0
- package/dist/cjs/messages/tools.cjs.map +1 -0
- package/dist/cjs/stream.cjs +20 -0
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/cjs/tools/CodeExecutor.cjs +4 -0
- package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs +438 -0
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -0
- package/dist/cjs/tools/ToolNode.cjs +54 -6
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/ToolSearchRegex.cjs +455 -0
- package/dist/cjs/tools/ToolSearchRegex.cjs.map +1 -0
- package/dist/cjs/tools/search/tool.cjs +21 -1
- package/dist/cjs/tools/search/tool.cjs.map +1 -1
- package/dist/cjs/utils/run.cjs +5 -1
- package/dist/cjs/utils/run.cjs.map +1 -1
- package/dist/esm/agents/AgentContext.mjs +228 -27
- package/dist/esm/agents/AgentContext.mjs.map +1 -1
- package/dist/esm/common/enum.mjs +2 -0
- package/dist/esm/common/enum.mjs.map +1 -1
- package/dist/esm/events.mjs +4 -1
- package/dist/esm/events.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +29 -19
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/instrumentation.mjs +1 -1
- package/dist/esm/instrumentation.mjs.map +1 -1
- package/dist/esm/llm/anthropic/index.mjs +1 -1
- package/dist/esm/llm/anthropic/index.mjs.map +1 -1
- package/dist/esm/llm/bedrock/index.mjs +122 -7
- package/dist/esm/llm/bedrock/index.mjs.map +1 -1
- package/dist/esm/llm/google/index.mjs +1 -1
- package/dist/esm/llm/google/index.mjs.map +1 -1
- package/dist/esm/llm/openai/index.mjs +109 -7
- package/dist/esm/llm/openai/index.mjs.map +1 -1
- package/dist/esm/llm/openai/utils/index.mjs +88 -2
- package/dist/esm/llm/openai/utils/index.mjs.map +1 -1
- package/dist/esm/llm/openrouter/index.mjs +176 -2
- package/dist/esm/llm/openrouter/index.mjs.map +1 -1
- package/dist/esm/main.mjs +3 -0
- package/dist/esm/main.mjs.map +1 -1
- package/dist/esm/messages/cache.mjs +149 -54
- package/dist/esm/messages/cache.mjs.map +1 -1
- package/dist/esm/messages/tools.mjs +82 -0
- package/dist/esm/messages/tools.mjs.map +1 -0
- package/dist/esm/stream.mjs +20 -0
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/esm/tools/CodeExecutor.mjs +4 -0
- package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
- package/dist/esm/tools/ProgrammaticToolCalling.mjs +430 -0
- package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -0
- package/dist/esm/tools/ToolNode.mjs +54 -6
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/ToolSearchRegex.mjs +448 -0
- package/dist/esm/tools/ToolSearchRegex.mjs.map +1 -0
- package/dist/esm/tools/search/tool.mjs +21 -1
- package/dist/esm/tools/search/tool.mjs.map +1 -1
- package/dist/esm/utils/run.mjs +5 -1
- package/dist/esm/utils/run.mjs.map +1 -1
- package/dist/types/agents/AgentContext.d.ts +65 -5
- package/dist/types/common/enum.d.ts +2 -0
- package/dist/types/graphs/Graph.d.ts +3 -2
- package/dist/types/index.d.ts +2 -0
- package/dist/types/llm/anthropic/index.d.ts +1 -1
- package/dist/types/llm/bedrock/index.d.ts +31 -4
- package/dist/types/llm/google/index.d.ts +1 -1
- package/dist/types/llm/openai/index.d.ts +4 -3
- package/dist/types/llm/openai/utils/index.d.ts +10 -1
- package/dist/types/llm/openrouter/index.d.ts +5 -2
- package/dist/types/messages/cache.d.ts +23 -8
- package/dist/types/messages/index.d.ts +1 -0
- package/dist/types/messages/tools.d.ts +17 -0
- package/dist/types/test/mockTools.d.ts +28 -0
- package/dist/types/tools/ProgrammaticToolCalling.d.ts +91 -0
- package/dist/types/tools/ToolNode.d.ts +10 -2
- package/dist/types/tools/ToolSearchRegex.d.ts +80 -0
- package/dist/types/types/graph.d.ts +7 -1
- package/dist/types/types/tools.d.ts +138 -0
- package/package.json +8 -3
- package/src/agents/AgentContext.ts +267 -27
- package/src/agents/__tests__/AgentContext.test.ts +805 -0
- package/src/common/enum.ts +2 -0
- package/src/events.ts +5 -1
- package/src/graphs/Graph.ts +35 -20
- package/src/index.ts +2 -0
- package/src/instrumentation.ts +1 -1
- package/src/llm/anthropic/index.ts +2 -2
- package/src/llm/bedrock/__tests__/bedrock-caching.test.ts +473 -0
- package/src/llm/bedrock/index.ts +150 -13
- package/src/llm/google/index.ts +2 -2
- package/src/llm/google/llm.spec.ts +3 -1
- package/src/llm/openai/index.ts +135 -9
- package/src/llm/openai/utils/index.ts +116 -1
- package/src/llm/openrouter/index.ts +224 -3
- package/src/messages/__tests__/tools.test.ts +473 -0
- package/src/messages/cache.ts +163 -61
- package/src/messages/index.ts +1 -0
- package/src/messages/tools.ts +99 -0
- package/src/scripts/code_exec_ptc.ts +334 -0
- package/src/scripts/programmatic_exec.ts +396 -0
- package/src/scripts/programmatic_exec_agent.ts +231 -0
- package/src/scripts/tool_search_regex.ts +162 -0
- package/src/specs/thinking-prune.test.ts +52 -118
- package/src/stream.ts +26 -0
- package/src/test/mockTools.ts +366 -0
- package/src/tools/CodeExecutor.ts +4 -0
- package/src/tools/ProgrammaticToolCalling.ts +558 -0
- package/src/tools/ToolNode.ts +60 -7
- package/src/tools/ToolSearchRegex.ts +535 -0
- package/src/tools/__tests__/ProgrammaticToolCalling.integration.test.ts +318 -0
- package/src/tools/__tests__/ProgrammaticToolCalling.test.ts +853 -0
- package/src/tools/__tests__/ToolSearchRegex.integration.test.ts +161 -0
- package/src/tools/__tests__/ToolSearchRegex.test.ts +232 -0
- package/src/tools/search/jina-reranker.test.ts +16 -16
- package/src/tools/search/tool.ts +23 -1
- package/src/types/graph.ts +7 -1
- package/src/types/tools.ts +166 -0
- package/src/utils/llmConfig.ts +8 -2
- package/src/utils/run.ts +5 -1
- package/src/tools/search/direct-url.test.ts +0 -530
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
// src/scripts/tool_search_regex.ts
|
|
2
|
+
/**
|
|
3
|
+
* Test script for the Tool Search Regex tool.
|
|
4
|
+
* Run with: npm run tool_search_regex
|
|
5
|
+
*
|
|
6
|
+
* Demonstrates runtime registry injection - the tool registry is passed
|
|
7
|
+
* at invocation time, not at initialization time.
|
|
8
|
+
*/
|
|
9
|
+
import { config } from 'dotenv';
|
|
10
|
+
config();
|
|
11
|
+
|
|
12
|
+
import { createToolSearchRegexTool } from '@/tools/ToolSearchRegex';
|
|
13
|
+
import type { LCToolRegistry } from '@/types';
|
|
14
|
+
import { createToolSearchToolRegistry } from '@/test/mockTools';
|
|
15
|
+
|
|
16
|
+
interface RunTestOptions {
|
|
17
|
+
fields?: ('name' | 'description' | 'parameters')[];
|
|
18
|
+
max_results?: number;
|
|
19
|
+
showArtifact?: boolean;
|
|
20
|
+
toolRegistry: LCToolRegistry;
|
|
21
|
+
onlyDeferred?: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function runTest(
|
|
25
|
+
searchTool: ReturnType<typeof createToolSearchRegexTool>,
|
|
26
|
+
testName: string,
|
|
27
|
+
query: string,
|
|
28
|
+
options: RunTestOptions
|
|
29
|
+
): Promise<void> {
|
|
30
|
+
console.log(`\n${'='.repeat(60)}`);
|
|
31
|
+
console.log(`TEST: ${testName}`);
|
|
32
|
+
console.log(`Query: "${query}"`);
|
|
33
|
+
if (options.fields) console.log(`Fields: ${options.fields.join(', ')}`);
|
|
34
|
+
if (options.max_results) console.log(`Max Results: ${options.max_results}`);
|
|
35
|
+
console.log('='.repeat(60));
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
const startTime = Date.now();
|
|
39
|
+
|
|
40
|
+
// Manual testing uses schema params directly
|
|
41
|
+
// (ToolNode uses different param structure when injecting)
|
|
42
|
+
const result = await searchTool.invoke({
|
|
43
|
+
query,
|
|
44
|
+
fields: options.fields,
|
|
45
|
+
max_results: options.max_results,
|
|
46
|
+
});
|
|
47
|
+
const duration = Date.now() - startTime;
|
|
48
|
+
|
|
49
|
+
console.log(`\nResult (${duration}ms):`);
|
|
50
|
+
if (Array.isArray(result)) {
|
|
51
|
+
console.log(result[0]);
|
|
52
|
+
if (options.showArtifact) {
|
|
53
|
+
console.log('\n--- Artifact ---');
|
|
54
|
+
console.dir(result[1], { depth: null });
|
|
55
|
+
}
|
|
56
|
+
} else {
|
|
57
|
+
console.log(result);
|
|
58
|
+
}
|
|
59
|
+
} catch (error) {
|
|
60
|
+
console.error('Error:', error instanceof Error ? error.message : error);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function main(): Promise<void> {
|
|
65
|
+
console.log('Tool Search Regex - Test Script');
|
|
66
|
+
console.log('================================');
|
|
67
|
+
console.log('Demonstrating runtime tool registry injection\n');
|
|
68
|
+
|
|
69
|
+
const apiKey = process.env.LIBRECHAT_CODE_API_KEY;
|
|
70
|
+
if (!apiKey) {
|
|
71
|
+
console.error(
|
|
72
|
+
'Error: LIBRECHAT_CODE_API_KEY environment variable is not set.'
|
|
73
|
+
);
|
|
74
|
+
console.log('Please set it in your .env file or environment.');
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
console.log('Creating sample tool registry...');
|
|
79
|
+
const toolRegistry = createToolSearchToolRegistry();
|
|
80
|
+
console.log(
|
|
81
|
+
`Registry contains ${toolRegistry.size} tools (${Array.from(toolRegistry.values()).filter((t) => t.defer_loading).length} deferred)`
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
console.log('\nCreating Tool Search Regex tool WITH registry for testing...');
|
|
85
|
+
const searchTool = createToolSearchRegexTool({ apiKey, toolRegistry });
|
|
86
|
+
console.log('Tool created successfully!');
|
|
87
|
+
console.log(
|
|
88
|
+
'Note: In production, ToolNode injects toolRegistry via params when invoked through the graph.\n'
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
const baseOptions = { toolRegistry, onlyDeferred: true };
|
|
92
|
+
|
|
93
|
+
// Test 1: Simple keyword search (with artifact display)
|
|
94
|
+
await runTest(searchTool, 'Simple keyword search', 'expense', {
|
|
95
|
+
...baseOptions,
|
|
96
|
+
showArtifact: true,
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// Test 2: Search for weather-related tools
|
|
100
|
+
await runTest(searchTool, 'Weather tools', 'weather|forecast', baseOptions);
|
|
101
|
+
|
|
102
|
+
// Test 3: Search with case variations
|
|
103
|
+
await runTest(searchTool, 'Case insensitive search', 'EMAIL', baseOptions);
|
|
104
|
+
|
|
105
|
+
// Test 4: Search in description only
|
|
106
|
+
await runTest(searchTool, 'Description-only search', 'database', {
|
|
107
|
+
...baseOptions,
|
|
108
|
+
fields: ['description'],
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// Test 5: Search with parameters field
|
|
112
|
+
await runTest(searchTool, 'Parameters search', 'query', {
|
|
113
|
+
...baseOptions,
|
|
114
|
+
fields: ['parameters'],
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// Test 6: Limited results
|
|
118
|
+
await runTest(searchTool, 'Limited to 2 results', 'get', {
|
|
119
|
+
...baseOptions,
|
|
120
|
+
max_results: 2,
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// Test 7: Pattern that matches nothing
|
|
124
|
+
await runTest(searchTool, 'No matches', 'xyznonexistent123', baseOptions);
|
|
125
|
+
|
|
126
|
+
// Test 8: Regex pattern with character class
|
|
127
|
+
await runTest(
|
|
128
|
+
searchTool,
|
|
129
|
+
'Regex with character class',
|
|
130
|
+
'get_[a-z]+',
|
|
131
|
+
baseOptions
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
// Test 9: Dangerous pattern (should be sanitized)
|
|
135
|
+
await runTest(
|
|
136
|
+
searchTool,
|
|
137
|
+
'Dangerous pattern (sanitized)',
|
|
138
|
+
'(a+)+',
|
|
139
|
+
baseOptions
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
// Test 10: Search all fields
|
|
143
|
+
await runTest(searchTool, 'All fields search', 'text', {
|
|
144
|
+
...baseOptions,
|
|
145
|
+
fields: ['name', 'description', 'parameters'],
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// Test 11: Search ALL tools (not just deferred)
|
|
149
|
+
await runTest(searchTool, 'Search ALL tools (incl. non-deferred)', 'calc', {
|
|
150
|
+
toolRegistry,
|
|
151
|
+
onlyDeferred: false, // Include non-deferred tools
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
console.log('\n' + '='.repeat(60));
|
|
155
|
+
console.log('All tests completed!');
|
|
156
|
+
console.log('='.repeat(60) + '\n');
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
main().catch((err) => {
|
|
160
|
+
console.error('Fatal error:', err);
|
|
161
|
+
process.exit(1);
|
|
162
|
+
});
|
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
// src/specs/thinking-prune.test.ts
|
|
2
|
-
import {
|
|
3
|
-
HumanMessage,
|
|
4
|
-
AIMessage,
|
|
5
|
-
SystemMessage,
|
|
6
|
-
BaseMessage,
|
|
7
|
-
ToolMessage,
|
|
8
|
-
} from '@langchain/core/messages';
|
|
2
|
+
import { HumanMessage, AIMessage, SystemMessage, BaseMessage, ToolMessage } from '@langchain/core/messages';
|
|
9
3
|
import type * as t from '@/types';
|
|
10
4
|
import { createPruneMessages } from '@/messages/prune';
|
|
11
5
|
|
|
@@ -13,10 +7,7 @@ import { createPruneMessages } from '@/messages/prune';
|
|
|
13
7
|
const createTestTokenCounter = (): t.TokenCounter => {
|
|
14
8
|
return (message: BaseMessage): number => {
|
|
15
9
|
// Use type assertion to help TypeScript understand the type
|
|
16
|
-
const content = message.content as
|
|
17
|
-
| string
|
|
18
|
-
| Array<t.MessageContentComplex | string>
|
|
19
|
-
| undefined;
|
|
10
|
+
const content = message.content as string | Array<t.MessageContentComplex | string> | undefined;
|
|
20
11
|
|
|
21
12
|
// Handle string content
|
|
22
13
|
if (typeof content === 'string') {
|
|
@@ -61,7 +52,7 @@ describe('Prune Messages with Thinking Mode Tests', () => {
|
|
|
61
52
|
content: [
|
|
62
53
|
{
|
|
63
54
|
type: 'text',
|
|
64
|
-
text: '/home/danny/
|
|
55
|
+
text: '/home/danny/LibreChat/gistfile1.txt\n\nread it 200 lines at a time\n\nthere are 5000 lines\n\ndo not stop until done',
|
|
65
56
|
},
|
|
66
57
|
],
|
|
67
58
|
});
|
|
@@ -70,8 +61,7 @@ describe('Prune Messages with Thinking Mode Tests', () => {
|
|
|
70
61
|
content: [
|
|
71
62
|
{
|
|
72
63
|
type: 'thinking',
|
|
73
|
-
thinking:
|
|
74
|
-
'The user is asking me to read a file located at `/home/danny/Illuma/gistfile1.txt` in chunks of 200 lines at a time, mentioning that the file has 5000 lines total. They want me to continue reading through the entire file without stopping.\n\nI\'ll need to use the text editor tool to view the file in chunks of 200 lines each. Since the file has 5000 lines, I\'ll need to view it in 25 chunks (5000 ÷ 200 = 25).\n\nI\'ll need to make multiple calls to the text editor with the `view` command, specifying different line ranges for each call.\n\nLet me plan out the approach:\n1. Start with lines 1-200\n2. Then 201-400\n3. Then 401-600\n4. And so on until I reach 4801-5000\n\nFor each call, I\'ll use the `view` command with the specific line range in the `view_range` parameter. I\'ll continue until I\'ve shown all 5000 lines as requested.',
|
|
64
|
+
thinking: 'The user is asking me to read a file located at `/home/danny/LibreChat/gistfile1.txt` in chunks of 200 lines at a time, mentioning that the file has 5000 lines total. They want me to continue reading through the entire file without stopping.\n\nI\'ll need to use the text editor tool to view the file in chunks of 200 lines each. Since the file has 5000 lines, I\'ll need to view it in 25 chunks (5000 ÷ 200 = 25).\n\nI\'ll need to make multiple calls to the text editor with the `view` command, specifying different line ranges for each call.\n\nLet me plan out the approach:\n1. Start with lines 1-200\n2. Then 201-400\n3. Then 401-600\n4. And so on until I reach 4801-5000\n\nFor each call, I\'ll use the `view` command with the specific line range in the `view_range` parameter. I\'ll continue until I\'ve shown all 5000 lines as requested.',
|
|
75
65
|
},
|
|
76
66
|
{
|
|
77
67
|
type: 'text',
|
|
@@ -81,8 +71,7 @@ describe('Prune Messages with Thinking Mode Tests', () => {
|
|
|
81
71
|
type: 'tool_use',
|
|
82
72
|
id: 'toolu_01YApWuFsEQCuBFDgYXmcmeZ',
|
|
83
73
|
name: 'text_editor_mcp_textEditor',
|
|
84
|
-
input:
|
|
85
|
-
'{"command": "view", "path": "/home/danny/Illuma/gistfile1.txt", "description": "Viewing lines 1-200 of the file", "view_range": [1,200]}',
|
|
74
|
+
input: '{"command": "view", "path": "/home/danny/LibreChat/gistfile1.txt", "description": "Viewing lines 1-200 of the file", "view_range": [1,200]}',
|
|
86
75
|
},
|
|
87
76
|
],
|
|
88
77
|
});
|
|
@@ -108,8 +97,7 @@ describe('Prune Messages with Thinking Mode Tests', () => {
|
|
|
108
97
|
type: 'tool_use',
|
|
109
98
|
id: 'toolu_01VnyMQ4CvEd6zLDxxtTd6d4',
|
|
110
99
|
name: 'text_editor_mcp_textEditor',
|
|
111
|
-
input:
|
|
112
|
-
'{"command": "view", "path": "/home/danny/Illuma/gistfile1.txt", "description": "Viewing lines 201-400 of the file", "view_range": [201,400]}',
|
|
100
|
+
input: '{"command": "view", "path": "/home/danny/LibreChat/gistfile1.txt", "description": "Viewing lines 201-400 of the file", "view_range": [201,400]}',
|
|
113
101
|
},
|
|
114
102
|
],
|
|
115
103
|
});
|
|
@@ -135,8 +123,7 @@ describe('Prune Messages with Thinking Mode Tests', () => {
|
|
|
135
123
|
type: 'tool_use',
|
|
136
124
|
id: 'toolu_01TZKs4nnBc58BYXKz1Mw4fp',
|
|
137
125
|
name: 'text_editor_mcp_textEditor',
|
|
138
|
-
input:
|
|
139
|
-
'{"command": "view", "path": "/home/danny/Illuma/gistfile1.txt", "description": "Viewing lines 401-600 of the file", "view_range": [401,600]}',
|
|
126
|
+
input: '{"command": "view", "path": "/home/danny/LibreChat/gistfile1.txt", "description": "Viewing lines 401-600 of the file", "view_range": [401,600]}',
|
|
140
127
|
},
|
|
141
128
|
],
|
|
142
129
|
});
|
|
@@ -162,8 +149,7 @@ describe('Prune Messages with Thinking Mode Tests', () => {
|
|
|
162
149
|
type: 'tool_use',
|
|
163
150
|
id: 'toolu_01TZgBacNxjx1QNUpJg9hca5',
|
|
164
151
|
name: 'text_editor_mcp_textEditor',
|
|
165
|
-
input:
|
|
166
|
-
'{"command": "view", "path": "/home/danny/Illuma/gistfile1.txt", "description": "Viewing lines 601-800 of the file", "view_range": [601,800]}',
|
|
152
|
+
input: '{"command": "view", "path": "/home/danny/LibreChat/gistfile1.txt", "description": "Viewing lines 601-800 of the file", "view_range": [601,800]}',
|
|
167
153
|
},
|
|
168
154
|
],
|
|
169
155
|
});
|
|
@@ -194,7 +180,7 @@ describe('Prune Messages with Thinking Mode Tests', () => {
|
|
|
194
180
|
// Create indexTokenCountMap based on the example provided
|
|
195
181
|
const indexTokenCountMap = {
|
|
196
182
|
'0': 617, // userMessage
|
|
197
|
-
'1': 52,
|
|
183
|
+
'1': 52, // assistantMessageWithThinking
|
|
198
184
|
'2': 4995, // toolResponseMessage1
|
|
199
185
|
'3': 307, // assistantMessage2
|
|
200
186
|
'4': 9359, // toolResponseMessage2
|
|
@@ -232,34 +218,22 @@ describe('Prune Messages with Thinking Mode Tests', () => {
|
|
|
232
218
|
expect(result.context.length).toBeGreaterThan(0);
|
|
233
219
|
|
|
234
220
|
// Find the first assistant message in the pruned context
|
|
235
|
-
const firstAssistantIndex = result.context.findIndex(
|
|
236
|
-
(msg) => msg.getType() === 'ai'
|
|
237
|
-
);
|
|
221
|
+
const firstAssistantIndex = result.context.findIndex(msg => msg.getType() === 'ai');
|
|
238
222
|
expect(firstAssistantIndex).toBe(0);
|
|
239
223
|
|
|
240
224
|
const firstAssistantMsg = result.context[firstAssistantIndex];
|
|
241
225
|
expect(Array.isArray(firstAssistantMsg.content)).toBe(true);
|
|
242
226
|
|
|
243
227
|
// Verify that the first assistant message has a thinking block
|
|
244
|
-
const hasThinkingBlock = (
|
|
245
|
-
|
|
246
|
-
).some(
|
|
247
|
-
(item: t.MessageContentComplex) =>
|
|
248
|
-
typeof item === 'object' && item.type === 'thinking'
|
|
249
|
-
);
|
|
228
|
+
const hasThinkingBlock = (firstAssistantMsg.content as t.MessageContentComplex[]).some((item: t.MessageContentComplex) =>
|
|
229
|
+
typeof item === 'object' && item.type === 'thinking');
|
|
250
230
|
expect(hasThinkingBlock).toBe(true);
|
|
251
231
|
|
|
252
232
|
// Verify that the thinking block is from the original assistant message
|
|
253
|
-
const thinkingBlock = (
|
|
254
|
-
|
|
255
|
-
).find(
|
|
256
|
-
(item: t.MessageContentComplex) =>
|
|
257
|
-
typeof item === 'object' && item.type === 'thinking'
|
|
258
|
-
);
|
|
233
|
+
const thinkingBlock = (firstAssistantMsg.content as t.MessageContentComplex[]).find((item: t.MessageContentComplex) =>
|
|
234
|
+
typeof item === 'object' && item.type === 'thinking');
|
|
259
235
|
expect(thinkingBlock).toBeDefined();
|
|
260
|
-
expect((thinkingBlock as t.ThinkingContentText).thinking).toContain(
|
|
261
|
-
'The user is asking me to read a file'
|
|
262
|
-
);
|
|
236
|
+
expect((thinkingBlock as t.ThinkingContentText).thinking).toContain('The user is asking me to read a file');
|
|
263
237
|
});
|
|
264
238
|
|
|
265
239
|
it('should handle token recalculation when inserting thinking blocks', () => {
|
|
@@ -477,49 +451,35 @@ describe('Prune Messages with Thinking Mode Tests', () => {
|
|
|
477
451
|
const result = pruneMessages({ messages });
|
|
478
452
|
|
|
479
453
|
// Find assistant message with tool call and its corresponding tool message in the pruned context
|
|
480
|
-
const assistantIndex = result.context.findIndex(
|
|
481
|
-
(
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
msg.content.some(
|
|
485
|
-
(item) =>
|
|
486
|
-
typeof item === 'object' &&
|
|
487
|
-
item.type === 'tool_use' &&
|
|
488
|
-
item.id === 'tool123'
|
|
489
|
-
)
|
|
454
|
+
const assistantIndex = result.context.findIndex(msg =>
|
|
455
|
+
msg.getType() === 'ai' &&
|
|
456
|
+
Array.isArray(msg.content) &&
|
|
457
|
+
msg.content.some(item => typeof item === 'object' && item.type === 'tool_use' && item.id === 'tool123')
|
|
490
458
|
);
|
|
491
459
|
|
|
492
460
|
// If the assistant message with tool call is in the context, its corresponding tool message should also be there
|
|
493
461
|
if (assistantIndex !== -1) {
|
|
494
|
-
const toolIndex = result.context.findIndex(
|
|
495
|
-
(
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
msg.tool_call_id === 'tool123'
|
|
462
|
+
const toolIndex = result.context.findIndex(msg =>
|
|
463
|
+
msg.getType() === 'tool' &&
|
|
464
|
+
'tool_call_id' in msg &&
|
|
465
|
+
msg.tool_call_id === 'tool123'
|
|
499
466
|
);
|
|
500
467
|
|
|
501
468
|
expect(toolIndex).not.toBe(-1);
|
|
502
469
|
}
|
|
503
470
|
|
|
504
471
|
// If the tool message is in the context, its corresponding assistant message should also be there
|
|
505
|
-
const toolIndex = result.context.findIndex(
|
|
506
|
-
(
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
msg.tool_call_id === 'tool123'
|
|
472
|
+
const toolIndex = result.context.findIndex(msg =>
|
|
473
|
+
msg.getType() === 'tool' &&
|
|
474
|
+
'tool_call_id' in msg &&
|
|
475
|
+
msg.tool_call_id === 'tool123'
|
|
510
476
|
);
|
|
511
477
|
|
|
512
478
|
if (toolIndex !== -1) {
|
|
513
|
-
const assistantWithToolIndex = result.context.findIndex(
|
|
514
|
-
(
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
msg.content.some(
|
|
518
|
-
(item) =>
|
|
519
|
-
typeof item === 'object' &&
|
|
520
|
-
item.type === 'tool_use' &&
|
|
521
|
-
item.id === 'tool123'
|
|
522
|
-
)
|
|
479
|
+
const assistantWithToolIndex = result.context.findIndex(msg =>
|
|
480
|
+
msg.getType() === 'ai' &&
|
|
481
|
+
Array.isArray(msg.content) &&
|
|
482
|
+
msg.content.some(item => typeof item === 'object' && item.type === 'tool_use' && item.id === 'tool123')
|
|
523
483
|
);
|
|
524
484
|
|
|
525
485
|
expect(assistantWithToolIndex).not.toBe(-1);
|
|
@@ -584,9 +544,7 @@ describe('Prune Messages with Thinking Mode Tests', () => {
|
|
|
584
544
|
// Calculate token counts for each message
|
|
585
545
|
const indexTokenCountMapWithoutSystem: Record<string, number> = {};
|
|
586
546
|
for (let i = 0; i < messagesWithoutSystem.length; i++) {
|
|
587
|
-
indexTokenCountMapWithoutSystem[i] = tokenCounter(
|
|
588
|
-
messagesWithoutSystem[i]
|
|
589
|
-
);
|
|
547
|
+
indexTokenCountMapWithoutSystem[i] = tokenCounter(messagesWithoutSystem[i]);
|
|
590
548
|
}
|
|
591
549
|
|
|
592
550
|
// Create pruneMessages function with thinking mode enabled
|
|
@@ -599,33 +557,24 @@ describe('Prune Messages with Thinking Mode Tests', () => {
|
|
|
599
557
|
});
|
|
600
558
|
|
|
601
559
|
// Prune messages
|
|
602
|
-
const resultWithoutSystem = pruneMessagesWithoutSystem({
|
|
603
|
-
messages: messagesWithoutSystem,
|
|
604
|
-
});
|
|
560
|
+
const resultWithoutSystem = pruneMessagesWithoutSystem({ messages: messagesWithoutSystem });
|
|
605
561
|
|
|
606
562
|
// Verify that the pruned context contains at least one message
|
|
607
563
|
expect(resultWithoutSystem.context.length).toBeGreaterThan(0);
|
|
608
564
|
|
|
609
565
|
// Find all assistant messages in the latest sequence (after the last human message)
|
|
610
|
-
const lastHumanIndex = resultWithoutSystem.context
|
|
611
|
-
|
|
612
|
-
.
|
|
613
|
-
const assistantMessagesAfterLastHuman = resultWithoutSystem.context
|
|
614
|
-
.slice(lastHumanIndex + 1)
|
|
615
|
-
.filter((msg) => msg.getType() === 'ai');
|
|
566
|
+
const lastHumanIndex = resultWithoutSystem.context.map(msg => msg.getType()).lastIndexOf('human');
|
|
567
|
+
const assistantMessagesAfterLastHuman = resultWithoutSystem.context.slice(lastHumanIndex + 1)
|
|
568
|
+
.filter(msg => msg.getType() === 'ai');
|
|
616
569
|
|
|
617
570
|
// Verify that at least one assistant message exists in the latest sequence
|
|
618
571
|
expect(assistantMessagesAfterLastHuman.length).toBeGreaterThan(0);
|
|
619
572
|
|
|
620
573
|
// Verify that at least one of these assistant messages has a thinking block
|
|
621
|
-
const hasThinkingBlock = assistantMessagesAfterLastHuman.some(
|
|
574
|
+
const hasThinkingBlock = assistantMessagesAfterLastHuman.some(msg => {
|
|
622
575
|
const content = msg.content as t.MessageContentComplex[];
|
|
623
|
-
return (
|
|
624
|
-
|
|
625
|
-
content.some(
|
|
626
|
-
(item) => typeof item === 'object' && item.type === 'thinking'
|
|
627
|
-
)
|
|
628
|
-
);
|
|
576
|
+
return Array.isArray(content) && content.some(item =>
|
|
577
|
+
typeof item === 'object' && item.type === 'thinking');
|
|
629
578
|
});
|
|
630
579
|
expect(hasThinkingBlock).toBe(true);
|
|
631
580
|
|
|
@@ -655,36 +604,26 @@ describe('Prune Messages with Thinking Mode Tests', () => {
|
|
|
655
604
|
});
|
|
656
605
|
|
|
657
606
|
// Prune messages
|
|
658
|
-
const resultWithSystem = pruneMessagesWithSystem({
|
|
659
|
-
messages: messagesWithSystem,
|
|
660
|
-
});
|
|
607
|
+
const resultWithSystem = pruneMessagesWithSystem({ messages: messagesWithSystem });
|
|
661
608
|
|
|
662
609
|
// Verify that the system message remains first
|
|
663
610
|
expect(resultWithSystem.context.length).toBeGreaterThan(1);
|
|
664
611
|
expect(resultWithSystem.context[0].getType()).toBe('system');
|
|
665
612
|
|
|
666
613
|
// Find all assistant messages in the latest sequence (after the last human message)
|
|
667
|
-
const lastHumanIndexWithSystem = resultWithSystem.context
|
|
668
|
-
|
|
669
|
-
.
|
|
670
|
-
const assistantMessagesAfterLastHumanWithSystem = resultWithSystem.context
|
|
671
|
-
.slice(lastHumanIndexWithSystem + 1)
|
|
672
|
-
.filter((msg) => msg.getType() === 'ai');
|
|
614
|
+
const lastHumanIndexWithSystem = resultWithSystem.context.map(msg => msg.getType()).lastIndexOf('human');
|
|
615
|
+
const assistantMessagesAfterLastHumanWithSystem = resultWithSystem.context.slice(lastHumanIndexWithSystem + 1)
|
|
616
|
+
.filter(msg => msg.getType() === 'ai');
|
|
673
617
|
|
|
674
618
|
// Verify that at least one assistant message exists in the latest sequence
|
|
675
619
|
expect(assistantMessagesAfterLastHumanWithSystem.length).toBeGreaterThan(0);
|
|
676
620
|
|
|
677
621
|
// Verify that at least one of these assistant messages has a thinking block
|
|
678
|
-
const hasThinkingBlockWithSystem =
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
content.some(
|
|
684
|
-
(item) => typeof item === 'object' && item.type === 'thinking'
|
|
685
|
-
)
|
|
686
|
-
);
|
|
687
|
-
});
|
|
622
|
+
const hasThinkingBlockWithSystem = assistantMessagesAfterLastHumanWithSystem.some(msg => {
|
|
623
|
+
const content = msg.content as t.MessageContentComplex[];
|
|
624
|
+
return Array.isArray(content) && content.some(item =>
|
|
625
|
+
typeof item === 'object' && item.type === 'thinking');
|
|
626
|
+
});
|
|
688
627
|
expect(hasThinkingBlockWithSystem).toBe(true);
|
|
689
628
|
});
|
|
690
629
|
|
|
@@ -747,23 +686,18 @@ describe('Prune Messages with Thinking Mode Tests', () => {
|
|
|
747
686
|
const result = pruneMessages({ messages });
|
|
748
687
|
|
|
749
688
|
// Find the first assistant message in the pruned context
|
|
750
|
-
const firstAssistantIndex = result.context.findIndex(
|
|
751
|
-
(msg) => msg.getType() === 'ai'
|
|
752
|
-
);
|
|
689
|
+
const firstAssistantIndex = result.context.findIndex(msg => msg.getType() === 'ai');
|
|
753
690
|
expect(firstAssistantIndex).not.toBe(-1);
|
|
754
691
|
|
|
755
692
|
const firstAssistantMsg = result.context[firstAssistantIndex];
|
|
756
693
|
expect(Array.isArray(firstAssistantMsg.content)).toBe(true);
|
|
757
694
|
|
|
758
695
|
// Verify that the first assistant message has a thinking block
|
|
759
|
-
const thinkingBlock = (
|
|
760
|
-
|
|
761
|
-
).find((item) => typeof item === 'object' && item.type === 'thinking');
|
|
696
|
+
const thinkingBlock = (firstAssistantMsg.content as t.MessageContentComplex[]).find(item =>
|
|
697
|
+
typeof item === 'object' && item.type === 'thinking');
|
|
762
698
|
expect(thinkingBlock).toBeDefined();
|
|
763
699
|
|
|
764
700
|
// Verify that it's the newer thinking block
|
|
765
|
-
expect((thinkingBlock as t.ThinkingContentText).thinking).toContain(
|
|
766
|
-
'newer thinking block'
|
|
767
|
-
);
|
|
701
|
+
expect((thinkingBlock as t.ThinkingContentText).thinking).toContain('newer thinking block');
|
|
768
702
|
});
|
|
769
703
|
});
|
package/src/stream.ts
CHANGED
|
@@ -107,6 +107,25 @@ export function getChunkContent({
|
|
|
107
107
|
| undefined
|
|
108
108
|
)?.summary?.[0]?.text;
|
|
109
109
|
}
|
|
110
|
+
if (
|
|
111
|
+
provider === Providers.OPENROUTER &&
|
|
112
|
+
chunk?.additional_kwargs?.reasoning_details != null &&
|
|
113
|
+
Array.isArray(chunk.additional_kwargs.reasoning_details)
|
|
114
|
+
) {
|
|
115
|
+
// Extract text from reasoning_details array (for Gemini, DeepSeek, etc.)
|
|
116
|
+
const textEntries = chunk.additional_kwargs.reasoning_details
|
|
117
|
+
.filter(
|
|
118
|
+
(detail) =>
|
|
119
|
+
detail.type === 'reasoning.text' &&
|
|
120
|
+
detail.text != null &&
|
|
121
|
+
detail.text !== ''
|
|
122
|
+
)
|
|
123
|
+
.map((detail) => detail.text)
|
|
124
|
+
.join('');
|
|
125
|
+
if (textEntries) {
|
|
126
|
+
return textEntries;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
110
129
|
return (
|
|
111
130
|
((chunk?.additional_kwargs?.[reasoningKey] as string | undefined) ?? '') ||
|
|
112
131
|
chunk?.content
|
|
@@ -355,6 +374,13 @@ hasToolCallChunks: ${hasToolCallChunks}
|
|
|
355
374
|
reasoning_content.summary[0].text
|
|
356
375
|
) {
|
|
357
376
|
reasoning_content = 'valid';
|
|
377
|
+
} else if (
|
|
378
|
+
agentContext.provider === Providers.OPENROUTER &&
|
|
379
|
+
chunk.additional_kwargs?.reasoning_details != null &&
|
|
380
|
+
Array.isArray(chunk.additional_kwargs.reasoning_details) &&
|
|
381
|
+
chunk.additional_kwargs.reasoning_details.length > 0
|
|
382
|
+
) {
|
|
383
|
+
reasoning_content = 'valid';
|
|
358
384
|
}
|
|
359
385
|
if (
|
|
360
386
|
reasoning_content != null &&
|