-
Notifications
You must be signed in to change notification settings - Fork 41
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
282 additions
and
24 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
import { ChatCompletionTool } from "openai/resources/chat/completions"; | ||
import Logger from "./Logger"; | ||
|
||
type ToolFunction = (...args: any[]) => any; | ||
|
||
interface ToolInfo { | ||
func: ToolFunction; | ||
description: string; | ||
parameters: Record<string, unknown>; | ||
} | ||
|
||
export class ToolsManager { | ||
private tools: Map<string, ToolInfo>; | ||
|
||
constructor() { | ||
this.tools = new Map(); | ||
} | ||
|
||
public registerDefaultTools() { | ||
this.registerTool( | ||
"get_current_weather", | ||
({ city }: { city: string }) => { | ||
return JSON.stringify({ | ||
city: city, | ||
temperature: "25°C", | ||
weather: "sunny", | ||
}); | ||
}, | ||
"Get the current weather for a specified city", | ||
{ | ||
type: "object", | ||
properties: { | ||
city: { type: "string", description: "The name of the city" }, | ||
}, | ||
required: ["city"], | ||
} | ||
); | ||
} | ||
|
||
public registerTool( | ||
name: string, | ||
func: ToolFunction, | ||
description: string, | ||
parameters: Record<string, unknown> | ||
) { | ||
this.tools.set(name, { func, description, parameters }); | ||
} | ||
|
||
public executeTool(name: string, args: any): string | null { | ||
const toolInfo = this.tools.get(name); | ||
if (toolInfo) { | ||
try { | ||
return JSON.stringify(toolInfo.func(args)); | ||
} catch (error) { | ||
Logger.log(`Error executing tool ${name}:`, error); | ||
return null; | ||
} | ||
} else { | ||
Logger.log(`Tool ${name} not found`); | ||
return null; | ||
} | ||
} | ||
|
||
public getToolsForOpenAI(): ChatCompletionTool[] { | ||
return Array.from(this.tools.entries()).map(([toolName, toolInfo]) => ({ | ||
type: "function", | ||
function: { | ||
name: toolName, | ||
description: toolInfo.description, | ||
parameters: toolInfo.parameters, | ||
}, | ||
})); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
import * as assert from "assert"; | ||
import { ToolsManager } from "../../src/ToolsManager"; | ||
import { ChatCompletionTool } from "openai/resources/chat/completions"; | ||
|
||
// Defines a Mocha test suite to group tests of similar kind together | ||
suite("ToolsManager Tests", () => { | ||
let toolsManager: ToolsManager; | ||
|
||
setup(() => { | ||
toolsManager = new ToolsManager(); | ||
}); | ||
|
||
test("registerTool should add a new tool", () => { | ||
const toolName = "test_tool"; | ||
const toolFunc = () => ({ result: "success" }); | ||
const toolDescription = "A test tool"; | ||
const toolParameters = { type: "object", properties: {} }; | ||
|
||
toolsManager.registerTool( | ||
toolName, | ||
toolFunc, | ||
toolDescription, | ||
toolParameters | ||
); | ||
|
||
const tools = toolsManager.getToolsForOpenAI(); | ||
assert.strictEqual(tools.length, 1); | ||
assert.strictEqual(tools[0].function.name, toolName); | ||
assert.strictEqual(tools[0].function.description, toolDescription); | ||
assert.deepStrictEqual(tools[0].function.parameters, toolParameters); | ||
}); | ||
|
||
test("executeTool should call the registered tool function", () => { | ||
const toolName = "test_tool"; | ||
const toolFunc = (args: any) => ({ result: args.input }); | ||
const toolDescription = "A test tool"; | ||
const toolParameters = { type: "object", properties: {} }; | ||
|
||
toolsManager.registerTool( | ||
toolName, | ||
toolFunc, | ||
toolDescription, | ||
toolParameters | ||
); | ||
|
||
const result = toolsManager.executeTool(toolName, { input: "test" }); | ||
assert.strictEqual(result, JSON.stringify({ result: "test" })); | ||
}); | ||
|
||
test("executeTool should return null for unregistered tool", () => { | ||
const result = toolsManager.executeTool("nonexistent_tool", {}); | ||
assert.strictEqual(result, null); | ||
}); | ||
|
||
test("getToolsForOpenAI should return correct format", () => { | ||
const toolName = "test_tool"; | ||
const toolFunc = () => ({}); | ||
const toolDescription = "A test tool"; | ||
const toolParameters = { | ||
type: "object", | ||
properties: { arg: { type: "string" } }, | ||
}; | ||
|
||
toolsManager.registerTool( | ||
toolName, | ||
toolFunc, | ||
toolDescription, | ||
toolParameters | ||
); | ||
|
||
const tools = toolsManager.getToolsForOpenAI(); | ||
assert.strictEqual(tools.length, 1); | ||
assert.deepStrictEqual(tools[0], { | ||
type: "function", | ||
function: { | ||
name: toolName, | ||
description: toolDescription, | ||
parameters: toolParameters, | ||
}, | ||
}); | ||
}); | ||
|
||
test("registerDefaultTools should register the weather tool", () => { | ||
toolsManager.registerDefaultTools(); | ||
const tools = toolsManager.getToolsForOpenAI(); | ||
assert.strictEqual(tools.length, 1); | ||
assert.strictEqual(tools[0].function.name, "get_current_weather"); | ||
}); | ||
|
||
test("registerTool should overwrite existing tool with same name", () => { | ||
const toolName = "test_tool"; | ||
const toolFunc1 = () => ({ result: "original" }); | ||
const toolFunc2 = () => ({ result: "overwritten" }); | ||
const toolDescription = "A test tool"; | ||
const toolParameters = { type: "object", properties: {} }; | ||
|
||
toolsManager.registerTool( | ||
toolName, | ||
toolFunc1, | ||
toolDescription, | ||
toolParameters | ||
); | ||
toolsManager.registerTool( | ||
toolName, | ||
toolFunc2, | ||
toolDescription, | ||
toolParameters | ||
); | ||
|
||
const result = toolsManager.executeTool(toolName, {}); | ||
assert.strictEqual(result, JSON.stringify({ result: "overwritten" })); | ||
}); | ||
|
||
test("executeTool should handle errors in tool function", () => { | ||
const toolName = "error_tool"; | ||
const toolFunc = () => { | ||
throw new Error("Test error"); | ||
}; | ||
const toolDescription = "A tool that throws an error"; | ||
const toolParameters = { type: "object", properties: {} }; | ||
|
||
toolsManager.registerTool( | ||
toolName, | ||
toolFunc, | ||
toolDescription, | ||
toolParameters | ||
); | ||
|
||
const result = toolsManager.executeTool(toolName, {}); | ||
assert.strictEqual(result, null); | ||
}); | ||
|
||
test("getToolsForOpenAI should return empty array when no tools are registered", () => { | ||
const tools = toolsManager.getToolsForOpenAI(); | ||
assert.strictEqual(tools.length, 0); | ||
}); | ||
|
||
test("registerTool should handle complex parameter structures", () => { | ||
const toolName = "complex_tool"; | ||
const toolFunc = () => ({}); | ||
const toolDescription = "A tool with complex parameters"; | ||
const toolParameters = { | ||
type: "object", | ||
properties: { | ||
stringArg: { type: "string" }, | ||
numberArg: { type: "number" }, | ||
booleanArg: { type: "boolean" }, | ||
arrayArg: { | ||
type: "array", | ||
items: { type: "string" }, | ||
}, | ||
objectArg: { | ||
type: "object", | ||
properties: { | ||
nestedProp: { type: "string" }, | ||
}, | ||
}, | ||
}, | ||
required: ["stringArg", "numberArg"], | ||
}; | ||
|
||
toolsManager.registerTool( | ||
toolName, | ||
toolFunc, | ||
toolDescription, | ||
toolParameters | ||
); | ||
|
||
const tools = toolsManager.getToolsForOpenAI(); | ||
assert.strictEqual(tools.length, 1); | ||
assert.deepStrictEqual(tools[0].function.parameters, toolParameters); | ||
}); | ||
}); |