mirror of
https://github.com/pacnpal/Roo-Code.git
synced 2025-12-21 04:41:16 -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
|
let askTs: number
|
||||||
if (partial !== undefined) {
|
if (partial !== undefined) {
|
||||||
const lastMessage = this.claudeMessages.at(-1)
|
const lastMessageOfType = findLast(this.claudeMessages, (m) => m.ask === type)
|
||||||
const isUpdatingPreviousPartial =
|
const isUpdatingPreviousPartial =
|
||||||
lastMessage && lastMessage.partial && lastMessage.type === "ask" && lastMessage.ask === type
|
lastMessageOfType &&
|
||||||
|
lastMessageOfType.partial &&
|
||||||
|
lastMessageOfType.type === "ask" &&
|
||||||
|
lastMessageOfType.ask === type
|
||||||
if (partial) {
|
if (partial) {
|
||||||
if (isUpdatingPreviousPartial) {
|
if (isUpdatingPreviousPartial) {
|
||||||
// existing partial message, so update it
|
// existing partial message, so update it
|
||||||
lastMessage.text = text
|
lastMessageOfType.text = text
|
||||||
lastMessage.partial = partial
|
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
|
// 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.saveClaudeMessages()
|
||||||
// await this.providerRef.deref()?.postStateToWebview()
|
// await this.providerRef.deref()?.postStateToWebview()
|
||||||
await this.providerRef
|
await this.providerRef
|
||||||
.deref()
|
.deref()
|
||||||
?.postMessageToWebview({ type: "partialMessage", partialMessage: lastMessage })
|
?.postMessageToWebview({ type: "partialMessage", partialMessage: lastMessageOfType })
|
||||||
throw new Error("Current ask promise was ignored")
|
throw new Error("Current ask promise was ignored 1")
|
||||||
} else {
|
} else {
|
||||||
// this is a new partial message, so add it with partial state
|
// this is a new partial message, so add it with partial state
|
||||||
// this.askResponse = undefined
|
// this.askResponse = undefined
|
||||||
@@ -251,7 +254,7 @@ export class ClaudeDev {
|
|||||||
// this.lastMessageTs = askTs
|
// this.lastMessageTs = askTs
|
||||||
await this.addToClaudeMessages({ ts: Date.now(), type: "ask", ask: type, text, partial })
|
await this.addToClaudeMessages({ ts: Date.now(), type: "ask", ask: type, text, partial })
|
||||||
await this.providerRef.deref()?.postStateToWebview()
|
await this.providerRef.deref()?.postStateToWebview()
|
||||||
throw new Error("Current ask promise was ignored")
|
throw new Error("Current ask promise was ignored 2")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// partial=false means its a complete version of a previously partial message
|
// partial=false means its a complete version of a previously partial message
|
||||||
@@ -262,9 +265,9 @@ export class ClaudeDev {
|
|||||||
this.askResponseImages = undefined
|
this.askResponseImages = undefined
|
||||||
askTs = Date.now()
|
askTs = Date.now()
|
||||||
this.lastMessageTs = askTs
|
this.lastMessageTs = askTs
|
||||||
lastMessage.ts = askTs
|
lastMessageOfType.ts = askTs
|
||||||
lastMessage.text = text
|
lastMessageOfType.text = text
|
||||||
lastMessage.partial = false
|
lastMessageOfType.partial = false
|
||||||
await this.saveClaudeMessages()
|
await this.saveClaudeMessages()
|
||||||
await this.providerRef.deref()?.postStateToWebview()
|
await this.providerRef.deref()?.postStateToWebview()
|
||||||
} else {
|
} else {
|
||||||
@@ -319,20 +322,23 @@ export class ClaudeDev {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (partial !== undefined) {
|
if (partial !== undefined) {
|
||||||
const lastMessage = this.claudeMessages.at(-1)
|
const lastMessageOfType = findLast(this.claudeMessages, (m) => m.say === type)
|
||||||
const isUpdatingPreviousPartial =
|
const isUpdatingPreviousPartial =
|
||||||
lastMessage && lastMessage.partial && lastMessage.type === "say" && lastMessage.say === type
|
lastMessageOfType &&
|
||||||
|
lastMessageOfType.partial &&
|
||||||
|
lastMessageOfType.type === "say" &&
|
||||||
|
lastMessageOfType.say === type
|
||||||
if (partial) {
|
if (partial) {
|
||||||
if (isUpdatingPreviousPartial) {
|
if (isUpdatingPreviousPartial) {
|
||||||
// existing partial message, so update it
|
// existing partial message, so update it
|
||||||
lastMessage.text = text
|
lastMessageOfType.text = text
|
||||||
lastMessage.images = images
|
lastMessageOfType.images = images
|
||||||
lastMessage.partial = partial
|
lastMessageOfType.partial = partial
|
||||||
// await this.saveClaudeMessages()
|
// await this.saveClaudeMessages()
|
||||||
// await this.providerRef.deref()?.postStateToWebview()
|
// await this.providerRef.deref()?.postStateToWebview()
|
||||||
await this.providerRef
|
await this.providerRef
|
||||||
.deref()
|
.deref()
|
||||||
?.postMessageToWebview({ type: "partialMessage", partialMessage: lastMessage })
|
?.postMessageToWebview({ type: "partialMessage", partialMessage: lastMessageOfType })
|
||||||
} else {
|
} else {
|
||||||
// this is a new partial message, so add it with partial state
|
// 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
|
// this is the complete version of a previously partial message, so replace the partial with the complete version
|
||||||
const sayTs = Date.now()
|
const sayTs = Date.now()
|
||||||
this.lastMessageTs = sayTs
|
this.lastMessageTs = sayTs
|
||||||
lastMessage.ts = sayTs
|
lastMessageOfType.ts = sayTs
|
||||||
lastMessage.text = text
|
lastMessageOfType.text = text
|
||||||
lastMessage.images = images
|
lastMessageOfType.images = images
|
||||||
lastMessage.partial = false
|
lastMessageOfType.partial = false
|
||||||
|
|
||||||
// instead of streaming partialMessage events, we do a save and post like normal to persist to disk
|
// instead of streaming partialMessage events, we do a save and post like normal to persist to disk
|
||||||
await this.saveClaudeMessages()
|
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
|
// 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")
|
const lastApiReqStarted = findLast(this.claudeMessages, (m) => m.say === "api_req_started")
|
||||||
if (lastApiReqFinished && lastApiReqFinished.text) {
|
if (lastApiReqStarted && lastApiReqStarted.text) {
|
||||||
const {
|
const {
|
||||||
tokensIn,
|
tokensIn,
|
||||||
tokensOut,
|
tokensOut,
|
||||||
cacheWrites,
|
cacheWrites,
|
||||||
cacheReads,
|
cacheReads,
|
||||||
}: { tokensIn?: number; tokensOut?: number; cacheWrites?: number; cacheReads?: number } = JSON.parse(
|
}: { 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 totalTokens = (tokensIn || 0) + (tokensOut || 0) + (cacheWrites || 0) + (cacheReads || 0)
|
||||||
const contextWindow = this.api.getModel().info.contextWindow
|
const contextWindow = this.api.getModel().info.contextWindow
|
||||||
const maxAllowedSize = Math.max(contextWindow - 40_000, contextWindow * 0.8)
|
const maxAllowedSize = Math.max(contextWindow - 40_000, contextWindow * 0.8)
|
||||||
@@ -2494,9 +2506,6 @@ ${this.customInstructions.trim()}
|
|||||||
textContent.content = textContentLines.join("\n")
|
textContent.content = textContentLines.join("\n")
|
||||||
|
|
||||||
this.assistantMessageContent = [textContent, ...toolCalls]
|
this.assistantMessageContent = [textContent, ...toolCalls]
|
||||||
|
|
||||||
// Present the updated content
|
|
||||||
this.presentAssistantMessage()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async recursivelyMakeClaudeRequests(
|
async recursivelyMakeClaudeRequests(
|
||||||
@@ -2692,23 +2701,31 @@ ${this.customInstructions.trim()}
|
|||||||
|
|
||||||
let totalCost: string | undefined
|
let totalCost: string | undefined
|
||||||
|
|
||||||
await this.say(
|
// 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)
|
||||||
"api_req_finished",
|
// 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
|
||||||
JSON.stringify({
|
// (it's worth removing a few months from now)
|
||||||
|
this.claudeMessages[lastApiReqIndex].text = JSON.stringify({
|
||||||
|
...JSON.parse(this.claudeMessages[lastApiReqIndex].text),
|
||||||
tokensIn: inputTokens,
|
tokensIn: inputTokens,
|
||||||
tokensOut: outputTokens,
|
tokensOut: outputTokens,
|
||||||
cacheWrites: cacheCreationInputTokens,
|
cacheWrites: cacheCreationInputTokens,
|
||||||
cacheReads: cacheReadInputTokens,
|
cacheReads: cacheReadInputTokens,
|
||||||
cost:
|
cost:
|
||||||
totalCost ||
|
totalCost ||
|
||||||
this.calculateApiCost(
|
this.calculateApiCost(inputTokens, outputTokens, cacheCreationInputTokens, cacheReadInputTokens),
|
||||||
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
|
// 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
|
// 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)
|
await pWaitFor(() => this.userMessageContentReady)
|
||||||
|
|
||||||
|
console.log("attempted to send new request")
|
||||||
|
|
||||||
|
// throw new Error("ClaudeDev fail")
|
||||||
|
|
||||||
const recDidEndLoop = await this.recursivelyMakeClaudeRequests(this.userMessageContent)
|
const recDidEndLoop = await this.recursivelyMakeClaudeRequests(this.userMessageContent)
|
||||||
didEndLoop = recDidEndLoop
|
didEndLoop = recDidEndLoop
|
||||||
} else {
|
} 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:
|
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.
|
1. You might begin your response explaining your thoughts, analysis, plan of action, etc.
|
||||||
2. If you need to use any tools, place ALL tool calls at the END of your message, after your normal text explanation.
|
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.
|
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:
|
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 1]
|
||||||
[Tool Call 2 if needed]
|
[Tool Call 2 if needed]
|
||||||
@@ -96,10 +95,25 @@ Here's the general structure your responses should follow:
|
|||||||
Remember:
|
Remember:
|
||||||
- Choose the most appropriate tool(s) based on the task and the tool descriptions provided.
|
- 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.
|
- 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.
|
- 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
|
||||||
<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.
|
- 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
|
# Tool Calls Examples
|
||||||
|
|
||||||
Here are some examples of how to structure your responses with tool calls:
|
|
||||||
|
|
||||||
## Example 1: Using a single tool
|
## 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>
|
<execute_command>
|
||||||
<command>
|
<command>
|
||||||
npm test
|
npm test
|
||||||
@@ -233,8 +243,6 @@ npm test
|
|||||||
|
|
||||||
## Example 2: Using multiple tools
|
## 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>
|
<write_to_file>
|
||||||
<path>
|
<path>
|
||||||
./frontend-config.json
|
./frontend-config.json
|
||||||
@@ -281,8 +289,6 @@ externalServices:
|
|||||||
|
|
||||||
## Example 3: Asking a follow-up question
|
## 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>
|
<ask_followup_question>
|
||||||
<question>
|
<question>
|
||||||
Which specific feature would you like me to implement in the example.py file?
|
Which specific feature would you like me to implement in the example.py file?
|
||||||
|
|||||||
@@ -426,14 +426,19 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
|
|||||||
[isAtBottom, visibleMessages, expandedRows]
|
[isAtBottom, visibleMessages, expandedRows]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const [lastScrollMessageCount, setLastScrollMessageCount] = useState<number>(0)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// dont scroll if we're just updating the api req started informational body
|
|
||||||
const lastMessage = visibleMessages.at(-1)
|
const lastMessage = visibleMessages.at(-1)
|
||||||
|
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"
|
const isLastApiReqStarted = lastMessage?.say === "api_req_started"
|
||||||
if (didScrollFromApiReqTs && isLastApiReqStarted && lastMessage?.ts === didScrollFromApiReqTs) {
|
if (didScrollFromApiReqTs && isLastApiReqStarted && lastMessage?.ts === didScrollFromApiReqTs) {
|
||||||
return
|
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.
|
// 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(() => {
|
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
|
// 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
|
||||||
@@ -443,6 +448,7 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
|
|||||||
}, 50)
|
}, 50)
|
||||||
|
|
||||||
return () => clearTimeout(timer)
|
return () => clearTimeout(timer)
|
||||||
|
}
|
||||||
}, [visibleMessages, didScrollFromApiReqTs])
|
}, [visibleMessages, didScrollFromApiReqTs])
|
||||||
|
|
||||||
const placeholderText = useMemo(() => {
|
const placeholderText = useMemo(() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user