mirror of
https://github.com/pacnpal/Roo-Code.git
synced 2025-12-20 12:21:13 -05:00
Update system prompt to tool call more reliably
This commit is contained in:
@@ -227,21 +227,24 @@ export class ClaudeDev {
|
||||
}
|
||||
let askTs: number
|
||||
if (partial !== undefined) {
|
||||
const lastMessage = this.claudeMessages.at(-1)
|
||||
const lastMessageOfType = findLast(this.claudeMessages, (m) => m.ask === type)
|
||||
const isUpdatingPreviousPartial =
|
||||
lastMessage && lastMessage.partial && lastMessage.type === "ask" && lastMessage.ask === type
|
||||
lastMessageOfType &&
|
||||
lastMessageOfType.partial &&
|
||||
lastMessageOfType.type === "ask" &&
|
||||
lastMessageOfType.ask === type
|
||||
if (partial) {
|
||||
if (isUpdatingPreviousPartial) {
|
||||
// existing partial message, so update it
|
||||
lastMessage.text = text
|
||||
lastMessage.partial = partial
|
||||
lastMessageOfType.text = text
|
||||
lastMessageOfType.partial = partial
|
||||
// todo be more efficient about saving and posting only new data or one whole message at a time so ignore partial for saves, and only post parts of partial message instead of whole array in new listener
|
||||
// await this.saveClaudeMessages()
|
||||
// await this.providerRef.deref()?.postStateToWebview()
|
||||
await this.providerRef
|
||||
.deref()
|
||||
?.postMessageToWebview({ type: "partialMessage", partialMessage: lastMessage })
|
||||
throw new Error("Current ask promise was ignored")
|
||||
?.postMessageToWebview({ type: "partialMessage", partialMessage: lastMessageOfType })
|
||||
throw new Error("Current ask promise was ignored 1")
|
||||
} else {
|
||||
// this is a new partial message, so add it with partial state
|
||||
// this.askResponse = undefined
|
||||
@@ -251,7 +254,7 @@ export class ClaudeDev {
|
||||
// this.lastMessageTs = askTs
|
||||
await this.addToClaudeMessages({ ts: Date.now(), type: "ask", ask: type, text, partial })
|
||||
await this.providerRef.deref()?.postStateToWebview()
|
||||
throw new Error("Current ask promise was ignored")
|
||||
throw new Error("Current ask promise was ignored 2")
|
||||
}
|
||||
} else {
|
||||
// partial=false means its a complete version of a previously partial message
|
||||
@@ -262,9 +265,9 @@ export class ClaudeDev {
|
||||
this.askResponseImages = undefined
|
||||
askTs = Date.now()
|
||||
this.lastMessageTs = askTs
|
||||
lastMessage.ts = askTs
|
||||
lastMessage.text = text
|
||||
lastMessage.partial = false
|
||||
lastMessageOfType.ts = askTs
|
||||
lastMessageOfType.text = text
|
||||
lastMessageOfType.partial = false
|
||||
await this.saveClaudeMessages()
|
||||
await this.providerRef.deref()?.postStateToWebview()
|
||||
} else {
|
||||
@@ -319,20 +322,23 @@ export class ClaudeDev {
|
||||
}
|
||||
|
||||
if (partial !== undefined) {
|
||||
const lastMessage = this.claudeMessages.at(-1)
|
||||
const lastMessageOfType = findLast(this.claudeMessages, (m) => m.say === type)
|
||||
const isUpdatingPreviousPartial =
|
||||
lastMessage && lastMessage.partial && lastMessage.type === "say" && lastMessage.say === type
|
||||
lastMessageOfType &&
|
||||
lastMessageOfType.partial &&
|
||||
lastMessageOfType.type === "say" &&
|
||||
lastMessageOfType.say === type
|
||||
if (partial) {
|
||||
if (isUpdatingPreviousPartial) {
|
||||
// existing partial message, so update it
|
||||
lastMessage.text = text
|
||||
lastMessage.images = images
|
||||
lastMessage.partial = partial
|
||||
lastMessageOfType.text = text
|
||||
lastMessageOfType.images = images
|
||||
lastMessageOfType.partial = partial
|
||||
// await this.saveClaudeMessages()
|
||||
// await this.providerRef.deref()?.postStateToWebview()
|
||||
await this.providerRef
|
||||
.deref()
|
||||
?.postMessageToWebview({ type: "partialMessage", partialMessage: lastMessage })
|
||||
?.postMessageToWebview({ type: "partialMessage", partialMessage: lastMessageOfType })
|
||||
} else {
|
||||
// this is a new partial message, so add it with partial state
|
||||
|
||||
@@ -345,10 +351,10 @@ export class ClaudeDev {
|
||||
// this is the complete version of a previously partial message, so replace the partial with the complete version
|
||||
const sayTs = Date.now()
|
||||
this.lastMessageTs = sayTs
|
||||
lastMessage.ts = sayTs
|
||||
lastMessage.text = text
|
||||
lastMessage.images = images
|
||||
lastMessage.partial = false
|
||||
lastMessageOfType.ts = sayTs
|
||||
lastMessageOfType.text = text
|
||||
lastMessageOfType.images = images
|
||||
lastMessageOfType.partial = false
|
||||
|
||||
// instead of streaming partialMessage events, we do a save and post like normal to persist to disk
|
||||
await this.saveClaudeMessages()
|
||||
@@ -1673,16 +1679,22 @@ ${this.customInstructions.trim()}
|
||||
}
|
||||
|
||||
// If the last API request's total token usage is close to the context window, truncate the conversation history to free up space for the new request
|
||||
const lastApiReqFinished = findLast(this.claudeMessages, (m) => m.say === "api_req_finished")
|
||||
if (lastApiReqFinished && lastApiReqFinished.text) {
|
||||
const lastApiReqStarted = findLast(this.claudeMessages, (m) => m.say === "api_req_started")
|
||||
if (lastApiReqStarted && lastApiReqStarted.text) {
|
||||
const {
|
||||
tokensIn,
|
||||
tokensOut,
|
||||
cacheWrites,
|
||||
cacheReads,
|
||||
}: { tokensIn?: number; tokensOut?: number; cacheWrites?: number; cacheReads?: number } = JSON.parse(
|
||||
lastApiReqFinished.text
|
||||
lastApiReqStarted.text
|
||||
)
|
||||
console.log("lastApiReqStarted", lastApiReqStarted.text, {
|
||||
tokensIn,
|
||||
tokensOut,
|
||||
cacheWrites,
|
||||
cacheReads,
|
||||
})
|
||||
const totalTokens = (tokensIn || 0) + (tokensOut || 0) + (cacheWrites || 0) + (cacheReads || 0)
|
||||
const contextWindow = this.api.getModel().info.contextWindow
|
||||
const maxAllowedSize = Math.max(contextWindow - 40_000, contextWindow * 0.8)
|
||||
@@ -2494,9 +2506,6 @@ ${this.customInstructions.trim()}
|
||||
textContent.content = textContentLines.join("\n")
|
||||
|
||||
this.assistantMessageContent = [textContent, ...toolCalls]
|
||||
|
||||
// Present the updated content
|
||||
this.presentAssistantMessage()
|
||||
}
|
||||
|
||||
async recursivelyMakeClaudeRequests(
|
||||
@@ -2692,23 +2701,31 @@ ${this.customInstructions.trim()}
|
||||
|
||||
let totalCost: string | undefined
|
||||
|
||||
await this.say(
|
||||
"api_req_finished",
|
||||
JSON.stringify({
|
||||
tokensIn: inputTokens,
|
||||
tokensOut: outputTokens,
|
||||
cacheWrites: cacheCreationInputTokens,
|
||||
cacheReads: cacheReadInputTokens,
|
||||
cost:
|
||||
totalCost ||
|
||||
this.calculateApiCost(
|
||||
inputTokens,
|
||||
outputTokens,
|
||||
cacheCreationInputTokens,
|
||||
cacheReadInputTokens
|
||||
),
|
||||
})
|
||||
)
|
||||
// update api_req_started. we can't use api_req_finished anymore since it's a unique case where it could come after a streaming message (ie in the middle of being updated or executed)
|
||||
// fortunately api_req_finished was always parsed out for the gui anyways, so it remains solely for legacy purposes to keep track of prices in tasks from history
|
||||
// (it's worth removing a few months from now)
|
||||
this.claudeMessages[lastApiReqIndex].text = JSON.stringify({
|
||||
...JSON.parse(this.claudeMessages[lastApiReqIndex].text),
|
||||
tokensIn: inputTokens,
|
||||
tokensOut: outputTokens,
|
||||
cacheWrites: cacheCreationInputTokens,
|
||||
cacheReads: cacheReadInputTokens,
|
||||
cost:
|
||||
totalCost ||
|
||||
this.calculateApiCost(inputTokens, outputTokens, cacheCreationInputTokens, cacheReadInputTokens),
|
||||
})
|
||||
await this.saveClaudeMessages()
|
||||
await this.providerRef.deref()?.postStateToWebview()
|
||||
|
||||
// await this.say(
|
||||
// "api_req_finished",
|
||||
// JSON.stringify({
|
||||
|
||||
// })
|
||||
// )
|
||||
|
||||
// console.log("apiContentBlocks", apiContentBlocks)
|
||||
// throw new Error("ClaudeDev fail")
|
||||
|
||||
// now add to apiconversationhistory
|
||||
// need to save assistant responses to file before proceeding to tool use since user can exit at any moment and we wouldn't be able to save the assistant's response
|
||||
@@ -2725,6 +2742,10 @@ ${this.customInstructions.trim()}
|
||||
|
||||
await pWaitFor(() => this.userMessageContentReady)
|
||||
|
||||
console.log("attempted to send new request")
|
||||
|
||||
// throw new Error("ClaudeDev fail")
|
||||
|
||||
const recDidEndLoop = await this.recursivelyMakeClaudeRequests(this.userMessageContent)
|
||||
didEndLoop = recDidEndLoop
|
||||
} else {
|
||||
|
||||
@@ -77,15 +77,14 @@ INSTRUCTIONS FOR FORMULATING YOUR RESPONSE
|
||||
|
||||
You must respond to the user's request by using at least one tool call. When formulating your response, follow these guidelines:
|
||||
|
||||
1. Begin your response with normal text, explaining your thoughts, analysis, or plan of action.
|
||||
2. If you need to use any tools, place ALL tool calls at the END of your message, after your normal text explanation.
|
||||
1. You might begin your response explaining your thoughts, analysis, plan of action, etc.
|
||||
2. Place ALL tool calls at the END of your message.
|
||||
3. You can use multiple tool calls if needed, but they should all be grouped together at the end of your message.
|
||||
4. After placing the tool calls, do not add any additional normal text. The tool calls should be the final content in your message.
|
||||
|
||||
Here's the general structure your responses should follow:
|
||||
|
||||
\`\`\`
|
||||
[Your normal text response explaining your thoughts and actions]
|
||||
...Your thoughts...
|
||||
|
||||
[Tool Call 1]
|
||||
[Tool Call 2 if needed]
|
||||
@@ -96,10 +95,25 @@ Here's the general structure your responses should follow:
|
||||
Remember:
|
||||
- Choose the most appropriate tool(s) based on the task and the tool descriptions provided.
|
||||
- Formulate your tool calls using the XML format specified for each tool.
|
||||
- Provide clear explanations in your normal text about what actions you're taking and why you're using particular tools.
|
||||
- Provide clear explanations about what actions you're taking and why you're using particular tools.
|
||||
- Act as if the tool calls will be executed immediately after your message, and your next response will have access to their results.
|
||||
|
||||
# Tool Descriptions and XML Formats
|
||||
# Tool Calls Formatting
|
||||
|
||||
Tool calls are formatted with the name of the tool enclosed in XML tags on their own line.
|
||||
Each parameter is defined within its own set of XML tags, also each on their own line.
|
||||
Example:
|
||||
<tool_name>
|
||||
<parameter1_name>
|
||||
value1
|
||||
</parameter1_name>
|
||||
<parameter2_name>
|
||||
value2
|
||||
</parameter2_name>
|
||||
</tool_name>
|
||||
Ensure that each tool call follows this structure for consistent parsing and execution.
|
||||
|
||||
# Tool Descriptions
|
||||
|
||||
## execute_command
|
||||
<execute_command>
|
||||
@@ -217,14 +231,10 @@ Parameters:
|
||||
- command: (optional) A CLI command to execute to show a live demo of the result to the user. For example, use 'open index.html' to display a created website. This command should be valid for the current operating system. Ensure the command is properly formatted and does not contain any harmful instructions.
|
||||
|
||||
|
||||
# Examples
|
||||
|
||||
Here are some examples of how to structure your responses with tool calls:
|
||||
# Tool Calls Examples
|
||||
|
||||
## Example 1: Using a single tool
|
||||
|
||||
Let's run the test suite for our project. This will help us ensure that all our components are functioning correctly.
|
||||
|
||||
<execute_command>
|
||||
<command>
|
||||
npm test
|
||||
@@ -233,8 +243,6 @@ npm test
|
||||
|
||||
## Example 2: Using multiple tools
|
||||
|
||||
Let's create two new configuration files for the web application, one for the frontend and one for the backend.
|
||||
|
||||
<write_to_file>
|
||||
<path>
|
||||
./frontend-config.json
|
||||
@@ -281,8 +289,6 @@ externalServices:
|
||||
|
||||
## Example 3: Asking a follow-up question
|
||||
|
||||
I've analyzed the project structure, but I need more information to proceed. Let me ask the user for clarification.
|
||||
|
||||
<ask_followup_question>
|
||||
<question>
|
||||
Which specific feature would you like me to implement in the example.py file?
|
||||
|
||||
@@ -426,23 +426,29 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
|
||||
[isAtBottom, visibleMessages, expandedRows]
|
||||
)
|
||||
|
||||
const [lastScrollMessageCount, setLastScrollMessageCount] = useState<number>(0)
|
||||
|
||||
useEffect(() => {
|
||||
// dont scroll if we're just updating the api req started informational body
|
||||
const lastMessage = visibleMessages.at(-1)
|
||||
const isLastApiReqStarted = lastMessage?.say === "api_req_started"
|
||||
if (didScrollFromApiReqTs && isLastApiReqStarted && lastMessage?.ts === didScrollFromApiReqTs) {
|
||||
return
|
||||
if (lastMessage?.partial && isAtBottom) {
|
||||
virtuosoRef.current?.scrollTo({ top: Number.MAX_SAFE_INTEGER, behavior: "auto" })
|
||||
} else {
|
||||
// dont scroll if we're just updating the api req started informational body
|
||||
|
||||
const isLastApiReqStarted = lastMessage?.say === "api_req_started"
|
||||
if (didScrollFromApiReqTs && isLastApiReqStarted && lastMessage?.ts === didScrollFromApiReqTs) {
|
||||
return
|
||||
}
|
||||
// We use a setTimeout to ensure new content is rendered before scrolling to the bottom. virtuoso's followOutput would scroll to the bottom before the new content could render.
|
||||
const timer = setTimeout(() => {
|
||||
// TODO: we can use virtuoso's isAtBottom to prevent scrolling if user is scrolled up, and show a 'scroll to bottom' button for better UX
|
||||
// NOTE: scroll to bottom may not work if you use margin, see virtuoso's troubleshooting
|
||||
virtuosoRef.current?.scrollTo({ top: Number.MAX_SAFE_INTEGER, behavior: "smooth" })
|
||||
setDidScrollFromApiReqTs(isLastApiReqStarted ? lastMessage?.ts : undefined) // need to do this in timer since this effect can get called a few times simultaneously
|
||||
}, 50)
|
||||
|
||||
return () => clearTimeout(timer)
|
||||
}
|
||||
|
||||
// We use a setTimeout to ensure new content is rendered before scrolling to the bottom. virtuoso's followOutput would scroll to the bottom before the new content could render.
|
||||
const timer = setTimeout(() => {
|
||||
// TODO: we can use virtuoso's isAtBottom to prevent scrolling if user is scrolled up, and show a 'scroll to bottom' button for better UX
|
||||
// NOTE: scroll to bottom may not work if you use margin, see virtuoso's troubleshooting
|
||||
virtuosoRef.current?.scrollTo({ top: Number.MAX_SAFE_INTEGER, behavior: "smooth" })
|
||||
setDidScrollFromApiReqTs(isLastApiReqStarted ? lastMessage?.ts : undefined) // need to do this in timer since this effect can get called a few times simultaneously
|
||||
}, 50)
|
||||
|
||||
return () => clearTimeout(timer)
|
||||
}, [visibleMessages, didScrollFromApiReqTs])
|
||||
|
||||
const placeholderText = useMemo(() => {
|
||||
|
||||
Reference in New Issue
Block a user