llm-advanced-tools 0.1.2 → 0.1.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.
@@ -1,198 +0,0 @@
1
- import { ToolDefinition, SearchConfig } from '../types';
2
- import { SearchStrategy, RegexSearchStrategy, BM25SearchStrategy } from '../search';
3
-
4
- /**
5
- * Registry for managing tool definitions and discovery
6
- */
7
- export class ToolRegistry {
8
- private tools: Map<string, ToolDefinition>;
9
- private deferredTools: Map<string, ToolDefinition>;
10
- private loadedTools: Set<string>;
11
- private searchStrategy: SearchStrategy;
12
-
13
- constructor(searchConfig?: SearchConfig) {
14
- this.tools = new Map();
15
- this.deferredTools = new Map();
16
- this.loadedTools = new Set();
17
-
18
- // Initialize search strategy
19
- if (searchConfig?.customSearchFn) {
20
- this.searchStrategy = {
21
- search: async (query, tools) =>
22
- searchConfig.customSearchFn!(query, tools)
23
- };
24
- } else {
25
- const strategy = searchConfig?.strategy ?? 'smart';
26
- const maxResults = searchConfig?.maxResults ?? 10;
27
- const threshold = searchConfig?.threshold ?? 0.0;
28
-
29
- // Map user-friendly names (and legacy names) to implementations
30
- // 'smart' or 'bm25' → BM25 (best relevance ranking)
31
- // 'keyword' or 'regex' → Regex (fast keyword matching)
32
- const useSmartSearch =
33
- strategy === 'smart' ||
34
- strategy === 'bm25';
35
-
36
- this.searchStrategy = useSmartSearch
37
- ? new BM25SearchStrategy(maxResults, threshold)
38
- : new RegexSearchStrategy(maxResults, threshold);
39
- }
40
- }
41
-
42
- /**
43
- * Register a tool
44
- */
45
- register(tool: ToolDefinition): void {
46
- if (this.tools.has(tool.name)) {
47
- throw new Error(`Tool "${tool.name}" is already registered`);
48
- }
49
-
50
- this.tools.set(tool.name, tool);
51
-
52
- if (tool.deferLoading) {
53
- this.deferredTools.set(tool.name, tool);
54
- } else {
55
- this.loadedTools.add(tool.name);
56
- }
57
- }
58
-
59
- /**
60
- * Register multiple tools at once
61
- */
62
- registerMany(tools: ToolDefinition[]): void {
63
- tools.forEach(tool => this.register(tool));
64
- }
65
-
66
- /**
67
- * Get a specific tool by name
68
- */
69
- get(name: string): ToolDefinition | undefined {
70
- return this.tools.get(name);
71
- }
72
-
73
- /**
74
- * Load a deferred tool into active context
75
- */
76
- load(name: string): ToolDefinition | undefined {
77
- const tool = this.deferredTools.get(name);
78
- if (tool) {
79
- this.loadedTools.add(name);
80
- return tool;
81
- }
82
- return undefined;
83
- }
84
-
85
- /**
86
- * Get all currently loaded tools
87
- */
88
- getLoadedTools(): ToolDefinition[] {
89
- return Array.from(this.loadedTools)
90
- .map(name => this.tools.get(name))
91
- .filter((tool): tool is ToolDefinition => tool !== undefined);
92
- }
93
-
94
- /**
95
- * Get all tools (including deferred)
96
- */
97
- getAllTools(): ToolDefinition[] {
98
- return Array.from(this.tools.values());
99
- }
100
-
101
- /**
102
- * Search for tools matching a query
103
- * @param query Search query
104
- * @param maxResults Maximum results to return
105
- * @param loadResults Whether to automatically load found tools
106
- */
107
- async search(
108
- query: string,
109
- maxResults?: number,
110
- loadResults = true
111
- ): Promise<ToolDefinition[]> {
112
- // Search within deferred tools
113
- const deferredToolsList = Array.from(this.deferredTools.values());
114
- const results = await this.searchStrategy.search(query, deferredToolsList, maxResults);
115
-
116
- // Optionally load the found tools
117
- if (loadResults) {
118
- results.forEach(tool => this.loadedTools.add(tool.name));
119
- }
120
-
121
- return results;
122
- }
123
-
124
- /**
125
- * Clear all loaded tools (keeps registrations)
126
- */
127
- clearLoaded(): void {
128
- this.loadedTools.clear();
129
- // Re-add non-deferred tools
130
- this.tools.forEach((tool, name) => {
131
- if (!tool.deferLoading) {
132
- this.loadedTools.add(name);
133
- }
134
- });
135
- }
136
-
137
- /**
138
- * Unregister a tool
139
- */
140
- unregister(name: string): boolean {
141
- const deleted = this.tools.delete(name);
142
- this.deferredTools.delete(name);
143
- this.loadedTools.delete(name);
144
- return deleted;
145
- }
146
-
147
- /**
148
- * Get statistics about the registry
149
- */
150
- getStats(): {
151
- total: number;
152
- loaded: number;
153
- deferred: number;
154
- } {
155
- return {
156
- total: this.tools.size,
157
- loaded: this.loadedTools.size,
158
- deferred: this.deferredTools.size
159
- };
160
- }
161
-
162
- /**
163
- * Create a tool search tool definition
164
- * This is a meta-tool that allows LLMs to search for other tools
165
- */
166
- createToolSearchTool(): ToolDefinition {
167
- return {
168
- name: 'tool_search',
169
- description:
170
- 'Search for available tools by name or description. Use this when you need to find tools for a specific task.',
171
- inputSchema: {
172
- type: 'object',
173
- properties: {
174
- query: {
175
- type: 'string',
176
- description: 'Search query describing the capability you need'
177
- },
178
- maxResults: {
179
- type: 'number',
180
- description: 'Maximum number of tools to return',
181
- default: 5
182
- }
183
- },
184
- required: ['query']
185
- },
186
- handler: async (input: { query: string; maxResults?: number }) => {
187
- const results = await this.search(input.query, input.maxResults ?? 5, true);
188
- return {
189
- tools: results.map(tool => ({
190
- name: tool.name,
191
- description: tool.description
192
- })),
193
- count: results.length
194
- };
195
- }
196
- };
197
- }
198
- }
@@ -1,122 +0,0 @@
1
- import { ExecutionResult, ExecutorConfig, ToolDefinition, ToolCall } from '../types';
2
-
3
- /**
4
- * Base interface for code executors
5
- */
6
- export interface CodeExecutor {
7
- /**
8
- * Execute code with access to tools
9
- * @param code Code to execute
10
- * @param tools Available tools that can be called from code
11
- * @param context Additional context/variables
12
- */
13
- execute(
14
- code: string,
15
- tools: ToolDefinition[],
16
- context?: Record<string, any>
17
- ): Promise<ExecutionResult>;
18
- }
19
-
20
- /**
21
- * Abstract base class for code executors
22
- */
23
- export abstract class BaseCodeExecutor implements CodeExecutor {
24
- protected config: ExecutorConfig;
25
- protected pendingToolCalls: ToolCall[] = [];
26
-
27
- constructor(config: ExecutorConfig = {}) {
28
- this.config = {
29
- timeout: 30000, // 30 seconds default
30
- memoryLimit: '256mb',
31
- ...config
32
- };
33
- }
34
-
35
- abstract execute(
36
- code: string,
37
- tools: ToolDefinition[],
38
- context?: Record<string, any>
39
- ): Promise<ExecutionResult>;
40
-
41
- /**
42
- * Create a tool wrapper function that the executed code can call
43
- */
44
- protected createToolWrapper(tool: ToolDefinition): (...args: any[]) => Promise<any> {
45
- return async (...args: any[]) => {
46
- // Determine input based on schema
47
- let input: any;
48
-
49
- if (args.length === 1 && typeof args[0] === 'object') {
50
- input = args[0];
51
- } else {
52
- // If multiple args, create object from schema
53
- // This is simplified - in reality would need to parse schema
54
- input = args[0];
55
- }
56
-
57
- // Generate unique ID for this call
58
- const callId = `call_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
59
-
60
- // Record the tool call
61
- this.pendingToolCalls.push({
62
- id: callId,
63
- name: tool.name,
64
- input,
65
- caller: {
66
- type: 'code_execution',
67
- toolId: 'code_exec'
68
- }
69
- });
70
-
71
- // Execute the tool
72
- try {
73
- const result = await tool.handler(input);
74
- return result;
75
- } catch (error) {
76
- throw new Error(`Tool ${tool.name} failed: ${error}`);
77
- }
78
- };
79
- }
80
-
81
- /**
82
- * Create the context object with tool functions
83
- */
84
- protected createExecutionContext(
85
- tools: ToolDefinition[],
86
- additionalContext?: Record<string, any>
87
- ): Record<string, any> {
88
- const context: Record<string, any> = {
89
- console: {
90
- log: (...args: any[]) => {
91
- // Capture console output
92
- return args.join(' ');
93
- }
94
- },
95
- ...additionalContext
96
- };
97
-
98
- // Add each tool as a callable function
99
- tools.forEach(tool => {
100
- if (tool.allowedCallers?.includes('code_execution')) {
101
- context[tool.name] = this.createToolWrapper(tool);
102
- }
103
- });
104
-
105
- return context;
106
- }
107
-
108
- /**
109
- * Wrap code with timeout
110
- */
111
- protected async withTimeout<T>(
112
- promise: Promise<T>,
113
- timeoutMs: number
114
- ): Promise<T> {
115
- return Promise.race([
116
- promise,
117
- new Promise<T>((_, reject) =>
118
- setTimeout(() => reject(new Error('Execution timeout')), timeoutMs)
119
- )
120
- ]);
121
- }
122
- }
@@ -1,2 +0,0 @@
1
- export { CodeExecutor, BaseCodeExecutor } from './base';
2
- export { VMExecutor, createDefaultExecutor } from './vm';
@@ -1,87 +0,0 @@
1
- import { ExecutionResult, ExecutorConfig, ToolDefinition } from '../types';
2
- import { BaseCodeExecutor } from './base';
3
- import * as vm from 'vm';
4
-
5
- /**
6
- * VM-based code executor
7
- *
8
- * WARNING: This provides basic isolation but is NOT secure for untrusted code.
9
- * For production use with untrusted code, use DockerExecutor or a cloud
10
- * sandbox service like E2B.
11
- */
12
- export class VMExecutor extends BaseCodeExecutor {
13
- constructor(config: ExecutorConfig = {}) {
14
- super(config);
15
- }
16
-
17
- async execute(
18
- code: string,
19
- tools: ToolDefinition[],
20
- context?: Record<string, any>
21
- ): Promise<ExecutionResult> {
22
- this.pendingToolCalls = [];
23
-
24
- const capturedLogs: string[] = [];
25
- let returnValue: any;
26
-
27
- try {
28
- // Create execution context with tools
29
- const execContext = this.createExecutionContext(tools, context);
30
-
31
- // Override console.log to capture output
32
- execContext.console = {
33
- log: (...args: any[]) => {
34
- const message = args.map(arg =>
35
- typeof arg === 'object' ? JSON.stringify(arg) : String(arg)
36
- ).join(' ');
37
- capturedLogs.push(message);
38
- }
39
- };
40
-
41
- // Wrap code in async function
42
- const wrappedCode = `
43
- (async () => {
44
- ${code}
45
- })()
46
- `;
47
-
48
- // Create VM context
49
- const vmContext = vm.createContext(execContext);
50
-
51
- // Execute with timeout
52
- const script = new vm.Script(wrappedCode);
53
- const result = await this.withTimeout(
54
- script.runInContext(vmContext, {
55
- timeout: this.config.timeout
56
- }),
57
- this.config.timeout!
58
- );
59
-
60
- returnValue = result;
61
-
62
- return {
63
- success: true,
64
- stdout: capturedLogs.join('\n'),
65
- returnValue,
66
- toolCalls: this.pendingToolCalls
67
- };
68
- } catch (error: any) {
69
- return {
70
- success: false,
71
- stderr: error.message,
72
- error: {
73
- message: error.message,
74
- stack: error.stack
75
- },
76
- toolCalls: this.pendingToolCalls
77
- };
78
- }
79
- }
80
- }
81
-
82
- /**
83
- * Create a default executor instance
84
- */
85
- export function createDefaultExecutor(config?: ExecutorConfig): VMExecutor {
86
- return new VMExecutor(config);
87
- }
package/src/index.ts DELETED
@@ -1,26 +0,0 @@
1
- // Core exports
2
- export { Client, ToolRegistry } from './core';
3
-
4
- // Type exports
5
- export type {
6
- ToolDefinition,
7
- ToolCall,
8
- ToolResult,
9
- Message,
10
- ChatRequest,
11
- ChatResponse,
12
- SearchConfig,
13
- ExecutorConfig,
14
- ProviderAdapter,
15
- ProviderCapabilities,
16
- ClientConfig
17
- } from './types';
18
-
19
- // Adapter exports
20
- export { OpenAIAdapter, VercelAIAdapter } from './adapters';
21
-
22
- // Search exports
23
- export { SearchStrategy, RegexSearchStrategy, BM25SearchStrategy } from './search';
24
-
25
- // Executor exports
26
- export { CodeExecutor, VMExecutor, createDefaultExecutor } from './executor';
@@ -1,63 +0,0 @@
1
- import { ToolDefinition } from '../types';
2
-
3
- /**
4
- * Base interface for tool search strategies
5
- */
6
- export interface SearchStrategy {
7
- /**
8
- * Search for tools matching the query
9
- * @param query Search query
10
- * @param tools Available tools to search
11
- * @param maxResults Maximum number of results to return
12
- * @returns Matching tools, sorted by relevance
13
- */
14
- search(
15
- query: string,
16
- tools: ToolDefinition[],
17
- maxResults?: number
18
- ): Promise<ToolDefinition[]>;
19
- }
20
-
21
- /**
22
- * Abstract base class for search strategies
23
- */
24
- export abstract class BaseSearchStrategy implements SearchStrategy {
25
- protected maxResults: number;
26
- protected threshold: number;
27
-
28
- constructor(maxResults = 10, threshold = 0.0) {
29
- this.maxResults = maxResults;
30
- this.threshold = threshold;
31
- }
32
-
33
- abstract search(
34
- query: string,
35
- tools: ToolDefinition[],
36
- maxResults?: number
37
- ): Promise<ToolDefinition[]>;
38
-
39
- /**
40
- * Calculate relevance score for a tool
41
- */
42
- protected abstract scoreRelevance(query: string, tool: ToolDefinition): number;
43
-
44
- /**
45
- * Rank tools by relevance
46
- */
47
- protected rankTools(
48
- query: string,
49
- tools: ToolDefinition[],
50
- maxResults?: number
51
- ): ToolDefinition[] {
52
- const scored = tools
53
- .map(tool => ({
54
- tool,
55
- score: this.scoreRelevance(query, tool)
56
- }))
57
- .filter(({ score }) => score >= this.threshold)
58
- .sort((a, b) => b.score - a.score);
59
-
60
- const limit = maxResults ?? this.maxResults;
61
- return scored.slice(0, limit).map(({ tool }) => tool);
62
- }
63
- }
@@ -1,64 +0,0 @@
1
- import { ToolDefinition } from '../types';
2
- import { BaseSearchStrategy } from './base';
3
- import * as natural from 'natural';
4
-
5
- /**
6
- * BM25-based search strategy
7
- * Better relevance ranking, handles synonyms and term frequency
8
- */
9
- export class BM25SearchStrategy extends BaseSearchStrategy {
10
- private tokenizer: natural.WordTokenizer;
11
- private tfidf: natural.TfIdf;
12
- private toolsIndex: Map<number, ToolDefinition>;
13
-
14
- constructor(maxResults = 10, threshold = 0.0) {
15
- super(maxResults, threshold);
16
- this.tokenizer = new natural.WordTokenizer();
17
- this.tfidf = new natural.TfIdf();
18
- this.toolsIndex = new Map();
19
- }
20
-
21
- async search(
22
- query: string,
23
- tools: ToolDefinition[],
24
- maxResults?: number
25
- ): Promise<ToolDefinition[]> {
26
- // Rebuild index for current tool set
27
- this.buildIndex(tools);
28
-
29
- // Search using TF-IDF
30
- const results: Array<{ tool: ToolDefinition; score: number }> = [];
31
-
32
- this.tfidf.tfidfs(query, (i, measure) => {
33
- const tool = this.toolsIndex.get(i);
34
- if (tool && measure >= this.threshold) {
35
- results.push({ tool, score: measure });
36
- }
37
- });
38
-
39
- // Sort by score (descending)
40
- results.sort((a, b) => b.score - a.score);
41
-
42
- // Return top results
43
- const limit = maxResults ?? this.maxResults;
44
- return results.slice(0, limit).map(({ tool }) => tool);
45
- }
46
-
47
- protected scoreRelevance(query: string, tool: ToolDefinition): number {
48
- // This is handled by TF-IDF in the search method
49
- // This method is not used for BM25 but required by base class
50
- return 0;
51
- }
52
-
53
- private buildIndex(tools: ToolDefinition[]): void {
54
- this.tfidf = new natural.TfIdf();
55
- this.toolsIndex.clear();
56
-
57
- tools.forEach((tool, index) => {
58
- // Combine name (with higher weight) and description for indexing
59
- const document = `${tool.name} ${tool.name} ${tool.name} ${tool.description}`;
60
- this.tfidf.addDocument(document);
61
- this.toolsIndex.set(index, tool);
62
- });
63
- }
64
- }
@@ -1,3 +0,0 @@
1
- export { SearchStrategy, BaseSearchStrategy } from './base';
2
- export { RegexSearchStrategy } from './regex';
3
- export { BM25SearchStrategy } from './bm25';
@@ -1,66 +0,0 @@
1
- import { ToolDefinition } from '../types';
2
- import { BaseSearchStrategy } from './base';
3
-
4
- /**
5
- * Regex-based search strategy
6
- * Fast and simple, good for exact matches
7
- */
8
- export class RegexSearchStrategy extends BaseSearchStrategy {
9
- async search(
10
- query: string,
11
- tools: ToolDefinition[],
12
- maxResults?: number
13
- ): Promise<ToolDefinition[]> {
14
- return this.rankTools(query, tools, maxResults);
15
- }
16
-
17
- protected scoreRelevance(query: string, tool: ToolDefinition): number {
18
- const normalizedQuery = query.toLowerCase();
19
- const toolName = tool.name.toLowerCase();
20
- const toolDesc = tool.description.toLowerCase();
21
-
22
- let score = 0;
23
-
24
- // Exact name match gets highest score
25
- if (toolName === normalizedQuery) {
26
- score += 100;
27
- }
28
-
29
- // Name contains query
30
- if (toolName.includes(normalizedQuery)) {
31
- score += 50;
32
- }
33
-
34
- // Query matches word boundary in name
35
- const nameWordBoundary = new RegExp(`\\b${this.escapeRegex(normalizedQuery)}`, 'i');
36
- if (nameWordBoundary.test(toolName)) {
37
- score += 30;
38
- }
39
-
40
- // Description contains query
41
- if (toolDesc.includes(normalizedQuery)) {
42
- score += 20;
43
- }
44
-
45
- // Query matches word boundary in description
46
- const descWordBoundary = new RegExp(`\\b${this.escapeRegex(normalizedQuery)}`, 'i');
47
- if (descWordBoundary.test(toolDesc)) {
48
- score += 10;
49
- }
50
-
51
- // Check for individual query words
52
- const queryWords = normalizedQuery.split(/\s+/);
53
- queryWords.forEach(word => {
54
- if (word.length > 2) {
55
- if (toolName.includes(word)) score += 5;
56
- if (toolDesc.includes(word)) score += 2;
57
- }
58
- });
59
-
60
- return score;
61
- }
62
-
63
- private escapeRegex(str: string): string {
64
- return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
65
- }
66
- }