Skip to content

Commit 7215c2b

Browse files
committed
Merge remote-tracking branch 'origin/multi-language' into multi-language
2 parents df529b5 + ced33c6 commit 7215c2b

10 files changed

Lines changed: 65 additions & 50 deletions

File tree

code_to_optimize_js/bubble_sort.js

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,25 @@
99
*/
1010
function bubbleSort(arr) {
1111
const n = arr.length;
12-
const result = [...arr]; // Create a copy to avoid mutation
12+
// Create a copy to avoid mutation
13+
const result = [...arr];
1314

14-
for (let i = 0; i < n - 1; i++) {
15-
for (let j = 0; j < n - i - 1; j++) {
15+
// Optimized bubble: shrink the inner loop to the last swap position
16+
// and exit early if no swaps occur in a pass.
17+
let end = n - 1;
18+
while (end > 0) {
19+
let lastSwap = -1;
20+
for (let j = 0; j < end; j++) {
1621
if (result[j] > result[j + 1]) {
1722
// Swap elements
1823
const temp = result[j];
1924
result[j] = result[j + 1];
2025
result[j + 1] = temp;
26+
lastSwap = j;
2127
}
2228
}
29+
if (lastSwap === -1) break;
30+
end = lastSwap;
2331
}
2432

2533
return result;

codeflash/languages/javascript/runtime/codeflash-comparator.js renamed to code_to_optimize_js/tests/codeflash-comparator.js

File renamed without changes.

codeflash/languages/javascript/runtime/codeflash-compare-results.js renamed to code_to_optimize_js/tests/codeflash-compare-results.js

File renamed without changes.

codeflash/languages/javascript/runtime/codeflash-jest-helper.js renamed to code_to_optimize_js/tests/codeflash-jest-helper.js

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,31 @@ const LOOP_INDEX = parseInt(process.env.CODEFLASH_LOOP_INDEX || '1', 10);
4949
const TEST_ITERATION = process.env.CODEFLASH_TEST_ITERATION || '0';
5050
const TEST_MODULE = process.env.CODEFLASH_TEST_MODULE || '';
5151

52+
// Random seed for reproducible test runs
53+
// Both original and optimized runs use the same seed to get identical "random" values
54+
const RANDOM_SEED = parseInt(process.env.CODEFLASH_RANDOM_SEED || '0', 10);
55+
56+
/**
57+
* Seeded random number generator using mulberry32 algorithm.
58+
* This provides reproducible "random" numbers given a fixed seed.
59+
*/
60+
function createSeededRandom(seed) {
61+
let state = seed;
62+
return function() {
63+
state |= 0;
64+
state = state + 0x6D2B79F5 | 0;
65+
let t = Math.imul(state ^ state >>> 15, 1 | state);
66+
t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
67+
return ((t ^ t >>> 14) >>> 0) / 4294967296;
68+
};
69+
}
70+
71+
// Override Math.random with seeded version if seed is provided
72+
if (RANDOM_SEED !== 0) {
73+
const seededRandom = createSeededRandom(RANDOM_SEED);
74+
Math.random = seededRandom;
75+
}
76+
5277
// Looping configuration for performance benchmarking
5378
const MIN_LOOPS = parseInt(process.env.CODEFLASH_MIN_LOOPS || '5', 10);
5479
const MAX_LOOPS = parseInt(process.env.CODEFLASH_MAX_LOOPS || '100000', 10);
@@ -301,12 +326,10 @@ function capture(funcName, lineId, fn, ...args) {
301326
// Get relative path from cwd and convert to module-style path
302327
const path = require('path');
303328
const relativePath = path.relative(process.cwd(), currentTestPath);
304-
// Convert to module-style path (e.g., "tests/test_foo.test.js" -> "test_foo.test")
305-
// Strip the leading "tests/" directory to match what Jest's junit XML produces
306-
// and what module_name_from_file_path(test_file, tests_root) generates
329+
// Convert to Python module-style path (e.g., "tests/test_foo.test.js" -> "tests.test_foo.test")
330+
// This matches what Jest's junit XML produces
307331
testModulePath = relativePath
308332
.replace(/\\/g, '/') // Handle Windows paths
309-
.replace(/^tests\//, '') // Strip leading "tests/" directory
310333
.replace(/\.js$/, '') // Remove .js extension
311334
.replace(/\.test$/, '.test') // Keep .test suffix
312335
.replace(/\//g, '.'); // Convert path separators to dots
@@ -406,11 +429,9 @@ function capturePerf(funcName, lineId, fn, ...args) {
406429
// Get relative path from cwd and convert to module-style path
407430
const path = require('path');
408431
const relativePath = path.relative(process.cwd(), currentTestPath);
409-
// Convert to module-style path (e.g., "tests/test_foo.test.js" -> "test_foo.test")
410-
// Strip leading "tests/" to match XML module path format
432+
// Convert to Python module-style path (e.g., "tests/test_foo.test.js" -> "tests.test_foo.test")
411433
testModulePath = relativePath
412434
.replace(/\\/g, '/')
413-
.replace(/^tests\//, '') // Strip leading "tests/" directory
414435
.replace(/\.js$/, '')
415436
.replace(/\.test$/, '.test')
416437
.replace(/\//g, '.');
@@ -548,11 +569,9 @@ function capturePerfLooped(funcName, lineId, fn, ...args) {
548569
// Get relative path from cwd and convert to module-style path
549570
const path = require('path');
550571
const relativePath = path.relative(process.cwd(), currentTestPath);
551-
// Convert to module-style path (e.g., "tests/test_foo.test.js" -> "test_foo.test")
552-
// Strip leading "tests/" to match XML module path format
572+
// Convert to Python module-style path (e.g., "tests/test_foo.test.js" -> "tests.test_foo.test")
553573
testModulePath = relativePath
554574
.replace(/\\/g, '/')
555-
.replace(/^tests\//, '') // Strip leading "tests/" directory
556575
.replace(/\.js$/, '')
557576
.replace(/\.test$/, '.test')
558577
.replace(/\//g, '.');
@@ -638,8 +657,8 @@ function capturePerfLooped(funcName, lineId, fn, ...args) {
638657
break;
639658
}
640659

641-
// Stop if we've reached min loops AND exceeded a reasonable portion of time
642-
if (loopCount >= MIN_LOOPS && elapsedMs >= TARGET_DURATION_MS * 0.8) {
660+
// Stop if we've reached min loops AND exceeded time limit
661+
if (loopCount >= MIN_LOOPS && elapsedMs >= TARGET_DURATION_MS) {
643662
break;
644663
}
645664

codeflash/languages/javascript/runtime/codeflash-serializer.js renamed to code_to_optimize_js/tests/codeflash-serializer.js

File renamed without changes.

codeflash/api/aiservice.py

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -101,10 +101,7 @@ def make_ai_service_request(
101101
return response
102102

103103
def _get_valid_candidates(
104-
self,
105-
optimizations_json: list[dict[str, Any]],
106-
source: OptimizedCandidateSource,
107-
language: str = "python",
104+
self, optimizations_json: list[dict[str, Any]], source: OptimizedCandidateSource, language: str = "python"
108105
) -> list[OptimizedCandidate]:
109106
candidates: list[OptimizedCandidate] = []
110107
for opt in optimizations_json:
@@ -183,8 +180,8 @@ def optimize_code( # noqa: D417
183180
payload["language_version"] = language_version or "ES2022"
184181

185182
# DEBUG: Print payload language field
186-
print(
187-
f"[CLI DEBUG] Sending optimize request with language='{payload['language']}' (type: {type(payload['language'])})"
183+
logger.debug(
184+
f"Sending optimize request with language='{payload['language']}' (type: {type(payload['language'])})"
188185
)
189186
logger.debug(f"Sending optimize request: trace_id={trace_id}, n_candidates={payload['n_candidates']}")
190187

@@ -685,9 +682,7 @@ def generate_regression_tests( # noqa: D417
685682
payload["language_version"] = language_version or "ES2022"
686683

687684
# DEBUG: Print payload language field
688-
print(
689-
f"[CLI DEBUG] Sending testgen request with language='{payload['language']}', framework='{test_framework}'"
690-
)
685+
logger.debug(f"Sending testgen request with language='{payload['language']}', framework='{test_framework}'")
691686
try:
692687
response = self.make_ai_service_request("/testgen", payload=payload, timeout=self.timeout)
693688
except requests.exceptions.RequestException as e:

codeflash/languages/javascript/runtime/__init__.py

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66

77
from pathlib import Path
88

9-
# Directory containing the JavaScript runtime files
10-
RUNTIME_DIR = Path(__file__).parent
9+
# TEMPORARY: Currently pointing to the development directory.
10+
# In the future, these scripts should be published as an npm package (e.g., @codeflash/runtime)
11+
# and this module should read from the installed package location in the user's node_modules.
12+
RUNTIME_DIR = Path(__file__).parent.parent.parent.parent.parent / "code_to_optimize_js"
1113

1214

1315
def get_jest_helper_path() -> Path:
@@ -51,9 +53,4 @@ def get_all_runtime_files() -> list[Path]:
5153
5254
Returns a list of all JS files that should be copied to the user's project.
5355
"""
54-
return [
55-
get_jest_helper_path(),
56-
get_comparator_path(),
57-
get_compare_results_path(),
58-
get_serializer_path(),
59-
]
56+
return [get_jest_helper_path(), get_comparator_path(), get_compare_results_path(), get_serializer_path()]

codeflash/optimization/function_optimizer.py

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1957,11 +1957,7 @@ def process_review(
19571957
)
19581958

19591959
generated_tests = add_runtime_comments_to_generated_tests(
1960-
generated_tests,
1961-
original_runtime_by_test,
1962-
optimized_runtime_by_test,
1963-
self.test_cfg.tests_project_rootdir,
1964-
language=self.function_to_optimize.language,
1960+
generated_tests, original_runtime_by_test, optimized_runtime_by_test, self.test_cfg.tests_project_rootdir
19651961
)
19661962

19671963
generated_tests_str = ""
@@ -2447,11 +2443,9 @@ def run_optimized_candidate(
24472443
logger.info("h3|Test results matched ✅")
24482444
console.rule()
24492445
else:
2450-
if not self.is_js:
2451-
# TODO: get repair to work with js/ts code
2452-
self.repair_if_possible(
2453-
candidate, diffs, eval_ctx, code_context, len(candidate_behavior_results), exp_type
2454-
)
2446+
self.repair_if_possible(
2447+
candidate, diffs, eval_ctx, code_context, len(candidate_behavior_results), exp_type
2448+
)
24552449
return self.get_results_not_matched_error()
24562450

24572451
logger.info(f"loading|Running performance tests for candidate {optimization_candidate_index}...")
@@ -2606,7 +2600,7 @@ def run_and_parse_tests(
26062600

26072601
if testing_type in {TestingMode.BEHAVIOR, TestingMode.PERFORMANCE}:
26082602
# For JavaScript behavior tests, skip SQLite cleanup - files needed for JS-native comparison
2609-
#TODO (ali): make sure it works fine
2603+
# TODO (ali): make sure it works fine
26102604
is_js_for_original_code = self.is_js and optimization_iteration == 0
26112605
is_js_behavior = (self.is_js and testing_type == TestingMode.BEHAVIOR) or is_js_for_original_code
26122606

codeflash/verification/equivalence.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
from codeflash.cli_cmds.console import logger
1111
from codeflash.code_utils.code_utils import shorten_pytest_error
12+
from codeflash.languages.javascript.runtime import get_compare_results_path
1213
from codeflash.models.models import TestDiff, TestDiffScope, TestResults, TestType, VerificationType
1314
from codeflash.verification.comparator import comparator
1415

@@ -17,10 +18,7 @@
1718

1819
INCREASED_RECURSION_LIMIT = 5000
1920

20-
# Path to JavaScript comparison script (relative to codeflash package)
21-
JAVASCRIPT_COMPARATOR_SCRIPT = (
22-
Path(__file__).parent.parent / "languages" / "javascript" / "runtime" / "codeflash-compare-results.js"
23-
)
21+
JAVASCRIPT_COMPARATOR_SCRIPT = get_compare_results_path()
2422

2523
reprlib_repr = reprlib.Repr()
2624
reprlib_repr.maxstring = 1500
@@ -223,14 +221,18 @@ def compare_javascript_test_results(
223221
scope = TestDiffScope.DID_PASS
224222

225223
test_info = diff.get("test_info", {})
224+
# Build a test identifier string for JavaScript tests
225+
test_function_name = test_info.get("test_function_name", "unknown")
226+
function_getting_tested = test_info.get("function_getting_tested", "unknown")
227+
test_src_code = f"// Test: {test_function_name}\n// Testing function: {function_getting_tested}"
226228

227229
test_diffs.append(
228230
TestDiff(
229231
scope=scope,
230232
original_value=diff.get("original"),
231233
candidate_value=diff.get("candidate"),
232-
test_src_code=None, # JavaScript tests don't have Python source
233-
candidate_pytest_error=None,
234+
test_src_code=test_src_code,
235+
candidate_pytest_error=diff.get("candidate_error"),
234236
original_pass=True, # Assume passed if we got results
235237
candidate_pass=diff.get("scope") != "missing",
236238
original_pytest_error=None,

codeflash/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
# These version placeholders will be replaced by uv-dynamic-versioning during build.
2-
__version__ = "0.19.1.post108.dev0+3a96f0ad"
2+
__version__ = "0.19.1"

0 commit comments

Comments
 (0)