From f6ac4ad4dfb1a9786aecca053ba36c27165ce911 Mon Sep 17 00:00:00 2001 From: Mohamed Sajith <82174741+mmssajith@users.noreply.github.com> Date: Thu, 26 Dec 2024 20:30:56 +0530 Subject: [PATCH 1/3] AGT-60 Init --- src/__tests__/ConfigService.test.ts | 52 +++++++++++++++++++ src/services/ConfigService.ts | 6 ++- src/services/LLM/LLMContextCreator.ts | 13 ++++- .../LLM/__tests__/PhaseManager.test.ts | 3 ++ .../LLM/tests/LLMContextCreator.test.ts | 46 ++++++++++++++++ 5 files changed, 117 insertions(+), 3 deletions(-) diff --git a/src/__tests__/ConfigService.test.ts b/src/__tests__/ConfigService.test.ts index 54092880b..11dca0800 100644 --- a/src/__tests__/ConfigService.test.ts +++ b/src/__tests__/ConfigService.test.ts @@ -72,6 +72,7 @@ describe("ConfigService", () => { strategyModel: "qwen/qwq-32b-preview", executeModel: "anthropic/claude-3.5-sonnet:beta", includeAllFilesOnEnvToContext: false, + truncateFilesOnEnvAfterLinesLimit: 1000, autoScaleAvailableModels: [ { id: "qwen/qwen-2.5-coder-32b-instruct", @@ -218,6 +219,7 @@ describe("ConfigService", () => { strategyModel: "model2", executeModel: "model3", includeAllFilesOnEnvToContext: false, + truncateFilesOnEnvAfterLinesLimit: 1000, autoScaleAvailableModels: [ { id: "model1", @@ -250,4 +252,54 @@ describe("ConfigService", () => { expect(config.packageManager).toBe("bundler"); }); }); + + describe("environment context configuration", () => { + it("should use default values for environment context settings", () => { + const minimalConfig = { + provider: "open-router", + interactive: true, + stream: true, + debug: false, + options: "temperature=0", + openRouterApiKey: "test-key", + autoScaleAvailableModels: [], + }; + + (fs.existsSync as jest.Mock).mockReturnValue(true); + (fs.readFileSync as jest.Mock).mockReturnValue( + JSON.stringify(minimalConfig), + ); + + const config = configService.getConfig(); + expect(config.includeAllFilesOnEnvToContext).toBe(true); + expect(config.truncateFilesOnEnvAfterLinesLimit).toBe(1000); + }); + + it("should allow custom values for environment context settings", () => { + const validMockConfig = { + provider: "open-router", + interactive: true, + stream: true, + debug: false, + options: "temperature=0", + openRouterApiKey: "test-key", + autoScaleAvailableModels: [], + }; + + const customConfig = { + ...validMockConfig, + includeAllFilesOnEnvToContext: false, + truncateFilesOnEnvAfterLinesLimit: 500, + }; + + (fs.existsSync as jest.Mock).mockReturnValue(true); + (fs.readFileSync as jest.Mock).mockReturnValue( + JSON.stringify(customConfig), + ); + + const config = configService.getConfig(); + expect(config.includeAllFilesOnEnvToContext).toBe(false); + expect(config.truncateFilesOnEnvAfterLinesLimit).toBe(500); + }); + }); }); diff --git a/src/services/ConfigService.ts b/src/services/ConfigService.ts index cf5a9dc11..1988fd0c2 100644 --- a/src/services/ConfigService.ts +++ b/src/services/ConfigService.ts @@ -17,7 +17,8 @@ const configSchema = z.object({ appName: z.string().optional().default("MyApp"), autoScaler: z.boolean().optional(), autoScaleMaxTryPerModel: z.number().optional(), - includeAllFilesOnEnvToContext: z.boolean().optional(), + includeAllFilesOnEnvToContext: z.boolean().optional().default(true), + truncateFilesOnEnvAfterLinesLimit: z.number().optional().default(1000), // Phase-specific model configurations discoveryModel: z.string().optional().default("google/gemini-flash-1.5-8b"), strategyModel: z.string().optional().default("openai/o1-mini"), @@ -145,7 +146,8 @@ export class ConfigService { discoveryModel: "google/gemini-flash-1.5-8b", strategyModel: "qwen/qwq-32b-preview", executeModel: "anthropic/claude-3.5-sonnet:beta", - includeAllFilesOnEnvToContext: false, + includeAllFilesOnEnvToContext: true, + truncateFilesOnEnvAfterLinesLimit: 1000, autoScaleAvailableModels: [ { id: "qwen/qwen-2.5-coder-32b-instruct", diff --git a/src/services/LLM/LLMContextCreator.ts b/src/services/LLM/LLMContextCreator.ts index 118df628f..40fee6b63 100644 --- a/src/services/LLM/LLMContextCreator.ts +++ b/src/services/LLM/LLMContextCreator.ts @@ -102,13 +102,24 @@ export class LLMContextCreator { return baseContext.message; } + private truncateFileContent(content: string, lineLimit: number): string { + const lines = content.split("\n"); + if (lines.length <= lineLimit) return content; + return lines.slice(0, lineLimit).join("\n") + "\n[Content truncated...]"; + } + private async getEnvironmentDetails(root: string): Promise { const scanResult = await this.directoryScanner.scan(root); if (!scanResult.success) { throw new Error(`Failed to scan directory: ${scanResult.error}`); } - return `# Current Working Directory (${root}) Files\n${scanResult.data}`; + const config = this.configService.getConfig(); + const limit = config.truncateFilesOnEnvAfterLinesLimit; + + const content = String(scanResult.data || ""); + const truncatedContent = this.truncateFileContent(content, limit); + return `# Current Working Directory (${root}) Files\n${truncatedContent}`; } private async getProjectInfo(root: string): Promise { diff --git a/src/services/LLM/__tests__/PhaseManager.test.ts b/src/services/LLM/__tests__/PhaseManager.test.ts index 2a6b65ef0..6a4289f52 100644 --- a/src/services/LLM/__tests__/PhaseManager.test.ts +++ b/src/services/LLM/__tests__/PhaseManager.test.ts @@ -28,6 +28,7 @@ describe("PhaseManager", () => { autoScaler: false, autoScaleMaxTryPerModel: 2, includeAllFilesOnEnvToContext: false, + truncateFilesOnEnvAfterLinesLimit: 1000, autoScaleAvailableModels: [ { id: "model1", @@ -123,6 +124,7 @@ describe("PhaseManager", () => { autoScaler: false, autoScaleMaxTryPerModel: 2, includeAllFilesOnEnvToContext: false, + truncateFilesOnEnvAfterLinesLimit: 1000, autoScaleAvailableModels: [ { id: "model1", @@ -180,6 +182,7 @@ describe("PhaseManager", () => { autoScaler: false, autoScaleMaxTryPerModel: 2, includeAllFilesOnEnvToContext: false, + truncateFilesOnEnvAfterLinesLimit: 1000, autoScaleAvailableModels: [ { id: "model1", diff --git a/src/services/LLM/tests/LLMContextCreator.test.ts b/src/services/LLM/tests/LLMContextCreator.test.ts index cfbd96326..f1fdf1a9a 100644 --- a/src/services/LLM/tests/LLMContextCreator.test.ts +++ b/src/services/LLM/tests/LLMContextCreator.test.ts @@ -428,4 +428,50 @@ describe("LLMContextCreator", () => { expect(result).toContain("project info"); }); }); + + describe("file content truncation", () => { + it("should truncate file content when it exceeds the limit", async () => { + const longContent = Array(1500).fill("line").join("\n"); + mocker.mockPrototype(DirectoryScanner, "scan", { + success: true, + data: longContent, + }); + + mocker.mockPrototype(ConfigService, "getConfig", { + includeAllFilesOnEnvToContext: true, + truncateFilesOnEnvAfterLinesLimit: 1000, + customInstructions: "test", + }); + + const context = await contextCreator.create( + "test message", + "/root", + true, + ); + expect(context).toContain("[Content truncated...]"); + expect(context.split("\n").length).toBeLessThan(1500); + }); + + it("should not truncate file content when under the limit", async () => { + const shortContent = Array(500).fill("line").join("\n"); + mocker.mockPrototype(DirectoryScanner, "scan", { + success: true, + data: shortContent, + }); + + mocker.mockPrototype(ConfigService, "getConfig", { + includeAllFilesOnEnvToContext: true, + truncateFilesOnEnvAfterLinesLimit: 1000, + customInstructions: "test", + }); + + const context = await contextCreator.create( + "test message", + "/root", + true, + ); + expect(context).not.toContain("[Content truncated...]"); + expect(context.split("\n").length).toBeLessThan(1000); + }); + }); }); From c29c5b6fb25cf36d0a7685019f54d38af3300cc0 Mon Sep 17 00:00:00 2001 From: mmssajith Date: Wed, 1 Jan 2025 17:09:25 +0530 Subject: [PATCH 2/3] Update Configs --- src/__tests__/ConfigService.test.ts | 54 ++++++++++++++++--- .../helpers/ConfigServiceTestHelper.ts | 6 ++- src/services/ConfigService.ts | 10 +++- src/services/LLM/LLMContextCreator.ts | 2 +- .../LLM/__tests__/PhaseManager.test.ts | 15 ++++-- .../LLM/tests/LLMContextCreator.test.ts | 20 +++++-- 6 files changed, 88 insertions(+), 19 deletions(-) diff --git a/src/__tests__/ConfigService.test.ts b/src/__tests__/ConfigService.test.ts index 30bc55744..8cb1a0903 100644 --- a/src/__tests__/ConfigService.test.ts +++ b/src/__tests__/ConfigService.test.ts @@ -1,5 +1,5 @@ import chalk from "chalk"; -import fs from "fs"; +import fs, { truncate } from "fs"; import path from "path"; import { ConfigService } from "../services/ConfigService"; @@ -23,7 +23,32 @@ describe("ConfigService", () => { debug: false, options: "temperature=0", openRouterApiKey: "test-key", - autoScaleAvailableModels: [], // Required by schema + appUrl: "https://localhost:8080", + appName: "MyApp", + autoScaler: true, + autoScaleMaxTryPerModel: 2, + contextPaths: { + includeFilesAndDirectories: false, + includeDirectoriesOnly: true, + }, + truncateFilesOnEnvAfterLinesLimit: 1000, + discoveryModel: "google/gemini-flash-1.5-8b", + strategyModel: "openai/o1-mini", + executeModel: "anthropic/claude-3.5-sonnet:beta", + autoScaleAvailableModels: [], + directoryScanner: { + defaultIgnore: ["dist", "coverage", ".next", "build", ".cache", ".husky"], + maxDepth: 8, + allFiles: true, + directoryFirst: true, + excludeDirectories: false, + }, + gitDiff: { + excludeLockFiles: true, + lockFiles: ["package-lock.json"], + }, + referenceExamples: {}, + timeoutSeconds: 0, }; // Helper functions @@ -70,8 +95,11 @@ describe("ConfigService", () => { discoveryModel: "google/gemini-flash-1.5-8b", strategyModel: "qwen/qwq-32b-preview", executeModel: "anthropic/claude-3.5-sonnet:beta", - includeAllFilesOnEnvToContext: false, - truncateFilesOnEnvAfterLinesLimit: 1000, + contextPaths: { + includeFilesAndDirectories: false, + includeDirectoriesOnly: true, + }, + truncateFilesOnEnvAfterLinesLimit: 1000, autoScaleAvailableModels: [ { id: "qwen/qwen-2.5-coder-32b-instruct", @@ -373,6 +401,11 @@ describe("ConfigService", () => { options: "temperature=0", openRouterApiKey: "test-key", autoScaleAvailableModels: [], + contextPaths: { + includeFilesAndDirectories: false, + includeDirectoriesOnly: true, + }, + truncateFilesOnEnvAfterLinesLimit: 1000, }; (fs.existsSync as jest.Mock).mockReturnValue(true); @@ -381,7 +414,8 @@ describe("ConfigService", () => { ); const config = configService.getConfig(); - expect(config.includeAllFilesOnEnvToContext).toBe(true); + expect(config.contextPaths.includeFilesAndDirectories).toBe(false); + expect(config.contextPaths.includeDirectoriesOnly).toBe(true); expect(config.truncateFilesOnEnvAfterLinesLimit).toBe(1000); }); @@ -398,8 +432,11 @@ describe("ConfigService", () => { const customConfig = { ...validMockConfig, - includeAllFilesOnEnvToContext: false, - truncateFilesOnEnvAfterLinesLimit: 500, + contextPaths:{ + includeFilesAndDirectories: true, + includeDirectoriesOnly: false, + }, + truncateFilesOnEnvAfterLinesLimit: 500, }; (fs.existsSync as jest.Mock).mockReturnValue(true); @@ -408,7 +445,8 @@ describe("ConfigService", () => { ); const config = configService.getConfig(); - expect(config.includeAllFilesOnEnvToContext).toBe(false); + expect(config.contextPaths.includeDirectoriesOnly).toBe(false); + expect(config.contextPaths.includeFilesAndDirectories).toBe(true); expect(config.truncateFilesOnEnvAfterLinesLimit).toBe(500); }); }); diff --git a/src/__tests__/helpers/ConfigServiceTestHelper.ts b/src/__tests__/helpers/ConfigServiceTestHelper.ts index 22701c636..58483eda0 100644 --- a/src/__tests__/helpers/ConfigServiceTestHelper.ts +++ b/src/__tests__/helpers/ConfigServiceTestHelper.ts @@ -40,7 +40,11 @@ export class ConfigServiceTestHelper { discoveryModel: "google/gemini-flash-1.5-8b", strategyModel: "qwen/qwq-32b-preview", executeModel: "anthropic/claude-3.5-sonnet:beta", - includeAllFilesOnEnvToContext: false, + contextPaths: { + includeFilesAndDirectories: false, + includeDirectoriesOnly: true, + }, + truncateFilesOnEnvAfterLinesLimit: 1000, autoScaleAvailableModels: [ { id: "qwen/qwen-2.5-coder-32b-instruct", diff --git a/src/services/ConfigService.ts b/src/services/ConfigService.ts index a6d7cc81e..1f3748af2 100644 --- a/src/services/ConfigService.ts +++ b/src/services/ConfigService.ts @@ -17,7 +17,10 @@ const configSchema = z.object({ appName: z.string().optional().default("MyApp"), autoScaler: z.boolean().optional(), autoScaleMaxTryPerModel: z.number().optional(), - includeAllFilesOnEnvToContext: z.boolean().optional().default(true), + contextPaths: z.object({ + includeFilesAndDirectories: z.boolean().optional().default(false), + includeDirectoriesOnly: z.boolean().optional().default(true), + }), truncateFilesOnEnvAfterLinesLimit: z.number().optional().default(1000), // Phase-specific model configurations discoveryModel: z.string().optional().default("google/gemini-flash-1.5-8b"), @@ -190,7 +193,10 @@ export class ConfigService { discoveryModel: "google/gemini-flash-1.5-8b", strategyModel: "qwen/qwq-32b-preview", executeModel: "anthropic/claude-3.5-sonnet:beta", - includeAllFilesOnEnvToContext: true, + contextPaths:{ + includeFilesAndDirectories: false, + includeDirectoriesOnly: true, + }, truncateFilesOnEnvAfterLinesLimit: 1000, autoScaleAvailableModels: [ { diff --git a/src/services/LLM/LLMContextCreator.ts b/src/services/LLM/LLMContextCreator.ts index 40fee6b63..d940fecbb 100644 --- a/src/services/LLM/LLMContextCreator.ts +++ b/src/services/LLM/LLMContextCreator.ts @@ -198,7 +198,7 @@ ${additionalInstructions ? `${additionalInstructions}` : ""} const phaseConfig = this.phaseManager.getCurrentPhaseConfig(); const customInstructions = await this.loadCustomInstructions(); - const envDetails = config.includeAllFilesOnEnvToContext + const envDetails = config.contextPaths.includeFilesAndDirectories ? context.environmentDetails : ""; diff --git a/src/services/LLM/__tests__/PhaseManager.test.ts b/src/services/LLM/__tests__/PhaseManager.test.ts index 6d7d5f9f0..c8285f39e 100644 --- a/src/services/LLM/__tests__/PhaseManager.test.ts +++ b/src/services/LLM/__tests__/PhaseManager.test.ts @@ -27,7 +27,10 @@ describe("PhaseManager", () => { executeModel: "model3", autoScaler: false, autoScaleMaxTryPerModel: 2, - includeAllFilesOnEnvToContext: false, + contextPaths:{ + includeFilesAndDirectories: false, + includeDirectoriesOnly: true, + }, truncateFilesOnEnvAfterLinesLimit: 1000, autoScaleAvailableModels: [ { @@ -124,7 +127,10 @@ describe("PhaseManager", () => { appName: "TestApp", autoScaler: false, autoScaleMaxTryPerModel: 2, - includeAllFilesOnEnvToContext: false, + contextPaths:{ + includeFilesAndDirectories: false, + includeDirectoriesOnly: true, + }, truncateFilesOnEnvAfterLinesLimit: 1000, autoScaleAvailableModels: [ { @@ -183,7 +189,10 @@ describe("PhaseManager", () => { executeModel: "model3", autoScaler: false, autoScaleMaxTryPerModel: 2, - includeAllFilesOnEnvToContext: false, + contextPaths:{ + includeFilesAndDirectories: false, + includeDirectoriesOnly: true, + }, truncateFilesOnEnvAfterLinesLimit: 1000, autoScaleAvailableModels: [ { diff --git a/src/services/LLM/tests/LLMContextCreator.test.ts b/src/services/LLM/tests/LLMContextCreator.test.ts index f1fdf1a9a..84f859785 100644 --- a/src/services/LLM/tests/LLMContextCreator.test.ts +++ b/src/services/LLM/tests/LLMContextCreator.test.ts @@ -83,7 +83,10 @@ describe("LLMContextCreator", () => { }); mocker.mockPrototype(ConfigService, "getConfig", { - includeAllFilesOnEnvToContext: true, + contextPaths: { + includeFilesAndDirectories: false, + includeDirectoriesOnly: true, + }, customInstructions: "Default custom instructions", }); @@ -308,7 +311,10 @@ describe("LLMContextCreator", () => { it("should not include environment details when config flag is false", async () => { mocker.mockPrototype(ConfigService, "getConfig", { - includeAllFilesOnEnvToContext: false, + contextPaths: { + includeFilesAndDirectories: false, + includeDirectoriesOnly: true, + }, customInstructions: "Default custom instructions", }); @@ -438,7 +444,10 @@ describe("LLMContextCreator", () => { }); mocker.mockPrototype(ConfigService, "getConfig", { - includeAllFilesOnEnvToContext: true, + contextPaths: { + includeFilesAndDirectories: false, + includeDirectoriesOnly: true, + }, truncateFilesOnEnvAfterLinesLimit: 1000, customInstructions: "test", }); @@ -460,7 +469,10 @@ describe("LLMContextCreator", () => { }); mocker.mockPrototype(ConfigService, "getConfig", { - includeAllFilesOnEnvToContext: true, + contextPaths: { + includeFilesAndDirectories: false, + includeDirectoriesOnly: true, + }, truncateFilesOnEnvAfterLinesLimit: 1000, customInstructions: "test", }); From 65c891a0f098a8c7a8776e75cddab0471cd48741 Mon Sep 17 00:00:00 2001 From: Mohamed Sajith <82174741+mmssajith@users.noreply.github.com> Date: Thu, 2 Jan 2025 21:37:59 +0530 Subject: [PATCH 3/3] Update potential NULL reference error --- src/services/LLM/LLMContextCreator.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/services/LLM/LLMContextCreator.ts b/src/services/LLM/LLMContextCreator.ts index 40fee6b63..ba43b4667 100644 --- a/src/services/LLM/LLMContextCreator.ts +++ b/src/services/LLM/LLMContextCreator.ts @@ -102,7 +102,11 @@ export class LLMContextCreator { return baseContext.message; } - private truncateFileContent(content: string, lineLimit: number): string { + private truncateFileContent( + content: string | null | undefined, + lineLimit: number, + ): string { + if (!content) return ""; const lines = content.split("\n"); if (lines.length <= lineLimit) return content; return lines.slice(0, lineLimit).join("\n") + "\n[Content truncated...]";