@tyvm/knowhow 0.0.20 → 0.0.22
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/package.json +3 -1
- package/src/agents/base/base.ts +16 -77
- package/src/agents/tools/executeScript/README.md +78 -0
- package/src/agents/tools/executeScript/definition.ts +73 -0
- package/src/agents/tools/executeScript/examples/quick-test.ts +80 -0
- package/src/agents/tools/executeScript/examples/serialization-test.ts +309 -0
- package/src/agents/tools/executeScript/examples/test-runner.ts +204 -0
- package/src/agents/tools/executeScript/index.ts +74 -0
- package/src/agents/tools/index.ts +1 -0
- package/src/agents/tools/list.ts +2 -1
- package/src/cli.ts +2 -6
- package/src/clients/index.ts +23 -9
- package/src/services/Tools.ts +150 -9
- package/src/services/script-execution/SandboxContext.ts +278 -0
- package/src/services/script-execution/ScriptExecutor.ts +337 -0
- package/src/services/script-execution/ScriptPolicy.ts +236 -0
- package/src/services/script-execution/ScriptTracer.ts +249 -0
- package/src/services/script-execution/types.ts +134 -0
- package/ts_build/src/agents/base/base.js +2 -53
- package/ts_build/src/agents/base/base.js.map +1 -1
- package/ts_build/src/agents/tools/executeScript/definition.d.ts +2 -0
- package/ts_build/src/agents/tools/executeScript/definition.js +70 -0
- package/ts_build/src/agents/tools/executeScript/definition.js.map +1 -0
- package/ts_build/src/agents/tools/executeScript/examples/quick-test.d.ts +3 -0
- package/ts_build/src/agents/tools/executeScript/examples/quick-test.js +68 -0
- package/ts_build/src/agents/tools/executeScript/examples/quick-test.js.map +1 -0
- package/ts_build/src/agents/tools/executeScript/examples/serialization-test.d.ts +15 -0
- package/ts_build/src/agents/tools/executeScript/examples/serialization-test.js +267 -0
- package/ts_build/src/agents/tools/executeScript/examples/serialization-test.js.map +1 -0
- package/ts_build/src/agents/tools/executeScript/examples/simple-example.d.ts +20 -0
- package/ts_build/src/agents/tools/executeScript/examples/simple-example.js +35 -0
- package/ts_build/src/agents/tools/executeScript/examples/simple-example.js.map +1 -0
- package/ts_build/src/agents/tools/executeScript/examples/test-runner.d.ts +4 -0
- package/ts_build/src/agents/tools/executeScript/examples/test-runner.js +202 -0
- package/ts_build/src/agents/tools/executeScript/examples/test-runner.js.map +1 -0
- package/ts_build/src/agents/tools/executeScript/handler.d.ts +27 -0
- package/ts_build/src/agents/tools/executeScript/handler.js +64 -0
- package/ts_build/src/agents/tools/executeScript/handler.js.map +1 -0
- package/ts_build/src/agents/tools/executeScript/index.d.ts +27 -0
- package/ts_build/src/agents/tools/executeScript/index.js +64 -0
- package/ts_build/src/agents/tools/executeScript/index.js.map +1 -0
- package/ts_build/src/agents/tools/executeScript.d.ts +29 -0
- package/ts_build/src/agents/tools/executeScript.js +124 -0
- package/ts_build/src/agents/tools/executeScript.js.map +1 -0
- package/ts_build/src/agents/tools/index.d.ts +1 -0
- package/ts_build/src/agents/tools/index.js +1 -0
- package/ts_build/src/agents/tools/index.js.map +1 -1
- package/ts_build/src/agents/tools/list.js +2 -0
- package/ts_build/src/agents/tools/list.js.map +1 -1
- package/ts_build/src/cli.js +2 -6
- package/ts_build/src/cli.js.map +1 -1
- package/ts_build/src/clients/index.d.ts +9 -2
- package/ts_build/src/clients/index.js +17 -4
- package/ts_build/src/clients/index.js.map +1 -1
- package/ts_build/src/services/Tools.d.ts +11 -1
- package/ts_build/src/services/Tools.js +94 -3
- package/ts_build/src/services/Tools.js.map +1 -1
- package/ts_build/src/services/script-execution/SandboxContext.d.ts +34 -0
- package/ts_build/src/services/script-execution/SandboxContext.js +188 -0
- package/ts_build/src/services/script-execution/SandboxContext.js.map +1 -0
- package/ts_build/src/services/script-execution/ScriptExecutor.d.ts +17 -0
- package/ts_build/src/services/script-execution/ScriptExecutor.js +207 -0
- package/ts_build/src/services/script-execution/ScriptExecutor.js.map +1 -0
- package/ts_build/src/services/script-execution/ScriptPolicy.d.ts +27 -0
- package/ts_build/src/services/script-execution/ScriptPolicy.js +150 -0
- package/ts_build/src/services/script-execution/ScriptPolicy.js.map +1 -0
- package/ts_build/src/services/script-execution/ScriptTracer.d.ts +19 -0
- package/ts_build/src/services/script-execution/ScriptTracer.js +186 -0
- package/ts_build/src/services/script-execution/ScriptTracer.js.map +1 -0
- package/ts_build/src/services/script-execution/types.d.ts +108 -0
- package/ts_build/src/services/script-execution/types.js +3 -0
- package/ts_build/src/services/script-execution/types.js.map +1 -0
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
#!/usr/bin/env ts-node
|
|
2
|
+
/**
|
|
3
|
+
* Test runner for the executeScript tool
|
|
4
|
+
* Usage: npx ts-node src/agents/tools/executeScript/examples/test-runner.ts
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { executeScript } from "../../executeScript";
|
|
8
|
+
import { Tools } from "../../../../services";
|
|
9
|
+
import { Clients } from "../../../../clients";
|
|
10
|
+
import { includedTools } from "../../../tools/list";
|
|
11
|
+
import * as allTools from "../../../tools";
|
|
12
|
+
|
|
13
|
+
// Sample script to test with
|
|
14
|
+
const testScript = `
|
|
15
|
+
// Test script that demonstrates various executeScript capabilities
|
|
16
|
+
console.log("Starting test script execution...");
|
|
17
|
+
|
|
18
|
+
async function main() {
|
|
19
|
+
// Test 1: Simple console output
|
|
20
|
+
console.log("Test 1: Basic logging works");
|
|
21
|
+
|
|
22
|
+
// Test 2: Call a tool (file search)
|
|
23
|
+
try {
|
|
24
|
+
console.log("Test 2: Calling fileSearch tool...");
|
|
25
|
+
const searchResult = await callTool("fileSearch", {
|
|
26
|
+
searchTerm: "package.json"
|
|
27
|
+
});
|
|
28
|
+
console.log("File search result:", searchResult);
|
|
29
|
+
} catch (error) {
|
|
30
|
+
console.error("Tool call failed:", error.message);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Test 3: Call another tool (text search)
|
|
34
|
+
try {
|
|
35
|
+
console.log("Test 3: Calling textSearch tool...");
|
|
36
|
+
const textResult = await callTool("textSearch", {
|
|
37
|
+
searchTerm: "executeScript"
|
|
38
|
+
});
|
|
39
|
+
console.log("Text search found", textResult?.length || 0, "matches");
|
|
40
|
+
} catch (error) {
|
|
41
|
+
console.error("Text search failed:", error.message);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Test 4: Make an LLM call
|
|
45
|
+
try {
|
|
46
|
+
console.log("Test 4: Making LLM call...");
|
|
47
|
+
const llmResponse = await llm([
|
|
48
|
+
{
|
|
49
|
+
role: "system",
|
|
50
|
+
content: "You are a helpful assistant. Respond with exactly one sentence."
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
role: "user",
|
|
54
|
+
content: "What is 2+2? Just give the answer briefly."
|
|
55
|
+
}
|
|
56
|
+
], {
|
|
57
|
+
model: "gpt-4o-mini",
|
|
58
|
+
max_tokens: 50
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
console.log("LLM Response:", llmResponse.choices[0].message.content);
|
|
62
|
+
} catch (error) {
|
|
63
|
+
console.error("LLM call failed:", error.message);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Test 5: Create an artifact
|
|
67
|
+
try {
|
|
68
|
+
console.log("Test 5: Creating artifact...");
|
|
69
|
+
createArtifact("test-results.md", \`# Test Results
|
|
70
|
+
|
|
71
|
+
Script executed successfully at: \${new Date().toISOString()}
|
|
72
|
+
|
|
73
|
+
This is a test artifact created by the executeScript tool.
|
|
74
|
+
|
|
75
|
+
## Test Summary
|
|
76
|
+
- Console logging: ✓
|
|
77
|
+
- Tool calls: ✓
|
|
78
|
+
- LLM calls: ✓
|
|
79
|
+
- Artifact creation: ✓
|
|
80
|
+
\`, "markdown");
|
|
81
|
+
console.log("Artifact created successfully");
|
|
82
|
+
} catch (error) {
|
|
83
|
+
console.error("Artifact creation failed:", error.message);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Return final result
|
|
87
|
+
return {
|
|
88
|
+
success: true,
|
|
89
|
+
message: "All tests completed successfully",
|
|
90
|
+
timestamp: new Date().toISOString(),
|
|
91
|
+
testsRun: 5
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Execute the main function
|
|
96
|
+
await main().then(result => {
|
|
97
|
+
console.log("=== SCRIPT COMPLETED ===");
|
|
98
|
+
console.log("Final result:", JSON.stringify(result, null, 2));
|
|
99
|
+
}).catch(error => {
|
|
100
|
+
console.error("=== SCRIPT FAILED ===");
|
|
101
|
+
console.error("Error:", error);
|
|
102
|
+
throw error;
|
|
103
|
+
});
|
|
104
|
+
`;
|
|
105
|
+
|
|
106
|
+
async function runTest() {
|
|
107
|
+
console.log("🚀 Starting executeScript test...\n");
|
|
108
|
+
|
|
109
|
+
try {
|
|
110
|
+
Tools.defineTools(includedTools, allTools);
|
|
111
|
+
|
|
112
|
+
const context = {
|
|
113
|
+
tools: Tools,
|
|
114
|
+
clients: Clients,
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
console.log("📋 Test Parameters:");
|
|
118
|
+
console.log("- Max Tool Calls: 10");
|
|
119
|
+
console.log("- Max Tokens: 1000");
|
|
120
|
+
console.log("- Max Execution Time: 60s");
|
|
121
|
+
console.log("- Max Cost: $0.50\n");
|
|
122
|
+
|
|
123
|
+
const startTime = Date.now();
|
|
124
|
+
|
|
125
|
+
// Execute the test script
|
|
126
|
+
const result = await executeScript(
|
|
127
|
+
{
|
|
128
|
+
script: testScript,
|
|
129
|
+
maxToolCalls: 10,
|
|
130
|
+
maxTokens: 1000,
|
|
131
|
+
maxExecutionTimeMs: 60000,
|
|
132
|
+
maxCostUsd: 0.5,
|
|
133
|
+
},
|
|
134
|
+
context
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
const executionTime = Date.now() - startTime;
|
|
138
|
+
|
|
139
|
+
console.log("\n" + "=".repeat(60));
|
|
140
|
+
console.log("🎯 TEST RESULTS");
|
|
141
|
+
console.log("=".repeat(60));
|
|
142
|
+
console.log(`⏱️ Execution Time: ${executionTime}ms`);
|
|
143
|
+
console.log(`✅ Success: ${result.success}`);
|
|
144
|
+
|
|
145
|
+
if (result.success) {
|
|
146
|
+
console.log(`📊 Result:`, result.result);
|
|
147
|
+
console.log(`🔧 Tool Calls Made: ${result.quotaUsage.toolCalls}`);
|
|
148
|
+
console.log(`🎯 Tokens Used: ${result.quotaUsage.tokens}`);
|
|
149
|
+
console.log(`💰 Cost: $${result.quotaUsage.costUsd.toFixed(4)}`);
|
|
150
|
+
|
|
151
|
+
if (result.artifacts.length > 0) {
|
|
152
|
+
console.log(`📁 Artifacts Created: ${result.artifacts.length}`);
|
|
153
|
+
result.artifacts.forEach((artifact) => {
|
|
154
|
+
console.log(
|
|
155
|
+
` - ${artifact.name} (${artifact.type}, ${artifact.contentLength} bytes)`
|
|
156
|
+
);
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (result.consoleOutput.length > 0) {
|
|
161
|
+
console.log(
|
|
162
|
+
`\n📝 Console Output (${result.consoleOutput.length} entries):`
|
|
163
|
+
);
|
|
164
|
+
result.consoleOutput.forEach((entry) => {
|
|
165
|
+
console.log(` ${entry}`);
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (result.violations.length > 0) {
|
|
170
|
+
console.log(`\n⚠️ Policy Violations: ${result.violations.length}`);
|
|
171
|
+
result.violations.forEach((violation) => {
|
|
172
|
+
console.log(` - ${JSON.stringify(violation)}`);
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
} else {
|
|
176
|
+
console.log(`❌ Error: ${result.error}`);
|
|
177
|
+
|
|
178
|
+
if (result.consoleOutput.length > 0) {
|
|
179
|
+
console.log(`\n📝 Console Output Before Failure:`);
|
|
180
|
+
result.consoleOutput.forEach((entry) => {
|
|
181
|
+
console.log(` ${entry}`);
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
console.log("\n" + "=".repeat(60));
|
|
187
|
+
console.log(result.success ? "🎉 TEST PASSED!" : "💥 TEST FAILED!");
|
|
188
|
+
console.log("=".repeat(60));
|
|
189
|
+
} catch (error) {
|
|
190
|
+
console.error("\n💥 TEST RUNNER ERROR:");
|
|
191
|
+
console.error(error);
|
|
192
|
+
process.exit(1);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Run the test if this file is executed directly
|
|
197
|
+
if (require.main === module) {
|
|
198
|
+
runTest().catch((error) => {
|
|
199
|
+
console.error("Unhandled error:", error);
|
|
200
|
+
process.exit(1);
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export { runTest, testScript };
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { ScriptExecutor } from "../../../services/script-execution/ScriptExecutor";
|
|
2
|
+
import { Tools } from "../../../services";
|
|
3
|
+
import { Clients } from "../../../clients";
|
|
4
|
+
import {
|
|
5
|
+
ExecutionRequest,
|
|
6
|
+
ExecutionResult,
|
|
7
|
+
} from "../../../services/script-execution/types";
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
export const executeScript = async (
|
|
11
|
+
{ script, maxToolCalls, maxTokens, maxExecutionTimeMs, maxCostUsd },
|
|
12
|
+
context
|
|
13
|
+
) => {
|
|
14
|
+
try {
|
|
15
|
+
// Create script executor with access to tools and clients
|
|
16
|
+
const executor = new ScriptExecutor(Tools, Clients);
|
|
17
|
+
|
|
18
|
+
// Execute the script
|
|
19
|
+
const result = await executor.execute({
|
|
20
|
+
script,
|
|
21
|
+
quotas: {
|
|
22
|
+
maxToolCalls: maxToolCalls || 50,
|
|
23
|
+
maxTokens: maxTokens || 10000,
|
|
24
|
+
maxExecutionTimeMs: maxExecutionTimeMs || 30000,
|
|
25
|
+
maxCostUsd: maxCostUsd || 1.0,
|
|
26
|
+
maxMemoryMb: 100,
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// If there were policy violations, include them in the response
|
|
31
|
+
const violations = result.trace.events
|
|
32
|
+
.filter((e) => e.type.includes("violation") || e.type.includes("error"))
|
|
33
|
+
.map((e) => e.data);
|
|
34
|
+
|
|
35
|
+
// Format the response
|
|
36
|
+
return {
|
|
37
|
+
success: result.success,
|
|
38
|
+
result: result.result,
|
|
39
|
+
error: result.error,
|
|
40
|
+
artifacts: result.artifacts.map((a) => ({
|
|
41
|
+
id: a.id,
|
|
42
|
+
name: a.name,
|
|
43
|
+
type: a.type,
|
|
44
|
+
contentLength: a.content.length,
|
|
45
|
+
createdAt: a.createdAt,
|
|
46
|
+
})),
|
|
47
|
+
consoleOutput: result.consoleOutput,
|
|
48
|
+
metrics: result.trace.metrics,
|
|
49
|
+
violations,
|
|
50
|
+
executionTimeMs: result.trace.endTime - result.trace.startTime,
|
|
51
|
+
quotaUsage: {
|
|
52
|
+
toolCalls: result.trace.metrics.toolCallCount,
|
|
53
|
+
tokens: result.trace.metrics.tokenUsage.total,
|
|
54
|
+
costUsd: result.trace.metrics.costUsd,
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
} catch (error) {
|
|
58
|
+
return {
|
|
59
|
+
success: false,
|
|
60
|
+
error: error instanceof Error ? error.message : String(error),
|
|
61
|
+
result: null,
|
|
62
|
+
artifacts: [],
|
|
63
|
+
consoleOutput: [],
|
|
64
|
+
metrics: null,
|
|
65
|
+
violations: [],
|
|
66
|
+
executionTimeMs: 0,
|
|
67
|
+
quotaUsage: {
|
|
68
|
+
toolCalls: 0,
|
|
69
|
+
tokens: 0,
|
|
70
|
+
costUsd: 0,
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
};
|
package/src/agents/tools/list.ts
CHANGED
|
@@ -7,6 +7,7 @@ import * as github from "./github/definitions";
|
|
|
7
7
|
import * as asana from "./asana/definitions";
|
|
8
8
|
import * as language from "./language/definitions";
|
|
9
9
|
import { googleSearchDefinition } from "./googleSearch";
|
|
10
|
+
import { executeScriptDefinition } from "./executeScript/definition";
|
|
10
11
|
|
|
11
12
|
export const includedTools = [
|
|
12
13
|
{
|
|
@@ -552,7 +553,7 @@ export const includedTools = [
|
|
|
552
553
|
},
|
|
553
554
|
},
|
|
554
555
|
},
|
|
555
|
-
|
|
556
|
+
executeScriptDefinition,
|
|
556
557
|
googleSearchDefinition,
|
|
557
558
|
...asana.definitions,
|
|
558
559
|
...github.definitions,
|
package/src/cli.ts
CHANGED
|
@@ -11,7 +11,7 @@ import { Vimmer } from "./agents/vim/vim";
|
|
|
11
11
|
import { Developer } from "./agents/developer/developer";
|
|
12
12
|
import { Tools } from "./services";
|
|
13
13
|
import { includedTools } from "./agents/tools/list";
|
|
14
|
-
import * as allTools from "./agents/tools
|
|
14
|
+
import * as allTools from "./agents/tools";
|
|
15
15
|
import { Mcp } from "./services/Mcp";
|
|
16
16
|
import { login } from "./login";
|
|
17
17
|
import { worker } from "./worker";
|
|
@@ -24,12 +24,8 @@ async function main() {
|
|
|
24
24
|
Agents.registerAgent(Patcher);
|
|
25
25
|
Agents.registerAgent(Developer);
|
|
26
26
|
Agents.loadAgentsFromConfig();
|
|
27
|
-
Tools.addTools(includedTools);
|
|
28
27
|
|
|
29
|
-
|
|
30
|
-
.filter(([_, value]) => typeof value === 'function')
|
|
31
|
-
.reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {});
|
|
32
|
-
Tools.addFunctions(toolFunctions);
|
|
28
|
+
Tools.defineTools(includedTools, allTools);
|
|
33
29
|
|
|
34
30
|
await Mcp.connectToConfigured(Tools);
|
|
35
31
|
await Clients.registerConfiguredModels();
|
package/src/clients/index.ts
CHANGED
|
@@ -153,13 +153,24 @@ export class AIClient {
|
|
|
153
153
|
);
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
-
|
|
156
|
+
providerHasModel(provider: string, model: string): boolean {
|
|
157
157
|
const models = this.clientModels[provider];
|
|
158
158
|
if (!models) return false;
|
|
159
159
|
return models.includes(model);
|
|
160
160
|
}
|
|
161
161
|
|
|
162
|
-
|
|
162
|
+
findModel(modelPrefix: string) {
|
|
163
|
+
for (const provider of Object.keys(this.clientModels)) {
|
|
164
|
+
const models = this.clientModels[provider];
|
|
165
|
+
const foundModel = models.find((m) => m.startsWith(modelPrefix));
|
|
166
|
+
if (foundModel) {
|
|
167
|
+
return { provider, model: foundModel };
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return undefined;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
detectProviderModel(provider: string, model?: string) {
|
|
163
174
|
if (this.providerHasModel(provider, model)) {
|
|
164
175
|
return { provider, model };
|
|
165
176
|
}
|
|
@@ -170,18 +181,21 @@ export class AIClient {
|
|
|
170
181
|
const inferredProvider = split[0];
|
|
171
182
|
const inferredModel = split.slice(1).join("/");
|
|
172
183
|
|
|
184
|
+
// Exact match
|
|
173
185
|
if (this.providerHasModel(inferredProvider, inferredModel)) {
|
|
174
186
|
return { provider: inferredProvider, model: inferredModel };
|
|
175
187
|
}
|
|
176
|
-
}
|
|
177
188
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
189
|
+
// Starts with match
|
|
190
|
+
const foundBySplit = this.findModel(inferredModel);
|
|
191
|
+
if (foundBySplit) {
|
|
192
|
+
return foundBySplit;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
182
195
|
|
|
183
|
-
|
|
184
|
-
|
|
196
|
+
const foundByModel = this.findModel(model);
|
|
197
|
+
if (foundByModel) {
|
|
198
|
+
return foundByModel;
|
|
185
199
|
}
|
|
186
200
|
|
|
187
201
|
return { provider, model };
|
package/src/services/Tools.ts
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import { ChatCompletionTool } from "openai/resources/chat";
|
|
2
|
+
import { replaceEscapedNewLines, restoreEscapedNewLines } from "../utils";
|
|
2
3
|
import { includedTools } from "../agents/tools/list";
|
|
3
|
-
import { Tool } from "../clients/types";
|
|
4
|
-
import {
|
|
4
|
+
import { Tool, ToolCall } from "../clients/types";
|
|
5
|
+
import {
|
|
6
|
+
ToolOverrideRegistration,
|
|
7
|
+
ToolWrapperRegistration,
|
|
8
|
+
ToolOverrideFunction,
|
|
9
|
+
ToolWrapper,
|
|
10
|
+
createPatternMatcher,
|
|
11
|
+
} from "./types";
|
|
5
12
|
|
|
6
13
|
export class ToolsService {
|
|
7
14
|
tools = [] as Tool[];
|
|
@@ -61,7 +68,7 @@ export class ToolsService {
|
|
|
61
68
|
const wrappers = this.findMatchingWrappers(name);
|
|
62
69
|
if (wrappers.length > 0) {
|
|
63
70
|
let wrappedFunction = func;
|
|
64
|
-
|
|
71
|
+
|
|
65
72
|
// Apply wrappers in priority order
|
|
66
73
|
for (const wrapperReg of wrappers) {
|
|
67
74
|
const currentFunction = wrappedFunction;
|
|
@@ -70,7 +77,7 @@ export class ToolsService {
|
|
|
70
77
|
return await wrapperReg.wrapper(currentFunction, args, tool);
|
|
71
78
|
};
|
|
72
79
|
}
|
|
73
|
-
|
|
80
|
+
|
|
74
81
|
this.functions[name] = wrappedFunction;
|
|
75
82
|
return;
|
|
76
83
|
}
|
|
@@ -90,15 +97,147 @@ export class ToolsService {
|
|
|
90
97
|
}
|
|
91
98
|
|
|
92
99
|
addTools(tools: Tool[]) {
|
|
93
|
-
|
|
100
|
+
// Prevent duplicate tool names
|
|
101
|
+
const existingTools = this.getToolNames();
|
|
102
|
+
const filteredTools = tools.filter(
|
|
103
|
+
(tool) => !existingTools.includes(tool.function.name)
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
this.tools.push(...filteredTools);
|
|
94
107
|
}
|
|
95
108
|
|
|
96
109
|
addFunctions(fns: { [fnName: string]: (...args: any) => any }) {
|
|
97
110
|
for (const fnName of Object.keys(fns)) {
|
|
111
|
+
if (typeof fns[fnName] !== "function") {
|
|
112
|
+
// Skip non-function entries
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
98
115
|
this.setFunction(fnName, fns[fnName]);
|
|
99
116
|
}
|
|
100
117
|
}
|
|
101
118
|
|
|
119
|
+
defineTools(
|
|
120
|
+
tools: Tool[],
|
|
121
|
+
functions: { [fnName: string]: ((...args: any) => any) | any }
|
|
122
|
+
) {
|
|
123
|
+
this.addTools(tools);
|
|
124
|
+
this.addFunctions(functions);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async callTool(toolCall: ToolCall, enabledTools = this.getToolNames()) {
|
|
128
|
+
const functionName = toolCall.function.name;
|
|
129
|
+
const functionArgs = JSON.parse(
|
|
130
|
+
restoreEscapedNewLines(toolCall.function.arguments)
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
try {
|
|
134
|
+
// Check if tool is enabled
|
|
135
|
+
if (!enabledTools.includes(functionName)) {
|
|
136
|
+
const options = enabledTools.join(", ");
|
|
137
|
+
throw new Error(
|
|
138
|
+
`Function ${functionName} not enabled, options are ${options}`
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Check if tool definition exists
|
|
143
|
+
const toolDefinition = this.getTool(functionName);
|
|
144
|
+
if (!toolDefinition) {
|
|
145
|
+
throw new Error(`Tool ${functionName} not found`);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Check if function implementation exists
|
|
149
|
+
const functionToCall = this.getFunction(functionName);
|
|
150
|
+
if (!functionToCall) {
|
|
151
|
+
const options = enabledTools.join(", ");
|
|
152
|
+
throw new Error(
|
|
153
|
+
`Function ${functionName} not found, options are ${options}`
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Prepare function arguments
|
|
158
|
+
const properties = toolDefinition?.function?.parameters?.properties || {};
|
|
159
|
+
const isPositional =
|
|
160
|
+
toolDefinition?.function?.parameters?.positional || false;
|
|
161
|
+
const fnArgs = isPositional
|
|
162
|
+
? Object.keys(properties).map((p) => functionArgs[p])
|
|
163
|
+
: functionArgs;
|
|
164
|
+
|
|
165
|
+
console.log(
|
|
166
|
+
`Calling function ${functionName} with args:`,
|
|
167
|
+
JSON.stringify(fnArgs, null, 2)
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
// Execute the function
|
|
171
|
+
const functionResponse = await Promise.resolve(
|
|
172
|
+
isPositional ? functionToCall(...fnArgs) : functionToCall(fnArgs)
|
|
173
|
+
).catch((e) => {
|
|
174
|
+
throw new Error("ERROR: " + e.message);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// Helper function to convert objects to JSON
|
|
178
|
+
const toJsonIfObject = (arg: any) => {
|
|
179
|
+
if (typeof arg === "object") {
|
|
180
|
+
return JSON.stringify(arg, null, 2);
|
|
181
|
+
}
|
|
182
|
+
return arg;
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
let toolMessages = [];
|
|
186
|
+
|
|
187
|
+
// Handle special case for parallel tool use
|
|
188
|
+
if (functionName === "multi_tool_use.parallel") {
|
|
189
|
+
const args = fnArgs[0] as {
|
|
190
|
+
recipient_name: string;
|
|
191
|
+
parameters: any;
|
|
192
|
+
}[];
|
|
193
|
+
|
|
194
|
+
toolMessages = args.map((call, index) => {
|
|
195
|
+
return {
|
|
196
|
+
tool_call_id: toolCall.id + "_" + index,
|
|
197
|
+
role: "tool",
|
|
198
|
+
name: call.recipient_name.split(".").pop(),
|
|
199
|
+
content: toJsonIfObject(functionResponse[index]) || "Done",
|
|
200
|
+
};
|
|
201
|
+
});
|
|
202
|
+
} else {
|
|
203
|
+
toolMessages = [
|
|
204
|
+
{
|
|
205
|
+
tool_call_id: toolCall.id,
|
|
206
|
+
role: "tool",
|
|
207
|
+
name: functionName,
|
|
208
|
+
content: toJsonIfObject(functionResponse) || "Done",
|
|
209
|
+
},
|
|
210
|
+
];
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return {
|
|
214
|
+
toolMessages,
|
|
215
|
+
toolCallId: toolCall.id,
|
|
216
|
+
functionName,
|
|
217
|
+
functionArgs,
|
|
218
|
+
functionResp: functionResponse,
|
|
219
|
+
};
|
|
220
|
+
} catch (error) {
|
|
221
|
+
console.log(error.message);
|
|
222
|
+
const toolMessages = [
|
|
223
|
+
{
|
|
224
|
+
tool_call_id: toolCall.id,
|
|
225
|
+
role: "tool",
|
|
226
|
+
name: "error",
|
|
227
|
+
content: error.message,
|
|
228
|
+
},
|
|
229
|
+
];
|
|
230
|
+
|
|
231
|
+
return {
|
|
232
|
+
toolMessages,
|
|
233
|
+
toolCallId: toolCall.id,
|
|
234
|
+
functionName,
|
|
235
|
+
functionArgs,
|
|
236
|
+
functionResp: undefined,
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
102
241
|
// Tool Override Methods
|
|
103
242
|
registerOverride(
|
|
104
243
|
pattern: string | RegExp,
|
|
@@ -119,14 +258,16 @@ export class ToolsService {
|
|
|
119
258
|
}
|
|
120
259
|
|
|
121
260
|
removeOverride(pattern: string | RegExp): void {
|
|
122
|
-
this.overrides = this.overrides.filter(reg => reg.pattern !== pattern);
|
|
261
|
+
this.overrides = this.overrides.filter((reg) => reg.pattern !== pattern);
|
|
123
262
|
}
|
|
124
263
|
|
|
125
264
|
removeWrapper(pattern: string | RegExp): void {
|
|
126
|
-
this.wrappers = this.wrappers.filter(reg => reg.pattern !== pattern);
|
|
265
|
+
this.wrappers = this.wrappers.filter((reg) => reg.pattern !== pattern);
|
|
127
266
|
}
|
|
128
267
|
|
|
129
|
-
private findMatchingOverride(
|
|
268
|
+
private findMatchingOverride(
|
|
269
|
+
toolName: string
|
|
270
|
+
): ToolOverrideRegistration | null {
|
|
130
271
|
for (const registration of this.overrides) {
|
|
131
272
|
const matcher = createPatternMatcher(registration.pattern);
|
|
132
273
|
if (matcher.matches(toolName)) {
|
|
@@ -161,4 +302,4 @@ export class ToolsService {
|
|
|
161
302
|
}
|
|
162
303
|
}
|
|
163
304
|
|
|
164
|
-
export const Tools = new ToolsService();
|
|
305
|
+
export const Tools = new ToolsService();
|