diff --git a/legacy/package.json b/legacy/package.json index d2d2e849..8bda68f6 100644 --- a/legacy/package.json +++ b/legacy/package.json @@ -43,6 +43,7 @@ "@inquirer/editor": "^1.2.0", "@inquirer/select": "^1.2.1", "debug": "^4.3.4", + "expr-eval": "^2.0.2", "handlebars": "^4.7.7", "js-tiktoken": "^1.0.6", "jsonrepair": "^3.1.0", diff --git a/legacy/pnpm-lock.yaml b/legacy/pnpm-lock.yaml index 32511920..2d8dd621 100644 --- a/legacy/pnpm-lock.yaml +++ b/legacy/pnpm-lock.yaml @@ -20,6 +20,9 @@ dependencies: debug: specifier: ^4.3.4 version: 4.3.4 + expr-eval: + specifier: ^2.0.2 + version: 2.0.2 handlebars: specifier: ^4.7.7 version: 4.7.7 @@ -1793,6 +1796,10 @@ packages: engines: {node: '>=12.0.0'} dev: true + /expr-eval@2.0.2: + resolution: {integrity: sha512-4EMSHGOPSwAfBiibw3ndnP0AvjDWLsMvGOvWEZ2F96IGk0bIVdjQisOHxReSkE13mHcfbuCiXw+G4y0zv6N8Eg==} + dev: false + /external-editor@3.1.0: resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} engines: {node: '>=4'} diff --git a/legacy/src/tools/calculator.ts b/legacy/src/tools/calculator.ts new file mode 100644 index 00000000..7dbe2c66 --- /dev/null +++ b/legacy/src/tools/calculator.ts @@ -0,0 +1,46 @@ +import { Parser } from 'expr-eval' +import { z } from 'zod' + +import * as types from '@/types' +import { BaseTask } from '@/task' + +export const CalculatorInputSchema = z.string().describe('expression') +export const CalculatorOutputSchema = z + .number() + .describe('result of calculating the expression') + +export type CalculatorInput = z.infer + +export type CalculatorOutput = z.infer + +export class CalculatorTool extends BaseTask< + CalculatorInput, + CalculatorOutput +> { + constructor(opts: types.BaseTaskOptions) { + super(opts) + } + + public override get inputSchema() { + return CalculatorInputSchema + } + + public override get outputSchema() { + return CalculatorOutputSchema + } + + public override get name(): string { + return 'calculator' + } + + public get descriptionForModel(): string { + return 'Useful for getting the result of a math expression. The input to this tool should be a valid mathematical expression that could be executed by a simple calculator.' + } + + protected override async _call( + ctx: types.TaskCallContext + ): Promise { + const result = Parser.evaluate(ctx.input!) + return result + } +} diff --git a/legacy/test/calculator.test.ts b/legacy/test/calculator.test.ts new file mode 100644 index 00000000..f16f39f6 --- /dev/null +++ b/legacy/test/calculator.test.ts @@ -0,0 +1,28 @@ +import test from 'ava' +import { expectTypeOf } from 'expect-type' + +import { CalculatorTool } from '@/tools/calculator' + +import { createTestAgenticRuntime } from './_utils' + +test('CalculatorTool', async (t) => { + const agentic = createTestAgenticRuntime() + const tool = new CalculatorTool({ agentic }) + + const res = await tool.call('1 + 1') + t.is(res, 2) + expectTypeOf(res).toMatchTypeOf() + + const res2 = await tool.callWithMetadata('cos(0)') + t.is(res2.result, 1) + expectTypeOf(res2.result).toMatchTypeOf() + + const { taskId, ...metadata } = res2.metadata + t.true(typeof taskId === 'string') + t.deepEqual(metadata, { + success: true, + taskName: 'calculator', + numRetries: 0, + error: undefined + }) +})