Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
102 commits
Select commit Hold shift + click to select a range
0a57afa
first pass at wrapper deco behavioral
KRRT7 Aug 26, 2025
c9aaaad
don't reapply too early
KRRT7 Aug 26, 2025
ad1d085
feedback
KRRT7 Aug 27, 2025
14353d4
Update codeflash_wrap_decorator.py
KRRT7 Aug 27, 2025
3c05f97
Update function_optimizer.py
KRRT7 Aug 28, 2025
bafad49
Update codeflash_wrap_decorator.py
KRRT7 Aug 28, 2025
745a3f7
test gen for async too
KRRT7 Aug 29, 2025
5a47237
Update aiservice.py
KRRT7 Aug 29, 2025
1ab1886
create path object once
KRRT7 Aug 29, 2025
af96274
use sqlite vs pickle
KRRT7 Aug 29, 2025
4dfda09
no dependencies on codeflash
KRRT7 Aug 29, 2025
2a2a3a3
add tests for async wrapper
KRRT7 Aug 29, 2025
b569d44
linting
KRRT7 Aug 29, 2025
4514dd6
address feedback re: using existing logic from tracer & cf capture
KRRT7 Aug 29, 2025
fb9555e
test
KRRT7 Aug 30, 2025
66f7a84
add unit tests
KRRT7 Aug 30, 2025
aa872b5
be consistent
KRRT7 Aug 30, 2025
0319803
Update codeflash_wrap_decorator.py
KRRT7 Aug 30, 2025
9bbe988
some async changes
KRRT7 Aug 31, 2025
1fe7c04
Update test_add_runtime_comments.py
KRRT7 Aug 31, 2025
91b8902
Update codeflash/code_utils/codeflash_wrap_decorator.py
misrasaurabh1 Sep 2, 2025
0f066c0
Merge branch 'granular-async-instrumentation' of https://github.com/c…
KRRT7 Sep 2, 2025
69fa701
mirror tests
KRRT7 Sep 3, 2025
862415e
add support for async tests in edit_generated_tests
KRRT7 Sep 3, 2025
febf2b6
add tests with run_and_parse_tests
KRRT7 Sep 3, 2025
61aade1
Update test_async_run_and_parse_tests.py
KRRT7 Sep 3, 2025
5bf5c20
⚡️ Speed up method `CommentMapper.visit_ClassDef` by 197% in PR #687 …
codeflash-ai[bot] Sep 3, 2025
b2bf9fa
add missing imports
KRRT7 Sep 3, 2025
ef74bb5
Update edit_generated_tests.py
KRRT7 Sep 3, 2025
e119085
Merge pull request #706 from codeflash-ai/codeflash/optimize-pr687-20…
KRRT7 Sep 3, 2025
600dafc
⚡️ Speed up method `CommentMapper.visit_FunctionDef` by 84% in PR #68…
codeflash-ai[bot] Sep 3, 2025
1142de5
linting / formatting
KRRT7 Sep 3, 2025
14280fa
Merge pull request #709 from codeflash-ai/codeflash/optimize-pr687-20…
KRRT7 Sep 3, 2025
3ca6fad
linter
KRRT7 Sep 3, 2025
73d03ae
add E2E
KRRT7 Sep 3, 2025
0af669f
Update workload.py
KRRT7 Sep 3, 2025
1ebc616
Update workload.py
KRRT7 Sep 3, 2025
d8a6ab2
temp
KRRT7 Sep 3, 2025
7bcd136
go
KRRT7 Sep 3, 2025
2429801
restore original code if not found for async
KRRT7 Sep 3, 2025
a364075
basic async critic - WIP
KRRT7 Sep 8, 2025
a535fa2
for AsyncFunctionDef too
KRRT7 Sep 8, 2025
075a2ae
add unit tests
KRRT7 Sep 8, 2025
4fac1e2
increase test coverage
KRRT7 Sep 8, 2025
8039ec4
coverage utils
KRRT7 Sep 8, 2025
0a296e2
more unit tests
KRRT7 Sep 8, 2025
189fe12
linting
KRRT7 Sep 8, 2025
878699a
Delete e2e-async.yaml
KRRT7 Sep 8, 2025
52a5a0c
Merge branch 'granular-async-instrumentation' of https://github.com/c…
KRRT7 Sep 8, 2025
ce421d4
Revert "basic async critic - WIP"
KRRT7 Sep 9, 2025
a6072b9
Merge branch 'granular-async-instrumentation' of https://github.com/c…
KRRT7 Sep 9, 2025
b189f1b
Merge pull request #719 from codeflash-ai/async-support-for
KRRT7 Sep 9, 2025
07cc91b
use async event loop timer
KRRT7 Sep 10, 2025
60b356c
disable crosshair for concolic
KRRT7 Sep 11, 2025
7e4ecb3
add debugs
KRRT7 Sep 11, 2025
ab500c1
Update codeflash.code-workspace
KRRT7 Sep 13, 2025
db82269
Optimize compare_test_results
codeflash-ai[bot] Sep 13, 2025
324f607
Merge pull request #732 from codeflash-ai/codeflash/optimize-pr687-20…
KRRT7 Sep 13, 2025
f98dae6
Update parse_test_output.py
KRRT7 Sep 16, 2025
96211b8
add tests_project_root to the wrappers
KRRT7 Sep 16, 2025
f872d5d
Update codeflash.code-workspace
KRRT7 Sep 16, 2025
ee1d386
Revert "add tests_project_root to the wrappers"
KRRT7 Sep 16, 2025
b858881
Update test_runner.py
KRRT7 Sep 16, 2025
a42c63a
Delete test_extract_test_context_from_frame.py
KRRT7 Sep 17, 2025
fadbdf7
better impl
KRRT7 Sep 17, 2025
1034ee8
Update parse_test_output.py
KRRT7 Sep 17, 2025
4ae0f93
calculate throughput
KRRT7 Sep 17, 2025
fb66ff5
add unit tests
KRRT7 Sep 17, 2025
2190963
fix wrapper validation test
KRRT7 Sep 17, 2025
2a3c975
fix linting
KRRT7 Sep 17, 2025
9c53948
cleanup
KRRT7 Sep 17, 2025
e5c4562
Update function_optimizer.py
KRRT7 Sep 17, 2025
04d31b5
optimized candidate result oo
KRRT7 Sep 17, 2025
8e516fc
critic
KRRT7 Sep 18, 2025
d7f9bbd
update tests again
KRRT7 Sep 18, 2025
58e5592
Update codeflash_wrap_decorator.py
KRRT7 Sep 18, 2025
a7a06ea
Update test_async_wrapper_sqlite_validation.py
KRRT7 Sep 22, 2025
d1b40d0
check.
KRRT7 Sep 22, 2025
a947ea0
go
KRRT7 Sep 22, 2025
2e0f38f
throughput
KRRT7 Sep 22, 2025
d3eefca
perfstdout
KRRT7 Sep 22, 2025
039c5ba
calculate throughtput for baseline
KRRT7 Sep 22, 2025
aba3dcb
Update function_optimizer.py
KRRT7 Sep 22, 2025
d3afd8a
Update critic.py
KRRT7 Sep 22, 2025
971470e
adjust critic
KRRT7 Sep 22, 2025
00a8981
update tests
KRRT7 Sep 22, 2025
1e4b3ba
pre-commit fixes
KRRT7 Sep 22, 2025
654055d
unbound local
KRRT7 Sep 22, 2025
540b8aa
Optimize AsyncCallInstrumenter.visit_ClassDef
codeflash-ai[bot] Sep 22, 2025
7aa30a8
Merge pull request #744 from codeflash-ai/codeflash/optimize-pr739-20…
KRRT7 Sep 22, 2025
fecdc94
Update .pre-commit-config.yaml
KRRT7 Sep 22, 2025
eccdeff
Update pre-commit.yaml
KRRT7 Sep 22, 2025
8b1b2be
Update .pre-commit-config.yaml
KRRT7 Sep 22, 2025
515b976
Delete pre-commit.yaml
KRRT7 Sep 22, 2025
30c8988
give throughput context to explanations
KRRT7 Sep 22, 2025
d8849a5
logger.debug
KRRT7 Sep 23, 2025
47384e2
add end to end test
KRRT7 Sep 23, 2025
43615bd
Create e2e-async.yaml
KRRT7 Sep 23, 2025
9aa34d9
Merge pull request #752 from codeflash-ai/end-to-end-test
KRRT7 Sep 23, 2025
b221be4
Merge pull request #739 from codeflash-ai/get-throughput-from-output
KRRT7 Sep 24, 2025
b6b4b63
only for debug
KRRT7 Sep 26, 2025
7bbb1e7
adjust tests
KRRT7 Sep 26, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions .github/workflows/e2e-async.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
name: E2E - Async

on:
pull_request:
paths:
- '**' # Trigger for all paths

workflow_dispatch:

jobs:
async-optimization:
# Dynamically determine if environment is needed only when workflow files change and contributor is external
environment: ${{ (github.event_name == 'workflow_dispatch' || (contains(toJSON(github.event.pull_request.files.*.filename), '.github/workflows/') && github.event.pull_request.user.login != 'misrasaurabh1' && github.event.pull_request.user.login != 'KRRT7')) && 'external-trusted-contributors' || '' }}

runs-on: ubuntu-latest
env:
CODEFLASH_AIS_SERVER: prod
POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }}
CODEFLASH_API_KEY: ${{ secrets.CODEFLASH_API_KEY }}
COLUMNS: 110
MAX_RETRIES: 3
RETRY_DELAY: 5
EXPECTED_IMPROVEMENT_PCT: 10
CODEFLASH_END_TO_END: 1
steps:
- name: 🛎️ Checkout
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.ref }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}

- name: Validate PR
run: |
# Check for any workflow changes
if git diff --name-only "${{ github.event.pull_request.base.sha }}" "${{ github.event.pull_request.head.sha }}" | grep -q "^.github/workflows/"; then
echo "⚠️ Workflow changes detected."

# Get the PR author
AUTHOR="${{ github.event.pull_request.user.login }}"
echo "PR Author: $AUTHOR"

# Allowlist check
if [[ "$AUTHOR" == "misrasaurabh1" || "$AUTHOR" == "KRRT7" ]]; then
echo "✅ Authorized user ($AUTHOR). Proceeding."
elif [[ "${{ github.event.pull_request.state }}" == "open" ]]; then
echo "✅ PR triggered by 'pull_request_target' and is open. Assuming protection rules are in place. Proceeding."
else
echo "⛔ Unauthorized user ($AUTHOR) attempting to modify workflows. Exiting."
exit 1
fi
else
echo "✅ No workflow file changes detected. Proceeding."
fi

- name: Set up Python 3.11 for CLI
uses: astral-sh/setup-uv@v5
with:
python-version: 3.11.6

- name: Install dependencies (CLI)
run: |
uv sync

- name: Run Codeflash to optimize async code
id: optimize_async_code
run: |
uv run python tests/scripts/end_to_end_test_async.py
19 changes: 0 additions & 19 deletions .github/workflows/pre-commit.yaml

This file was deleted.

3 changes: 2 additions & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.12.7
rev: v0.13.1
hooks:
# Run the linter.
- id: ruff-check
args: [ --config=pyproject.toml ]
# Run the formatter.
- id: ruff-format
43 changes: 43 additions & 0 deletions code_to_optimize/async_bubble_sort.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import asyncio
from typing import List, Union


async def async_sorter(lst: List[Union[int, float]]) -> List[Union[int, float]]:
"""
Async bubble sort implementation for testing.
"""
print("codeflash stdout: Async sorting list")

await asyncio.sleep(0.01)

n = len(lst)
for i in range(n):
for j in range(0, n - i - 1):
if lst[j] > lst[j + 1]:
lst[j], lst[j + 1] = lst[j + 1], lst[j]

result = lst.copy()
print(f"result: {result}")
return result


class AsyncBubbleSorter:
"""Class with async sorting method for testing."""

async def sorter(self, lst: List[Union[int, float]]) -> List[Union[int, float]]:
"""
Async bubble sort implementation within a class.
"""
print("codeflash stdout: AsyncBubbleSorter.sorter() called")

# Add some async delay
await asyncio.sleep(0.005)

n = len(lst)
for i in range(n):
for j in range(0, n - i - 1):
if lst[j] > lst[j + 1]:
lst[j], lst[j + 1] = lst[j + 1], lst[j]

result = lst.copy()
return result
16 changes: 16 additions & 0 deletions code_to_optimize/code_directories/async_e2e/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import time
import asyncio


async def retry_with_backoff(func, max_retries=3):
if max_retries < 1:
raise ValueError("max_retries must be at least 1")
last_exception = None
for attempt in range(max_retries):
try:
return await func()
except Exception as e:
last_exception = e
if attempt < max_retries - 1:
time.sleep(0.0001 * attempt)
raise last_exception
6 changes: 6 additions & 0 deletions code_to_optimize/code_directories/async_e2e/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[tool.codeflash]
disable-telemetry = true
formatter-cmds = ["ruff check --exit-zero --fix $file", "ruff format $file"]
module-root = "."
test-framework = "pytest"
tests-root = "tests"
Empty file.
6 changes: 5 additions & 1 deletion codeflash.code-workspace
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,11 @@
"request": "launch",
"program": "${workspaceFolder:codeflash}/codeflash/main.py",
"args": [
"--all",
"--file",
"src/async_examples/concurrency.py",
"--function",
"task",
"--verbose"
],
"cwd": "${input:chooseCwd}",
"console": "integratedTerminal",
Expand Down
14 changes: 14 additions & 0 deletions codeflash/api/aiservice.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ def optimize_python_code( # noqa: D417
trace_id: str,
num_candidates: int = 10,
experiment_metadata: ExperimentMetadata | None = None,
*,
is_async: bool = False,
) -> list[OptimizedCandidate]:
"""Optimize the given python code for performance by making a request to the Django endpoint.

Expand Down Expand Up @@ -131,6 +133,7 @@ def optimize_python_code( # noqa: D417
"current_username": get_last_commit_author_if_pr_exists(None),
"repo_owner": git_repo_owner,
"repo_name": git_repo_name,
"is_async": is_async,
}

logger.info("Generating optimized candidates…")
Expand Down Expand Up @@ -295,6 +298,9 @@ def get_new_explanation( # noqa: D417
annotated_tests: str,
optimization_id: str,
original_explanation: str,
original_throughput: str | None = None,
optimized_throughput: str | None = None,
throughput_improvement: str | None = None,
) -> str:
"""Optimize the given python code for performance by making a request to the Django endpoint.

Expand All @@ -311,6 +317,9 @@ def get_new_explanation( # noqa: D417
- annotated_tests: str - test functions annotated with runtime
- optimization_id: str - unique id of opt candidate
- original_explanation: str - original_explanation generated for the opt candidate
- original_throughput: str | None - throughput for the baseline code (operations per second)
- optimized_throughput: str | None - throughput for the optimized code (operations per second)
- throughput_improvement: str | None - throughput improvement percentage

Returns
-------
Expand All @@ -330,6 +339,9 @@ def get_new_explanation( # noqa: D417
"optimization_id": optimization_id,
"original_explanation": original_explanation,
"dependency_code": dependency_code,
"original_throughput": original_throughput,
"optimized_throughput": optimized_throughput,
"throughput_improvement": throughput_improvement,
}
logger.info("Generating explanation")
console.rule()
Expand Down Expand Up @@ -439,7 +451,9 @@ def generate_regression_tests( # noqa: D417
"test_index": test_index,
"python_version": platform.python_version(),
"codeflash_version": codeflash_version,
"is_async": function_to_optimize.is_async,
}

try:
response = self.make_ai_service_request("/testgen", payload=payload, timeout=600)
except requests.exceptions.RequestException as e:
Expand Down
89 changes: 86 additions & 3 deletions codeflash/code_utils/code_extractor.py
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,8 @@ def _collect_imports_from_block(self, block: cst.IndentedBlock) -> None:
if child.module is None:
continue
module = self.get_full_dotted_name(child.module)
if isinstance(child.names, cst.ImportStar):
continue
for alias in child.names:
if isinstance(alias, cst.ImportAlias):
name = alias.name.value
Expand Down Expand Up @@ -403,6 +405,73 @@ def add_global_assignments(src_module_code: str, dst_module_code: str) -> str:
return transformed_module.code


def resolve_star_import(module_name: str, project_root: Path) -> set[str]:
try:
module_path = module_name.replace(".", "/")
possible_paths = [project_root / f"{module_path}.py", project_root / f"{module_path}/__init__.py"]

module_file = None
for path in possible_paths:
if path.exists():
module_file = path
break

if module_file is None:
logger.warning(f"Could not find module file for {module_name}, skipping star import resolution")
return set()

with module_file.open(encoding="utf8") as f:
module_code = f.read()

tree = ast.parse(module_code)

all_names = None
for node in ast.walk(tree):
if (
isinstance(node, ast.Assign)
and len(node.targets) == 1
and isinstance(node.targets[0], ast.Name)
and node.targets[0].id == "__all__"
):
if isinstance(node.value, (ast.List, ast.Tuple)):
all_names = []
for elt in node.value.elts:
if isinstance(elt, ast.Constant) and isinstance(elt.value, str):
all_names.append(elt.value)
elif isinstance(elt, ast.Str): # Python < 3.8 compatibility
all_names.append(elt.s)
break

if all_names is not None:
return set(all_names)

public_names = set()
for node in tree.body:
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)):
if not node.name.startswith("_"):
public_names.add(node.name)
elif isinstance(node, ast.Assign):
for target in node.targets:
if isinstance(target, ast.Name) and not target.id.startswith("_"):
public_names.add(target.id)
elif isinstance(node, ast.AnnAssign):
if isinstance(node.target, ast.Name) and not node.target.id.startswith("_"):
public_names.add(node.target.id)
elif isinstance(node, ast.Import) or (
isinstance(node, ast.ImportFrom) and not any(alias.name == "*" for alias in node.names)
):
for alias in node.names:
name = alias.asname or alias.name
if not name.startswith("_"):
public_names.add(name)

return public_names # noqa: TRY300

except Exception as e:
logger.warning(f"Error resolving star import for {module_name}: {e}")
return set()


def add_needed_imports_from_module(
src_module_code: str,
dst_module_code: str,
Expand Down Expand Up @@ -457,9 +526,23 @@ def add_needed_imports_from_module(
f"{mod}.{obj}" in helper_functions_fqn or dst_context.full_module_name == mod # avoid circular deps
):
continue # Skip adding imports for helper functions already in the context
if f"{mod}.{obj}" not in dotted_import_collector.imports:
AddImportsVisitor.add_needed_import(dst_context, mod, obj)
RemoveImportsVisitor.remove_unused_import(dst_context, mod, obj)

# Handle star imports by resolving them to actual symbol names
if obj == "*":
resolved_symbols = resolve_star_import(mod, project_root)
logger.debug(f"Resolved star import from {mod}: {resolved_symbols}")

for symbol in resolved_symbols:
if (
f"{mod}.{symbol}" not in helper_functions_fqn
and f"{mod}.{symbol}" not in dotted_import_collector.imports
):
AddImportsVisitor.add_needed_import(dst_context, mod, symbol)
RemoveImportsVisitor.remove_unused_import(dst_context, mod, symbol)
else:
if f"{mod}.{obj}" not in dotted_import_collector.imports:
AddImportsVisitor.add_needed_import(dst_context, mod, obj)
RemoveImportsVisitor.remove_unused_import(dst_context, mod, obj)
except Exception as e:
logger.exception(f"Error adding imports to destination module code: {e}")
return dst_module_code
Expand Down
Loading
Loading