Merge pull request #305 from RooVetGit/only_load_context_in_user_input

Only load context and parse @-mentions in user input
This commit is contained in:
Matt Rubens
2025-01-09 10:47:56 -05:00
committed by GitHub
3 changed files with 99 additions and 9 deletions

View File

@@ -0,0 +1,5 @@
---
"roo-cline": patch
---
Only parse @-mentions in user input (not in files)

View File

@@ -2360,22 +2360,30 @@ export class Cline {
// 2. ToolResultBlockParam's content/context text arrays if it contains "<feedback>" (see formatToolDeniedFeedback, attemptCompletion, executeCommand, and consecutiveMistakeCount >= 3) or "<answer>" (see askFollowupQuestion), we place all user generated content in these tags so they can effectively be used as markers for when we should parse mentions) // 2. ToolResultBlockParam's content/context text arrays if it contains "<feedback>" (see formatToolDeniedFeedback, attemptCompletion, executeCommand, and consecutiveMistakeCount >= 3) or "<answer>" (see askFollowupQuestion), we place all user generated content in these tags so they can effectively be used as markers for when we should parse mentions)
Promise.all( Promise.all(
userContent.map(async (block) => { userContent.map(async (block) => {
const shouldProcessMentions = (text: string) =>
text.includes("<task>") || text.includes("<feedback>");
if (block.type === "text") { if (block.type === "text") {
return { if (shouldProcessMentions(block.text)) {
...block,
text: await parseMentions(block.text, cwd, this.urlContentFetcher),
}
} else if (block.type === "tool_result") {
const isUserMessage = (text: string) => text.includes("<feedback>") || text.includes("<answer>")
if (typeof block.content === "string" && isUserMessage(block.content)) {
return { return {
...block, ...block,
content: await parseMentions(block.content, cwd, this.urlContentFetcher), text: await parseMentions(block.text, cwd, this.urlContentFetcher),
} }
}
return block;
} else if (block.type === "tool_result") {
if (typeof block.content === "string") {
if (shouldProcessMentions(block.content)) {
return {
...block,
content: await parseMentions(block.content, cwd, this.urlContentFetcher),
}
}
return block;
} else if (Array.isArray(block.content)) { } else if (Array.isArray(block.content)) {
const parsedContent = await Promise.all( const parsedContent = await Promise.all(
block.content.map(async (contentBlock) => { block.content.map(async (contentBlock) => {
if (contentBlock.type === "text" && isUserMessage(contentBlock.text)) { if (contentBlock.type === "text" && shouldProcessMentions(contentBlock.text)) {
return { return {
...contentBlock, ...contentBlock,
text: await parseMentions(contentBlock.text, cwd, this.urlContentFetcher), text: await parseMentions(contentBlock.text, cwd, this.urlContentFetcher),
@@ -2389,6 +2397,7 @@ export class Cline {
content: parsedContent, content: parsedContent,
} }
} }
return block;
} }
return block return block
}), }),

View File

@@ -626,6 +626,82 @@ describe('Cline', () => {
text: '[Referenced image in conversation]' text: '[Referenced image in conversation]'
}); });
}); });
describe('loadContext', () => {
it('should process mentions in task and feedback tags', async () => {
const cline = new Cline(
mockProvider,
mockApiConfig,
undefined,
false,
undefined,
'test task'
);
// Mock parseMentions to track calls
const mockParseMentions = jest.fn().mockImplementation(text => `processed: ${text}`);
jest.spyOn(require('../../core/mentions'), 'parseMentions').mockImplementation(mockParseMentions);
const userContent = [
{
type: 'text',
text: 'Regular text with @/some/path'
} as const,
{
type: 'text',
text: '<task>Text with @/some/path in task tags</task>'
} as const,
{
type: 'tool_result',
tool_use_id: 'test-id',
content: [{
type: 'text',
text: '<feedback>Check @/some/path</feedback>'
}]
} as Anthropic.ToolResultBlockParam,
{
type: 'tool_result',
tool_use_id: 'test-id-2',
content: [{
type: 'text',
text: 'Regular tool result with @/path'
}]
} as Anthropic.ToolResultBlockParam
];
// Process the content
const [processedContent] = await cline['loadContext'](userContent);
// Regular text should not be processed
expect((processedContent[0] as Anthropic.TextBlockParam).text)
.toBe('Regular text with @/some/path');
// Text within task tags should be processed
expect((processedContent[1] as Anthropic.TextBlockParam).text)
.toContain('processed:');
expect(mockParseMentions).toHaveBeenCalledWith(
'<task>Text with @/some/path in task tags</task>',
expect.any(String),
expect.any(Object)
);
// Feedback tag content should be processed
const toolResult1 = processedContent[2] as Anthropic.ToolResultBlockParam;
const content1 = Array.isArray(toolResult1.content) ? toolResult1.content[0] : toolResult1.content;
expect((content1 as Anthropic.TextBlockParam).text).toContain('processed:');
expect(mockParseMentions).toHaveBeenCalledWith(
'<feedback>Check @/some/path</feedback>',
expect.any(String),
expect.any(Object)
);
// Regular tool result should not be processed
const toolResult2 = processedContent[3] as Anthropic.ToolResultBlockParam;
const content2 = Array.isArray(toolResult2.content) ? toolResult2.content[0] : toolResult2.content;
expect((content2 as Anthropic.TextBlockParam).text)
.toBe('Regular tool result with @/path');
});
});
}); });
}); });
}); });