Refactor tool call to tool use

This commit is contained in:
Saoud Rizwan
2024-09-29 03:03:03 -04:00
parent 156f735c89
commit 204116c6d3
2 changed files with 43 additions and 43 deletions

View File

@@ -31,11 +31,11 @@ import { arePathsEqual, getReadablePath } from "../utils/path"
import { import {
AssistantMessageContent, AssistantMessageContent,
TextContent, TextContent,
ToolCall,
ToolCallName,
toolCallNames,
ToolParamName, ToolParamName,
toolParamNames, toolParamNames,
ToolUse,
ToolUseName,
toolUseNames,
} from "./prompts/AssistantMessage" } from "./prompts/AssistantMessage"
import { parseMentions } from "./mentions" import { parseMentions } from "./mentions"
import { formatResponse } from "./prompts/responses" import { formatResponse } from "./prompts/responses"
@@ -1140,7 +1140,7 @@ export class ClaudeDev {
case "text": case "text":
await this.say("text", block.content, undefined, block.partial) await this.say("text", block.content, undefined, block.partial)
break break
case "tool_call": case "tool_use":
const toolDescription = () => { const toolDescription = () => {
switch (block.name) { switch (block.name) {
case "execute_command": case "execute_command":
@@ -2058,9 +2058,9 @@ export class ClaudeDev {
content: "", content: "",
partial: true, partial: true,
} }
let toolCalls: ToolCall[] = [] let toolUses: ToolUse[] = []
let currentToolCall: ToolCall | undefined = undefined let currentToolUse: ToolUse | undefined = undefined
let currentParamName: ToolParamName | undefined = undefined let currentParamName: ToolParamName | undefined = undefined
let currentParamValueLines: string[] = [] let currentParamValueLines: string[] = []
let textContentLines: string[] = [] let textContentLines: string[] = []
@@ -2090,42 +2090,47 @@ export class ClaudeDev {
for (const line of rawLines) { for (const line of rawLines) {
const trimmed = line.trim() const trimmed = line.trim()
// if currenttoolcall or currentparamname look for closing tag, more efficient and safe // if currenttoolcall or currentparamname look for closing tag, more efficient and safe
if (currentToolCall && currentParamName && trimmed === `</${currentParamName}>`) { if (currentToolUse && currentParamName && trimmed === `</${currentParamName}>`) {
// End of a tool parameter // End of a tool parameter
currentToolCall.params[currentParamName] = currentParamValueLines.join("\n") currentToolUse.params[currentParamName] = currentParamValueLines.join("\n")
currentParamName = undefined currentParamName = undefined
currentParamValueLines = [] currentParamValueLines = []
// currentParamValue = undefined // currentParamValue = undefined
continue continue
} else if (currentToolCall && !currentParamName && trimmed === `</${currentToolCall.name}>`) { } else if (currentToolUse && !currentParamName && trimmed === `</${currentToolUse.name}>`) {
// End of a tool call // End of a tool call
currentToolCall.partial = false currentToolUse.partial = false
toolCalls.push(currentToolCall) toolUses.push(currentToolUse)
currentToolCall = undefined currentToolUse = undefined
continue continue
} }
if (!currentParamName && trimmed.startsWith("<") && trimmed.endsWith(">")) { if (!currentParamName && trimmed.startsWith("<") && trimmed.endsWith(">")) {
const tag = trimmed.slice(1, -1) const tag = trimmed.slice(1, -1)
if (toolCallNames.includes(tag as ToolCallName)) { if (toolUseNames.includes(tag as ToolUseName)) {
// Start of a new tool call // Start of a new tool call
currentToolCall = { type: "tool_call", name: tag as ToolCallName, params: {}, partial: true } currentToolUse = {
type: "tool_use",
name: tag as ToolUseName,
params: {},
partial: true,
} satisfies ToolUse
// This also indicates the end of the text content // This also indicates the end of the text content
textContent.partial = false textContent.partial = false
continue continue
} else if (currentToolCall && toolParamNames.includes(tag as ToolParamName)) { } else if (currentToolUse && toolParamNames.includes(tag as ToolParamName)) {
// Start of a parameter // Start of a parameter
currentParamName = tag as ToolParamName currentParamName = tag as ToolParamName
// currentToolCall.params[currentParamName] = "" // currentToolUse.params[currentParamName] = ""
continue continue
} }
} }
if (currentToolCall && !currentParamName) { if (currentToolUse && !currentParamName) {
// current tool doesn't have a param match yet, it's likely partial so ignore // current tool doesn't have a param match yet, it's likely partial so ignore
continue continue
} }
if (currentToolCall && currentParamName) { if (currentToolUse && currentParamName) {
// add line to current param value // add line to current param value
currentParamValueLines.push(line) currentParamValueLines.push(line)
continue continue
@@ -2137,18 +2142,18 @@ export class ClaudeDev {
} }
} }
if (currentToolCall) { if (currentToolUse) {
// stream did not complete tool call, add it as partial // stream did not complete tool call, add it as partial
if (currentParamName) { if (currentParamName) {
// tool call has a parameter that was not completed // tool call has a parameter that was not completed
currentToolCall.params[currentParamName] = currentParamValueLines.join("\n") currentToolUse.params[currentParamName] = currentParamValueLines.join("\n")
} }
toolCalls.push(currentToolCall) toolUses.push(currentToolUse)
} }
textContent.content = textContentLines.join("\n") textContent.content = textContentLines.join("\n")
const prevLength = this.assistantMessageContent.length const prevLength = this.assistantMessageContent.length
this.assistantMessageContent = [textContent, ...toolCalls] this.assistantMessageContent = [textContent, ...toolUses]
if (this.assistantMessageContent.length > prevLength) { if (this.assistantMessageContent.length > prevLength) {
this.userMessageContentReady = false // new content we need to present, reset to false in case previous content set this to true this.userMessageContentReady = false // new content we need to present, reset to false in case previous content set this to true
} }
@@ -2321,7 +2326,7 @@ export class ClaudeDev {
await pWaitFor(() => this.userMessageContentReady) await pWaitFor(() => this.userMessageContentReady)
// if the model did not tool use, then we need to tell it to either use a tool or attempt_completion // if the model did not tool use, then we need to tell it to either use a tool or attempt_completion
const didToolUse = this.assistantMessageContent.some((block) => block.type === "tool_call") const didToolUse = this.assistantMessageContent.some((block) => block.type === "tool_use")
if (!didToolUse) { if (!didToolUse) {
this.userMessageContent.push({ this.userMessageContent.push({
type: "text", type: "text",

View File

@@ -1,9 +1,4 @@
// export interface AssistantMessage { export type AssistantMessageContent = TextContent | ToolUse
// textContent: TextContent
// toolCalls: ToolCall[]
// }
export type AssistantMessageContent = TextContent | ToolCall
export interface TextContent { export interface TextContent {
type: "text" type: "text"
@@ -11,7 +6,7 @@ export interface TextContent {
partial: boolean partial: boolean
} }
export const toolCallNames = [ export const toolUseNames = [
"execute_command", "execute_command",
"read_file", "read_file",
"write_to_file", "write_to_file",
@@ -24,7 +19,7 @@ export const toolCallNames = [
] as const ] as const
// Converts array of tool call names into a union type ("execute_command" | "read_file" | ...) // Converts array of tool call names into a union type ("execute_command" | "read_file" | ...)
export type ToolCallName = (typeof toolCallNames)[number] export type ToolUseName = (typeof toolUseNames)[number]
export const toolParamNames = [ export const toolParamNames = [
"command", "command",
@@ -40,56 +35,56 @@ export const toolParamNames = [
export type ToolParamName = (typeof toolParamNames)[number] export type ToolParamName = (typeof toolParamNames)[number]
export interface ToolCall { export interface ToolUse {
type: "tool_call" type: "tool_use"
name: ToolCallName name: ToolUseName
// params is a partial record, allowing only some or none of the possible parameters to be used // params is a partial record, allowing only some or none of the possible parameters to be used
params: Partial<Record<ToolParamName, string>> params: Partial<Record<ToolParamName, string>>
partial: boolean partial: boolean
} }
interface ExecuteCommandToolCall extends ToolCall { export interface ExecuteCommandToolUse extends ToolUse {
name: "execute_command" name: "execute_command"
// Pick<Record<ToolParamName, string>, "command"> makes "command" required, but Partial<> makes it optional // Pick<Record<ToolParamName, string>, "command"> makes "command" required, but Partial<> makes it optional
params: Partial<Pick<Record<ToolParamName, string>, "command">> params: Partial<Pick<Record<ToolParamName, string>, "command">>
} }
interface ReadFileToolCall extends ToolCall { export interface ReadFileToolUse extends ToolUse {
name: "read_file" name: "read_file"
params: Partial<Pick<Record<ToolParamName, string>, "path">> params: Partial<Pick<Record<ToolParamName, string>, "path">>
} }
interface WriteToFileToolCall extends ToolCall { export interface WriteToFileToolUse extends ToolUse {
name: "write_to_file" name: "write_to_file"
params: Partial<Pick<Record<ToolParamName, string>, "path" | "content">> params: Partial<Pick<Record<ToolParamName, string>, "path" | "content">>
} }
interface SearchFilesToolCall extends ToolCall { export interface SearchFilesToolUse extends ToolUse {
name: "search_files" name: "search_files"
params: Partial<Pick<Record<ToolParamName, string>, "path" | "regex" | "file_pattern">> params: Partial<Pick<Record<ToolParamName, string>, "path" | "regex" | "file_pattern">>
} }
interface ListFilesToolCall extends ToolCall { export interface ListFilesToolUse extends ToolUse {
name: "list_files" name: "list_files"
params: Partial<Pick<Record<ToolParamName, string>, "path" | "recursive">> params: Partial<Pick<Record<ToolParamName, string>, "path" | "recursive">>
} }
interface ListCodeDefinitionNamesToolCall extends ToolCall { export interface ListCodeDefinitionNamesToolUse extends ToolUse {
name: "list_code_definition_names" name: "list_code_definition_names"
params: Partial<Pick<Record<ToolParamName, string>, "path">> params: Partial<Pick<Record<ToolParamName, string>, "path">>
} }
interface InspectSiteToolCall extends ToolCall { export interface InspectSiteToolUse extends ToolUse {
name: "inspect_site" name: "inspect_site"
params: Partial<Pick<Record<ToolParamName, string>, "url">> params: Partial<Pick<Record<ToolParamName, string>, "url">>
} }
interface AskFollowupQuestionToolCall extends ToolCall { export interface AskFollowupQuestionToolUse extends ToolUse {
name: "ask_followup_question" name: "ask_followup_question"
params: Partial<Pick<Record<ToolParamName, string>, "question">> params: Partial<Pick<Record<ToolParamName, string>, "question">>
} }
interface AttemptCompletionToolCall extends ToolCall { export interface AttemptCompletionToolUse extends ToolUse {
name: "attempt_completion" name: "attempt_completion"
params: Partial<Pick<Record<ToolParamName, string>, "result" | "command">> params: Partial<Pick<Record<ToolParamName, string>, "result" | "command">>
} }