diff options
author | A.J. Shulman <Shulman.aj@gmail.com> | 2025-05-21 13:11:52 -0400 |
---|---|---|
committer | A.J. Shulman <Shulman.aj@gmail.com> | 2025-05-21 13:11:52 -0400 |
commit | c3dba47bcda10bbcd72010c177afa8fd301e87e1 (patch) | |
tree | e8fe23915a09d4a9a95afbf971bce8e852fc5619 | |
parent | 0e98320d3b237f1927b9f1367494dccd7f66eda9 (diff) |
feat: add codebase exploration tools for agent assistance
Add three new agent tools to improve navigation and understanding of the codebase:
FileContentTool: retrieves complete content of specified files (max 3)
FileNamesTool: lists all available files in the codebase
CodebaseSummarySearchTool: performs semantic search across file summaries
6 files changed, 212 insertions, 0 deletions
diff --git a/src/client/views/nodes/chatbot/agentsystem/Agent.ts b/src/client/views/nodes/chatbot/agentsystem/Agent.ts index 86d40864e..1a9df1a75 100644 --- a/src/client/views/nodes/chatbot/agentsystem/Agent.ts +++ b/src/client/views/nodes/chatbot/agentsystem/Agent.ts @@ -26,6 +26,9 @@ import { Upload } from '../../../../../server/SharedMediaTypes'; import { RAGTool } from '../tools/RAGTool'; import { AgentDocumentManager } from '../utils/AgentDocumentManager'; import { CreateLinksTool } from '../tools/CreateLinksTool'; +import { CodebaseSummarySearchTool } from '../tools/CodebaseSummarySearchTool'; +import { FileContentTool } from '../tools/FileContentTool'; +import { FileNamesTool } from '../tools/FileNamesTool'; //import { CreateTextDocTool } from '../tools/CreateTextDocumentTool'; dotenv.config(); @@ -87,6 +90,9 @@ export class Agent { //imageCreationTool: new ImageCreationTool(createImage), documentMetadata: new DocumentMetadataTool(this._docManager), createLinks: new CreateLinksTool(this._docManager), + codebaseSummarySearch: new CodebaseSummarySearchTool(this.vectorstore), + fileContent: new FileContentTool(this.vectorstore), + fileNames: new FileNamesTool(this.vectorstore), }; } diff --git a/src/client/views/nodes/chatbot/tools/CodebaseSummarySearchTool.ts b/src/client/views/nodes/chatbot/tools/CodebaseSummarySearchTool.ts new file mode 100644 index 000000000..5fdc52375 --- /dev/null +++ b/src/client/views/nodes/chatbot/tools/CodebaseSummarySearchTool.ts @@ -0,0 +1,75 @@ +import { Observation } from '../types/types'; +import { ParametersType, ToolInfo } from '../types/tool_types'; +import { Vectorstore } from '../vectorstore/Vectorstore'; +import { BaseTool } from './BaseTool'; + +const codebaseSummarySearchToolParams = [ + { + name: 'query', + type: 'string[]', + description: 'HIGHLY detailed (MANY SENTENCES) descriptions of the code files you want to find in the codebase.', + required: true, + }, + { + name: 'top_k', + type: 'number', + description: 'Number of top matching files to return. Default is 5.', + required: false, + }, +] as const; + +type CodebaseSummarySearchToolParamsType = typeof codebaseSummarySearchToolParams; + +const codebaseSummarySearchToolInfo: ToolInfo<CodebaseSummarySearchToolParamsType> = { + name: 'codebaseSummarySearch', + description: 'Searches the Dash codebase for files that match a semantic query. Returns a list of the most relevant files with their summaries to help understand the codebase structure.', + citationRules: `When using the CodebaseSummarySearchTool: +1. Present results clearly, showing filepaths and their summaries +2. Use the file summaries to provide context about the codebase organization +3. The results can be used to identify relevant files for deeper inspection`, + parameterRules: codebaseSummarySearchToolParams, +}; + +export class CodebaseSummarySearchTool extends BaseTool<CodebaseSummarySearchToolParamsType> { + constructor(private vectorstore: Vectorstore) { + super(codebaseSummarySearchToolInfo); + } + + async execute(args: ParametersType<CodebaseSummarySearchToolParamsType>): Promise<Observation[]> { + console.log(`Executing codebase summary search with query: "${args.query}"`); + + // Use the vectorstore's searchFileSummaries method + const topK = args.top_k || 5; + const results: { filepath: string; summary: string; score?: number | undefined }[] = []; + for (const query of args.query) { + const result = await this.vectorstore.searchFileSummaries(query, topK); + results.push(...result); + } + + if (results.length === 0) { + return [ + { + type: 'text', + text: `No files matching the query "${args.query}" were found in the codebase.`, + }, + ]; + } + + // Format results as observations + const formattedResults: Observation[] = [ + { + type: 'text', + text: `Found ${results.length} file(s) matching the query "${args.query}":\n\n`, + }, + ]; + + results.forEach((result, index) => { + formattedResults.push({ + type: 'text', + text: `File #${index + 1}: ${result.filepath}\n` + `Relevance Score: ${result.score?.toFixed(4) || 'N/A'}\n` + `Summary: ${result.summary}\n\n`, + }); + }); + + return formattedResults; + } +} diff --git a/src/client/views/nodes/chatbot/tools/FileContentTool.ts b/src/client/views/nodes/chatbot/tools/FileContentTool.ts new file mode 100644 index 000000000..f994aab67 --- /dev/null +++ b/src/client/views/nodes/chatbot/tools/FileContentTool.ts @@ -0,0 +1,78 @@ +import { Observation } from '../types/types'; +import { ParametersType, ToolInfo } from '../types/tool_types'; +import { Vectorstore } from '../vectorstore/Vectorstore'; +import { BaseTool } from './BaseTool'; + +const fileContentToolParams = [ + { + name: 'filepaths', + type: 'string[]', + description: 'Array of file paths to retrieve content for. Limited to a maximum of 3 files.', + required: true, + }, +] as const; + +type FileContentToolParamsType = typeof fileContentToolParams; + +const fileContentToolInfo: ToolInfo<FileContentToolParamsType> = { + name: 'fileContent', + description: 'Retrieves the complete content of up to 3 specified files from the Dash codebase to help understand implementation details.', + citationRules: `When using the FileContentTool: +1. Present file content clearly with proper code formatting +2. Include the file path at the beginning of each file's content +3. Use this tool after identifying relevant files with the CodebaseSummarySearchTool +4. Maximum of 3 files can be retrieved at once`, + parameterRules: fileContentToolParams, +}; + +export class FileContentTool extends BaseTool<FileContentToolParamsType> { + constructor(private vectorstore: Vectorstore) { + super(fileContentToolInfo); + } + + async execute(args: ParametersType<FileContentToolParamsType>): Promise<Observation[]> { + console.log(`Executing file content retrieval for: ${args.filepaths.join(', ')}`); + + // Enforce the limit of 3 files + const filepaths = args.filepaths.slice(0, 3); + if (args.filepaths.length > 3) { + console.warn(`FileContentTool: Request for ${args.filepaths.length} files was limited to 3`); + } + + const observations: Observation[] = []; + + if (filepaths.length === 0) { + return [ + { + type: 'text', + text: 'No filepaths provided. Please specify at least one file path to retrieve content.', + }, + ]; + } + + // Add initial message + observations.push({ + type: 'text', + text: `Retrieving content for ${filepaths.length} file(s):\n\n`, + }); + + // Fetch content for each file + for (const filepath of filepaths) { + const content = await this.vectorstore.getFileContent(filepath); + + if (content) { + observations.push({ + type: 'text', + text: `File: ${filepath}\n\n\`\`\`\n${content}\n\`\`\`\n\n`, + }); + } else { + observations.push({ + type: 'text', + text: `Error: Could not retrieve content for file "${filepath}"\n\n`, + }); + } + } + + return observations; + } +} diff --git a/src/client/views/nodes/chatbot/tools/FileNamesTool.ts b/src/client/views/nodes/chatbot/tools/FileNamesTool.ts new file mode 100644 index 000000000..b69874afa --- /dev/null +++ b/src/client/views/nodes/chatbot/tools/FileNamesTool.ts @@ -0,0 +1,34 @@ +import { Observation } from '../types/types'; +import { ParametersType, ToolInfo } from '../types/tool_types'; +import { Vectorstore } from '../vectorstore/Vectorstore'; +import { BaseTool } from './BaseTool'; + +const fileNamesToolParams = [] as const; + +type FileNamesToolParamsType = typeof fileNamesToolParams; + +const fileNamesToolInfo: ToolInfo<FileNamesToolParamsType> = { + name: 'fileNames', + description: 'Retrieves the names of all files in the Dash codebase to help understand the codebase structure.', + citationRules: `No citation needed.`, + parameterRules: fileNamesToolParams, +}; + +export class FileNamesTool extends BaseTool<FileNamesToolParamsType> { + constructor(private vectorstore: Vectorstore) { + super(fileNamesToolInfo); + } + + async execute(args: ParametersType<FileNamesToolParamsType>): Promise<Observation[]> { + console.log(`Executing file names retrieval`); + + const filepaths = await this.vectorstore.getFileNames(); + + return [ + { + type: 'text', + text: `The file names in the codebase are: ${filepaths}`, + }, + ]; + } +} diff --git a/src/client/views/nodes/chatbot/vectorstore/Vectorstore.ts b/src/client/views/nodes/chatbot/vectorstore/Vectorstore.ts index 5c2d0e5ea..72060973b 100644 --- a/src/client/views/nodes/chatbot/vectorstore/Vectorstore.ts +++ b/src/client/views/nodes/chatbot/vectorstore/Vectorstore.ts @@ -85,6 +85,12 @@ export class Vectorstore { } } + async getFileNames() { + const response = await Networking.FetchFromServer('/getFileNames'); + const filepaths = JSON.parse(response); + return filepaths; + } + /** * Initializes the Pinecone index by checking if it exists and creating it if necessary. * Sets the index to use cosine similarity for vector similarity calculations. diff --git a/src/server/ApiManagers/AssistantManager.ts b/src/server/ApiManagers/AssistantManager.ts index 9d0427b52..c7c347c71 100644 --- a/src/server/ApiManagers/AssistantManager.ts +++ b/src/server/ApiManagers/AssistantManager.ts @@ -118,6 +118,19 @@ export default class AssistantManager extends ApiManager { }, }); + // Register an endpoint to retrieve file names from the file_summaries.json file + register({ + method: Method.GET, + subscription: '/getFileNames', + secureHandler: async ({ res }) => { + const filePath = path.join(filesDirectory, Directory.vectorstore, 'file_summaries.json'); + const data = fs.readFileSync(filePath, 'utf8'); + console.log(Object.keys(JSON.parse(data))); + + res.send(Object.keys(JSON.parse(data))); + }, + }); + // Register an endpoint to retrieve file content from the content json file register({ method: Method.POST, |