From 5296dc832172072bd2455e0ec8789169c0188b25 Mon Sep 17 00:00:00 2001 From: Saurabh Misra Date: Sat, 31 Jan 2026 06:47:01 +0000 Subject: [PATCH 1/3] fix: calculate MAX_BATCHES correctly in Jest loop-runner Previously, MAX_BATCHES was set to PERF_LOOP_COUNT directly (e.g., 250), which caused the loop-runner to run 250 batches even though only 25 batches were needed to produce timing data (with BATCH_SIZE=10). The bug was that timing markers only appeared for the first N batches (where N = LOOP_COUNT / BATCH_SIZE), and the remaining batches were wasted overhead. Fix: Calculate MAX_BATCHES as ceil(LOOP_COUNT / BATCH_SIZE) + 1, capped at LOOP_COUNT. This ensures only the necessary batches run: - With LOOP_COUNT=250, BATCH_SIZE=10: MAX_BATCHES = 26 (not 250) This significantly improves benchmark efficiency by eliminating wasted Jest passes that don't contribute timing data. Co-Authored-By: Claude Opus 4.5 --- packages/codeflash/runtime/loop-runner.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/codeflash/runtime/loop-runner.js b/packages/codeflash/runtime/loop-runner.js index b75e44d78..ca04c95d6 100644 --- a/packages/codeflash/runtime/loop-runner.js +++ b/packages/codeflash/runtime/loop-runner.js @@ -32,7 +32,11 @@ const internalRequire = createRequire(jestRunnerPath); const runTest = internalRequire('./runTest').default; // 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); From a3dd1b3484dab6e8de73357728ab04af18ecb4b2 Mon Sep 17 00:00:00 2001 From: Saurabh Misra Date: Sat, 31 Jan 2026 07:50:20 +0000 Subject: [PATCH 2/3] fix: prevent imported types from being duplicated during code replacement When the optimized code contains type definitions (interfaces, types) that are already imported in the original file, the code replacer was incorrectly adding them as new declarations because it only checked for existing declarations, not imports. Fix: In `_add_global_declarations_for_language`, also check for imported names (default imports, named imports, namespace imports) and exclude them from being added as new declarations. This fixes the bug where imported interfaces like `TreeNode` were being duplicated in the output even though they were already imported. See: https://github.com/codeflash-ai/appsmith/pull/20 Co-Authored-By: Claude Opus 4.5 --- codeflash/code_utils/code_replacer.py | 13 ++ tests/test_languages/test_js_code_replacer.py | 171 ++++++++++++++++++ 2 files changed, 184 insertions(+) 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/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 From 3eab43732615e1413d322e211acc4e7335819c26 Mon Sep 17 00:00:00 2001 From: Saurabh Misra Date: Sat, 31 Jan 2026 07:57:48 +0000 Subject: [PATCH 3/3] fix: pass type definitions as read-only context instead of prepending to source code The root cause of the duplicate type definitions bug was that code_context.read_only_context (containing type definitions) was being prepended to target_file_code, making it part of the read-writable code sent to the AI. When the AI receives: - source_code = code with type definitions prepended - dependency_code = "" (empty) The AI treats the type definitions as code to optimize and naturally includes them in its output. Fix: Pass read_only_context as read_only_context_code (dependency_code) instead of prepending it to the source code. This way: - source_code = only the function to optimize - dependency_code = type definitions (AI sees as context, won't include in output) Combined with the previous code_replacer fix (checking for imported names), this provides defense-in-depth against duplicate type definitions. Co-Authored-By: Claude Opus 4.5 --- codeflash/context/code_context_extractor.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) 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,