diff --git a/codeflash/code_utils/code_replacer.py b/codeflash/code_utils/code_replacer.py index c997f8e53..6a57b61e1 100644 --- a/codeflash/code_utils/code_replacer.py +++ b/codeflash/code_utils/code_replacer.py @@ -659,6 +659,19 @@ def _add_global_declarations_for_language( # Get names of existing declarations existing_names = {decl.name for decl in original_declarations} + # Also exclude names that are already imported (to avoid duplicating imported types) + original_imports = analyzer.find_imports(original_source) + for imp in original_imports: + # Add default import name + if imp.default_import: + existing_names.add(imp.default_import) + # Add named imports (use alias if present, otherwise use original name) + for name, alias in imp.named_imports: + existing_names.add(alias if alias else name) + # Add namespace import + if imp.namespace_import: + existing_names.add(imp.namespace_import) + # Find new declarations (names that don't exist in original) new_declarations = [] seen_sources = set() # Track to avoid duplicates from destructuring diff --git a/codeflash/context/code_context_extractor.py b/codeflash/context/code_context_extractor.py index 28141dcb9..0479d4178 100644 --- a/codeflash/context/code_context_extractor.py +++ b/codeflash/context/code_context_extractor.py @@ -294,10 +294,9 @@ def get_code_optimization_context_for_language( helper_code = "\n\n".join(h.source_code for h in same_file_helpers) target_file_code = target_file_code + "\n\n" + helper_code - # Add global variables (module-level declarations) referenced by the function and helpers - # These should be included in read-writable context so AI can modify them if needed - if code_context.read_only_context: - target_file_code = code_context.read_only_context + "\n\n" + target_file_code + # Note: code_context.read_only_context contains type definitions and global variables + # These should be passed as read-only context to the AI, not prepended to the target code + # If prepended to target code, the AI treats them as code to optimize and includes them in output # Add imports to target file code if imports_code: @@ -350,8 +349,9 @@ def get_code_optimization_context_for_language( return CodeOptimizationContext( testgen_context=testgen_context, read_writable_code=read_writable_code, - # Global variables are now included in read-writable code, so don't duplicate in read-only - read_only_context_code="", + # Pass type definitions and globals as read-only context for the AI + # This way the AI sees them as context but doesn't include them in optimized output + read_only_context_code=code_context.read_only_context, hashing_code_context=read_writable_code.flat, hashing_code_context_hash=code_hash, helper_functions=helper_function_sources, diff --git a/packages/codeflash/runtime/loop-runner.js b/packages/codeflash/runtime/loop-runner.js index 72ce4fcd4..dbcb8642f 100644 --- a/packages/codeflash/runtime/loop-runner.js +++ b/packages/codeflash/runtime/loop-runner.js @@ -47,7 +47,11 @@ try { } // Configuration -const MAX_BATCHES = parseInt(process.env.CODEFLASH_PERF_LOOP_COUNT || '10000', 10); +const PERF_LOOP_COUNT = parseInt(process.env.CODEFLASH_PERF_LOOP_COUNT || '10000', 10); +const PERF_BATCH_SIZE = parseInt(process.env.CODEFLASH_PERF_BATCH_SIZE || '10', 10); +// MAX_BATCHES = how many batches needed to reach PERF_LOOP_COUNT iterations +// Add 1 to handle any rounding, but cap at PERF_LOOP_COUNT to avoid excessive batches +const MAX_BATCHES = Math.min(Math.ceil(PERF_LOOP_COUNT / PERF_BATCH_SIZE) + 1, PERF_LOOP_COUNT); const TARGET_DURATION_MS = parseInt(process.env.CODEFLASH_PERF_TARGET_DURATION_MS || '10000', 10); const MIN_BATCHES = parseInt(process.env.CODEFLASH_PERF_MIN_LOOPS || '5', 10); diff --git a/tests/test_languages/test_js_code_replacer.py b/tests/test_languages/test_js_code_replacer.py index d257fea30..3d703aa34 100644 --- a/tests/test_languages/test_js_code_replacer.py +++ b/tests/test_languages/test_js_code_replacer.py @@ -1893,3 +1893,174 @@ export class DataProcessor { } """ + + +class TestImportedTypeNotDuplicated: + """Tests to ensure imported types are not duplicated during code replacement. + + When a type is already imported in the original file, it should NOT be + added as a new declaration from the optimized code, even if the optimized + code contains the type definition (because it was provided as context). + + See: https://github.com/codeflash-ai/appsmith/pull/20 + """ + + def test_imported_interface_not_added_as_declaration(self, ts_support, temp_project): + """Test that an imported interface is not duplicated in the output. + + When TreeNode is imported from another file and the optimized code + contains the TreeNode interface definition (from read-only context), + the replacement should NOT add the interface to the original file. + """ + from codeflash.models.models import CodeStringsMarkdown, CodeString + + # Original source imports TreeNode + original_source = """\ +import type { TreeNode } from "./constants"; + +export function getNearestAbove( + tree: Record, + effectedBoxId: string, +) { + const aboves = tree[effectedBoxId].aboves; + return aboves.reduce((prev: string[], next: string) => { + if (!prev[0]) return [next]; + let nextBottomRow = tree[next].bottomRow; + let prevBottomRow = tree[prev[0]].bottomRow; + if (nextBottomRow > prevBottomRow) return [next]; + return prev; + }, []); +} +""" + file_path = temp_project / "helpers.ts" + file_path.write_text(original_source, encoding="utf-8") + + # Optimized code includes the TreeNode interface (from read-only context) + # This simulates what the AI might return when type definitions are included in context + optimized_code_with_interface = """\ +interface TreeNode { + aboves: string[]; + belows: string[]; + topRow: number; + bottomRow: number; +} + +export function getNearestAbove( + tree: Record, + effectedBoxId: string, +) { + const aboves = tree[effectedBoxId].aboves; + return aboves.reduce((prev: string[], next: string) => { + if (!prev[0]) return [next]; + // Optimized: cache lookups + const nextBottomRow = tree[next].bottomRow; + const prevBottomRow = tree[prev[0]].bottomRow; + return nextBottomRow > prevBottomRow ? [next] : prev; + }, []); +} +""" + + code_markdown = CodeStringsMarkdown( + code_strings=[ + CodeString( + code=optimized_code_with_interface, + file_path=Path("helpers.ts"), + language="typescript" + ) + ], + language="typescript" + ) + + replace_function_definitions_for_language( + ["getNearestAbove"], + code_markdown, + file_path, + temp_project, + ) + + result = file_path.read_text() + + # The TreeNode interface should NOT appear in the result + # (it's already imported, so adding it would cause a duplicate) + assert "interface TreeNode" not in result, ( + f"TreeNode interface should NOT be added to the file since it's already imported.\n" + f"Result contains:\n{result}" + ) + + # The import should still be there + assert 'import type { TreeNode } from "./constants"' in result, ( + f"Original import should be preserved.\nResult:\n{result}" + ) + + # The optimized function should be there + assert "// Optimized: cache lookups" in result, ( + f"Optimized function should be in the result.\nResult:\n{result}" + ) + + # The result should be valid TypeScript + assert ts_support.validate_syntax(result) is True + + def test_multiple_imported_types_not_duplicated(self, ts_support, temp_project): + """Test that multiple imported types are not duplicated.""" + from codeflash.models.models import CodeStringsMarkdown, CodeString + + original_source = """\ +import type { TreeNode, NodeSpace } from "./constants"; +import { MAX_BOX_SIZE } from "./constants"; + +export function processNode(node: TreeNode, space: NodeSpace): number { + return node.topRow + space.top; +} +""" + file_path = temp_project / "processor.ts" + file_path.write_text(original_source, encoding="utf-8") + + # Optimized code includes both interfaces + optimized_code = """\ +interface TreeNode { + topRow: number; + bottomRow: number; +} + +interface NodeSpace { + top: number; + bottom: number; +} + +export function processNode(node: TreeNode, space: NodeSpace): number { + // Optimized + return (node.topRow + space.top) | 0; +} +""" + + code_markdown = CodeStringsMarkdown( + code_strings=[ + CodeString( + code=optimized_code, + file_path=Path("processor.ts"), + language="typescript" + ) + ], + language="typescript" + ) + + replace_function_definitions_for_language( + ["processNode"], + code_markdown, + file_path, + temp_project, + ) + + result = file_path.read_text() + + # Neither interface should be added + assert "interface TreeNode" not in result + assert "interface NodeSpace" not in result + + # Imports should be preserved + assert 'import type { TreeNode, NodeSpace } from "./constants"' in result + + # Optimized code should be there + assert "// Optimized" in result + + assert ts_support.validate_syntax(result) is True