Skip to content

Commit deb9d24

Browse files
feat: add top level async streaming (#655)
* feat: add top level async streaming Co-authored-by: Akihiko Kuroda <akihikokuroda2020@gmail.com> * feat: add examples for top level streaming * fix: pr comments * fix: pr comments and add simpler example * fix: issues with docs gen / quality * feat: add test files for mypy to catch overload typing issues --------- Co-authored-by: Akihiko Kuroda <akihikokuroda2020@gmail.com>
1 parent d971776 commit deb9d24

32 files changed

Lines changed: 5904 additions & 2750 deletions

cli/fix/__init__.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
"""CLI for fixing async calls after top-level ainstruct, aquery, and aact contract change.."""
2+
3+
from enum import StrEnum
4+
5+
import typer
6+
7+
fix_app = typer.Typer(name="fix", help="Fix code for API changes.")
8+
9+
10+
class _FixMode(StrEnum):
11+
"""Types of fixes that can be applied."""
12+
13+
ADD_AWAIT_RESULT = "add-await-result"
14+
ADD_STREAM_LOOP = "add-stream-loop"
15+
16+
17+
from cli.fix.commands import fix_async # noqa: E402
18+
19+
fix_app.command("async")(fix_async)

cli/fix/commands.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
"""CLI command for `m fix async`."""
2+
3+
from pathlib import Path
4+
5+
import typer
6+
7+
from cli.fix import _FixMode
8+
9+
10+
def fix_async(
11+
path: str = typer.Argument(..., help="File or directory to scan"),
12+
mode: _FixMode = typer.Option(
13+
_FixMode.ADD_AWAIT_RESULT, "--mode", "-m", help="Fix strategy to apply"
14+
),
15+
dry_run: bool = typer.Option(
16+
False, "--dry-run", help="Report locations without modifying files"
17+
),
18+
):
19+
"""Fix async calls (aact, ainstruct, aquery) for the await_result default change.
20+
21+
Args:
22+
path: File or directory to scan.
23+
mode: Fix strategy to apply.
24+
dry_run: If ``True``, report locations without modifying files.
25+
26+
Raises:
27+
typer.Exit: If *path* does not exist.
28+
29+
\b
30+
Modes:
31+
add-await-result (default) Adds await_result=True to each call so it
32+
blocks until the result is ready. Use this if you don't
33+
need to stream partial results.
34+
add-stream-loop Inserts a `while not r.is_computed(): await r.astream()`
35+
loop after each call. This only works if you passed a
36+
streaming model option (e.g. stream=True) to the call;
37+
otherwise the loop will finish immediately.
38+
39+
\b
40+
Best practices:
41+
- Run with --dry-run first to review what will be changed.
42+
- Only run a given mode once per file. The tool detects prior fixes and
43+
skips calls that already have await_result=True or a stream loop, but
44+
it is safest to treat it as a one-shot migration.
45+
- Do not run both modes on the same file. If a stream loop is already
46+
present, add-await-result will skip that call (and vice versa).
47+
48+
\b
49+
Detection notes:
50+
- Most import styles are detected: `import mellea`,
51+
`from mellea import MelleaSession`,
52+
`from mellea.stdlib.functional import aact`, module aliases, etc.
53+
- Calls that are already followed by `await r.avalue()`,
54+
`await r.astream()`, or a `while not r.is_computed()` loop are
55+
automatically skipped, even when nested inside if/try/for blocks.
56+
"""
57+
from cli.fix.fixer import fix_path
58+
59+
target = Path(path)
60+
if not target.exists():
61+
typer.echo(f"Error: {path} does not exist", err=True)
62+
raise typer.Exit(code=1)
63+
64+
result = fix_path(target, mode, dry_run=dry_run)
65+
66+
if result.total_fixes == 0:
67+
typer.echo("No fixable calls found.")
68+
return
69+
70+
action = "Found" if dry_run else "Fixed"
71+
typer.echo(
72+
f"{action} {result.total_fixes} call(s) in {result.files_affected} file(s):"
73+
)
74+
for loc in result.locations:
75+
typer.echo(
76+
f" {loc.filepath}:{loc.line} - {loc.function_name}() [{loc.call_style}]"
77+
)

0 commit comments

Comments
 (0)