summaryrefslogtreecommitdiffhomepage
path: root/js/src/tool
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/tool')
-rw-r--r--js/src/tool/bash.ts8
-rw-r--r--js/src/tool/edit.ts20
-rw-r--r--js/src/tool/tool.ts67
-rw-r--r--js/src/tool/view.ts54
4 files changed, 67 insertions, 82 deletions
diff --git a/js/src/tool/bash.ts b/js/src/tool/bash.ts
index d5428432c..fb5f18069 100644
--- a/js/src/tool/bash.ts
+++ b/js/src/tool/bash.ts
@@ -1,5 +1,5 @@
import { z } from "zod";
-import { tool } from "./tool";
+import { Tool, tool } from "./tool";
const MAX_OUTPUT_LENGTH = 30000;
const BANNED_COMMANDS = [
@@ -170,7 +170,7 @@ Important:
- Return an empty response - the user will see the gh output directly
- Never update git config`;
-export const BashTool = tool({
+export const BashTool = Tool.define({
name: "bash",
description: DESCRIPTION,
parameters: z.object({
@@ -193,7 +193,9 @@ export const BashTool = tool({
timeout: timeout,
});
return {
- content: process.stdout.toString("utf-8"),
+ output: {
+ content: process.stdout.toString("utf-8"),
+ },
};
},
});
diff --git a/js/src/tool/edit.ts b/js/src/tool/edit.ts
index 2427b19b6..24bf29bcb 100644
--- a/js/src/tool/edit.ts
+++ b/js/src/tool/edit.ts
@@ -1,11 +1,11 @@
import { z } from "zod";
-import { tool } from "./tool";
import * as fs from "fs";
import * as path from "path";
import { Log } from "../util/log";
-import { App } from "../app";
+import { Tool } from "./tool";
+import { FileTimes } from "./util/file-times";
-const log = Log.create({ service: "edit-tool" });
+const log = Log.create({ service: "tool.edit" });
// Simple diff generation
function generateDiff(
@@ -116,7 +116,7 @@ When making edits:
Remember: when making multiple file edits in a row to the same file, you should prefer to send all edits in a single message with multiple calls to this tool, rather than multiple messages with a single call each.`;
-export const EditTool = tool({
+export const EditTool = Tool.define({
name: "edit",
description: DESCRIPTION,
parameters: z.object({
@@ -136,14 +136,20 @@ export const EditTool = tool({
// Handle different operations based on parameters
if (params.old_string === "") {
- return createNewFile(filePath, params.new_string);
+ return {
+ output: createNewFile(filePath, params.new_string),
+ };
}
if (params.new_string === "") {
- return deleteContent(filePath, params.old_string);
+ return {
+ output: deleteContent(filePath, params.old_string),
+ };
}
- return replaceContent(filePath, params.old_string, params.new_string);
+ return {
+ output: replaceContent(filePath, params.old_string, params.new_string),
+ };
},
});
diff --git a/js/src/tool/tool.ts b/js/src/tool/tool.ts
index 08ab935b3..748025707 100644
--- a/js/src/tool/tool.ts
+++ b/js/src/tool/tool.ts
@@ -1,28 +1,49 @@
-import { type Tool, tool as AITool } from "ai";
+import { tool, type Tool as AITool } from "ai";
import { Log } from "../util/log";
const log = Log.create({ service: "tool" });
-export function tool<Params, Result>(
- tool: Tool<Params, Result> & {
- name: string;
- },
-) {
- return {
- [tool.name]: AITool({
- ...tool,
- execute: async (params, opts) => {
- log.info("invoking", {
- id: opts.toolCallId,
- name: tool.name,
- ...params,
- });
- try {
- return tool.execute!(params, opts);
- } catch (e: any) {
- return "An error occurred: " + e.toString();
- }
- },
- }),
- };
+export namespace Tool {
+ export interface Metadata {
+ properties: Record<string, any>;
+ time: {
+ start: number;
+ end: number;
+ };
+ }
+ export function define<Params, Output>(
+ input: AITool<Params, { metadata?: any; output: Output }> & {
+ name: string;
+ },
+ ) {
+ return {
+ [input.name]: tool({
+ ...input,
+ execute: async (params, opts) => {
+ log.info("invoking", {
+ id: opts.toolCallId,
+ name: input.name,
+ ...params,
+ });
+ try {
+ const start = Date.now();
+ const result = await input.execute!(params, opts);
+ const metadata: Metadata = {
+ properties: result.metadata,
+ time: {
+ start,
+ end: Date.now(),
+ },
+ };
+ return {
+ metadata,
+ output: result.output,
+ };
+ } catch (e: any) {
+ return "An error occurred: " + e.toString();
+ }
+ },
+ }),
+ };
+ }
}
diff --git a/js/src/tool/view.ts b/js/src/tool/view.ts
index 5e4223bef..ba6fabbfa 100644
--- a/js/src/tool/view.ts
+++ b/js/src/tool/view.ts
@@ -1,7 +1,7 @@
import { z } from "zod";
-import { tool } from "./tool";
import * as fs from "fs";
import * as path from "path";
+import { Tool } from "./tool";
const MAX_READ_SIZE = 250 * 1024;
const DEFAULT_READ_LIMIT = 2000;
@@ -38,7 +38,7 @@ TIPS:
- For code exploration, first use Grep to find relevant files, then View to examine them
- When viewing large files, use the offset parameter to read specific sections`;
-export const ViewTool = tool({
+export const ViewTool = Tool.define({
name: "view",
description: DESCRIPTION,
parameters: z.object({
@@ -117,56 +117,12 @@ export const ViewTool = tool({
}
output += "\n</file>";
- return output;
+ return {
+ output: output,
+ };
},
});
-function addLineNumbers(content: string, startLine: number): string {
- if (!content) {
- return "";
- }
-
- const lines = content.split("\n");
-
- return lines
- .map((line, i) => {
- const lineNum = i + startLine;
- const numStr = lineNum.toString();
-
- if (numStr.length >= 6) {
- return `${numStr}|${line}`;
- } else {
- const paddedNum = numStr.padStart(6, " ");
- return `${paddedNum}|${line}`;
- }
- })
- .join("\n");
-}
-
-function readTextFile(
- filePath: string,
- offset: number,
- limit: number,
-): { content: string; lineCount: number } {
- const fileContent = fs.readFileSync(filePath, "utf8");
- const allLines = fileContent.split("\n");
- let lineCount = allLines.length;
-
- // Get the lines we want based on offset and limit
- const selectedLines = allLines
- .slice(offset, offset + limit)
- .map((line) =>
- line.length > MAX_LINE_LENGTH
- ? line.substring(0, MAX_LINE_LENGTH) + "..."
- : line,
- );
-
- return {
- content: selectedLines.join("\n"),
- lineCount,
- };
-}
-
function isImageFile(filePath: string): string | false {
const ext = path.extname(filePath).toLowerCase();
switch (ext) {