Add mistake tracking to prevent indefinitely running loops

This commit is contained in:
Saoud Rizwan
2024-09-03 06:12:30 -04:00
parent ce71ed7cba
commit 7801f02d97
4 changed files with 61 additions and 0 deletions

View File

@@ -267,6 +267,7 @@ export class ClaudeDev {
private askResponseImages?: string[]
private lastMessageTs?: number
private executeCommandRunningProcess?: ResultPromise
private mistakeCount: number = 0
private shouldSkipNextApiReqStartedMessage = false
private providerRef: WeakRef<ClaudeDevProvider>
private abort: boolean = false
@@ -714,6 +715,7 @@ export class ClaudeDev {
text: "If you have completed the user's task, use the attempt_completion tool. If you require additional information from the user, use the ask_followup_question tool. Otherwise, if you have not completed the task and do not need additional information, then proceed with the next step of the task. (This is an automated message, so do not respond to it conversationally.)",
},
]
this.mistakeCount++
}
}
}
@@ -777,6 +779,7 @@ export class ClaudeDev {
"error",
"Claude tried to use write_to_file without value for required parameter 'path'. Retrying..."
)
this.mistakeCount++
return "Error: Missing value for required parameter 'path'. Please retry with complete response."
}
@@ -786,6 +789,7 @@ export class ClaudeDev {
"error",
`Claude tried to use write_to_file for '${relPath}' without value for required parameter 'content'. This is likely due to output token limits. Retrying...`
)
this.mistakeCount++
return "Error: Missing value for required parameter 'content'. Please retry with complete response."
}
@@ -950,6 +954,7 @@ export class ClaudeDev {
"error",
"Claude tried to use read_file without value for required parameter 'path'. Retrying..."
)
this.mistakeCount++
return "Error: Missing value for required parameter 'path'. Please retry with complete response."
}
try {
@@ -991,6 +996,7 @@ export class ClaudeDev {
"error",
"Claude tried to use list_files without value for required parameter 'path'. Retrying..."
)
this.mistakeCount++
return "Error: Missing value for required parameter 'path'. Please retry with complete response."
}
try {
@@ -1095,6 +1101,7 @@ export class ClaudeDev {
"error",
"Claude tried to use list_code_definition_names without value for required parameter 'path'. Retrying..."
)
this.mistakeCount++
return "Error: Missing value for required parameter 'path'. Please retry with complete response."
}
try {
@@ -1138,6 +1145,7 @@ export class ClaudeDev {
"error",
"Claude tried to use search_files without value for required parameter 'path'. Retrying..."
)
this.mistakeCount++
return "Error: Missing value for required parameter 'path'. Please retry with complete response."
}
@@ -1146,6 +1154,7 @@ export class ClaudeDev {
"error",
`Claude tried to use search_files without value for required parameter 'regex'. Retrying...`
)
this.mistakeCount++
return "Error: Missing value for required parameter 'regex'. Please retry with complete response."
}
@@ -1191,6 +1200,7 @@ export class ClaudeDev {
"error",
"Claude tried to use execute_command without value for required parameter 'command'. Retrying..."
)
this.mistakeCount++
return "Error: Missing value for required parameter 'command'. Please retry with complete response."
}
const { response, text, images } = await this.ask("command", command)
@@ -1320,6 +1330,7 @@ export class ClaudeDev {
"error",
"Claude tried to use ask_followup_question without value for required parameter 'question'. Retrying..."
)
this.mistakeCount++
return "Error: Missing value for required parameter 'question'. Please retry with complete response."
}
const { text, images } = await this.ask("followup", question)
@@ -1334,6 +1345,7 @@ export class ClaudeDev {
"error",
"Claude tried to use attempt_completion without value for required parameter 'result'. Retrying..."
)
this.mistakeCount++
return "Error: Missing value for required parameter 'result'. Please retry with complete response."
}
let resultToSend = result
@@ -1422,6 +1434,27 @@ ${this.customInstructions.trim()}
throw new Error("ClaudeDev instance aborted")
}
if (this.mistakeCount >= 3) {
const { response, text, images } = await this.ask(
"mistake_limit_reached",
`This may indicate a failure in his thought process or inability to use a tool properly, which can be alleviated with some user direction (e.g. "let's try breaking this large file down into smaller files").`
)
if (response === "yesButtonTapped") {
// proceed anyways
this.mistakeCount = 0
} else {
userContent.push(
...[
{
type: "text",
text: `You seem to be having trouble proceeding. The user has provided the following feedback to help guide you:\n<feedback>\n${text}\n</feedback>\n\n${await this.getPotentiallyRelevantDetails()}`,
} as Anthropic.Messages.TextBlockParam,
...this.formatImagesIntoBlocks(images),
]
)
}
}
await this.addToApiConversationHistory({ role: "user", content: userContent })
if (!this.shouldSkipNextApiReqStartedMessage) {

View File

@@ -42,6 +42,7 @@ export type ClaudeAsk =
| "api_req_failed"
| "resume_task"
| "resume_completed_task"
| "mistake_limit_reached"
export type ClaudeSay =
| "task"

View File

@@ -64,6 +64,13 @@ const ChatRow: React.FC<ChatRowProps> = ({
style={{ color: errorColor, marginBottom: "-1.5px" }}></span>,
<span style={{ color: errorColor, fontWeight: "bold" }}>Error</span>,
]
case "mistake_limit_reached":
return [
<span
className="codicon codicon-error"
style={{ color: errorColor, marginBottom: "-1.5px" }}></span>,
<span style={{ color: errorColor, fontWeight: "bold" }}>Claude is having trouble...</span>,
]
case "command":
return [
isCommandExecuting ? (
@@ -403,6 +410,16 @@ const ChatRow: React.FC<ChatRowProps> = ({
switch (message.ask) {
case "tool":
return renderTool(message, headerStyle)
case "mistake_limit_reached":
return (
<>
<div style={headerStyle}>
{icon}
{title}
</div>
<p style={{ ...pStyle, color: "var(--vscode-errorForeground)" }}>{message.text}</p>
</>
)
case "command":
const splitMessage = (text: string) => {
const outputIndex = text.indexOf(COMMAND_OUTPUT_STRING)

View File

@@ -102,6 +102,13 @@ const ChatView = ({
setPrimaryButtonText("Retry")
setSecondaryButtonText("Start New Task")
break
case "mistake_limit_reached":
setTextAreaDisabled(false)
setClaudeAsk("mistake_limit_reached")
setEnableButtons(true)
setPrimaryButtonText("Proceed Anyways")
setSecondaryButtonText("Start New Task")
break
case "followup":
setTextAreaDisabled(false)
setClaudeAsk("followup")
@@ -225,6 +232,7 @@ const ChatView = ({
case "completion_result": // if this happens then the user has feedback for the completion result
case "resume_task":
case "resume_completed_task":
case "mistake_limit_reached":
vscode.postMessage({
type: "askResponse",
askResponse: "messageResponse",
@@ -269,6 +277,7 @@ const ChatView = ({
case "command_output":
case "tool":
case "resume_task":
case "mistake_limit_reached":
vscode.postMessage({ type: "askResponse", askResponse: "yesButtonTapped" })
break
case "completion_result":
@@ -287,6 +296,7 @@ const ChatView = ({
const handleSecondaryButtonClick = () => {
switch (claudeAsk) {
case "api_req_failed":
case "mistake_limit_reached":
startNewTask()
break
case "command":