Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs(README): add instructions to implement custom native typescript tool #89

175 changes: 175 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,178 @@ docker run -d -p 27017:27017 mongo:latest
```
docker run -d -p 6379:6379 redis:latest
```

## Implement a Custom Native Typescript Tool (all the way to the UI)

In this example we will implement the tool `RiddleTool`, all the way to the [Bee UI](https://github.com/i-am-bee/bee-ui).

### Bee API

### Create a new custom tool

1. Create a new file in *src/runs/execution/tools* directory. In this example we will create *riddle-tool.ts*.
2. Copy and paste the [riddle example base tool](https://github.com/i-am-bee/bee-agent-framework/blob/main/examples/tools/custom/base.ts). In *riddle-tool.ts*.

### Implement the tool in helpers and services

#### Add the tool in *src/runs/execution/helpers.ts* file.
hebusch marked this conversation as resolved.
Show resolved Hide resolved

1. Import the tool in the file:

```typescript
import { RiddleTool } from "./riddle-tool.js";
```

2. Append the tool to the array `tools` in the `getTools` function:

```typescript
export async function getTools(run: LoadedRun, context: AgentContext): Promise<FrameworkTool[]> {
const tools: FrameworkTool[] = [];

tools.push(new RiddleTool()); // Add the tool to the array
hebusch marked this conversation as resolved.
Show resolved Hide resolved

const vectorStores = getRunVectorStores(run.assistant.$, run.thread.$);
for (const vectorStore of vectorStores) {
vectorStore.lastActiveAt = new Date();
}

...
```
3. Add the tool in `createToolCall` function:

```typescript
...
// Add the tool in the `else if` branch
} else if (tool instanceof RiddleTool) {
return new SystemCall({
toolId: SystemTools.RIDDLE_TOOL,
input: await tool.parse(input)
});
}
throw new Error(`Unknown tool: ${tool.name}`);
}
```
4. Add the tool in `finalizeToolCall` function:

```typescript
...
// Add the tool in the `switch` statement
case SystemTools.RIDDLE_TOOL: {
if (!(result instanceof StringToolOutput)) throw new TypeError();
hebusch marked this conversation as resolved.
Show resolved Hide resolved
toolCall.output = result.result;
break;
}
}
````

#### Add the tool definition in *src/tools/entities/tool-calls/system-call.entity.ts* file:

```typescript
export enum SystemTools {
WEB_SEARCH = 'web_search',
WIKIPEDIA = 'wikipedia',
WEATHER = 'weather',
ARXIV = 'arxiv',
READ_FILE = 'read_file',
RIDDLE_TOOL = 'riddle_tool', // Add the tool definition
}
```

#### Set the tool in the handlers in *src/tools/tools.service.ts* file:

1. Import the tool in the file:

```typescript
import { RiddleTool } from '@/runs/execution/tools/riddle-tool.js';
```

2. Instance the tool in the `getSystemTools` function:

```typescript
function getSystemTools() {
...

const systemTools = new Map<string, SystemTool>();

const riddleTool = new RiddleTool(); // Add this line

...
```
3. Set the tool at the end of `getSystemTools` function:

```typescript
...
// add this block of code
systemTools.set('riddle_tool', {
type: ToolType.SYSTEM,
id: 'riddle_tool',
createdAt: new Date(),
hebusch marked this conversation as resolved.
Show resolved Hide resolved
...riddleTool,
inputSchema: riddleTool.inputSchema.bind(riddleTool),
isExternal: false,
hebusch marked this conversation as resolved.
Show resolved Hide resolved
metadata: {
$ui_description_short:
'It generates a random puzzle to test your knowledge.'
},
userDescription:
'It generates a random puzzle to test your knowledge.'
});

return systemTools;
}
```
4. Add the tool in `listTools` function:

```typescript
...

const systemTools: (SystemTool | undefined)[] =
!type || type.includes(ToolType.SYSTEM)
? [
allSystemTools.get(SystemTools.WEB_SEARCH),
allSystemTools.get(SystemTools.WIKIPEDIA),
allSystemTools.get(SystemTools.WEATHER),
allSystemTools.get(SystemTools.ARXIV),
allSystemTools.get('read_file'),
allSystemTools.get('riddle_tool') // add this line
hebusch marked this conversation as resolved.
Show resolved Hide resolved
]
: [];
...
```
That's it! You have implemented the tool in the Bee API. :rocket:

### Bee UI

For the tool to be available in the UI, you need to follow these steps:

1. Add the tool the the schema in *src/app/api/schema.d.ts* file:
xjacka marked this conversation as resolved.
Show resolved Hide resolved

```typescript
"web_search" | "wikipedia" | "weather" | "arxiv" | "read_file" | "riddle_tool";
```

There are several lines like this in the file. I recommend replacing all of them.

2. Add the tool to `SYSTEM_TOOL_NAME` and `SYSTEM_TOOL_ICONS`in *src/modules/tools/hooks/useToolInfo.tsx* file:

```typescript
const SYSTEM_TOOL_NAME: Record<SystemToolId, string> = {
wikipedia: 'Wikipedia',
web_search: 'WebSearch',
weather: 'OpenMeteo',
arxiv: 'Arxiv',
read_file: 'ReadFile',
riddle_tool: 'RiddleTool', // Add this line
};

const SYSTEM_TOOL_ICONS: Record<SystemToolId, ComponentType> = {
wikipedia: Wikipedia,
web_search: IbmWatsonDiscovery,
weather: PartlyCloudy,
arxiv: Arxiv,
read_file: DocumentView,
riddle_tool: Code, // Add this line
};
```

That's it! You have implemented the tool in the Bee UI. :rocket: