diff --git a/README.md b/README.md index 790d5a2..eb10e47 100644 --- a/README.md +++ b/README.md @@ -40,8 +40,9 @@ app = App( ), ) -@app.tool(id="send_email", description="Send one email.") +@app.tool(id="send_email") def send_email(to: str, subject: str, body: str) -> dict: + """Send one email.""" return {"ok": True, "to": to, "subject": subject} DAILY_FOLLOWUPS = schedule.cron( @@ -88,15 +89,6 @@ ara runtime control call --session sess-123 --action list_windows ara runtime control call --session sess-123 --action launch_app --arg id=browser --arg url=https://mail.google.com ``` -If you prefer embedded script commands (`python app.py deploy`), add: - -```python -from ara_sdk import run_cli - -if __name__ == "__main__": - run_cli(app) -``` - `ara logs app.py` streams live runtime events for the app across all active runs. Each line includes timestamp + run id + event type. To persist output, use shell piping: diff --git a/examples/01-c-agent-skills-loading.py b/examples/01-c-agent-skills-loading.py index bb7e12a..7c6cdf1 100644 --- a/examples/01-c-agent-skills-loading.py +++ b/examples/01-c-agent-skills-loading.py @@ -20,9 +20,9 @@ @app.tool( id="title_case_decorator", - description="Convert input text by dispatching to the decorator-registered title-case handler.", ) def title_case_decorator(text: str) -> dict: + """Convert input text by dispatching to the decorator-registered title-case handler.""" # Keep registry + decorator local to the tool function because ara_sdk stores # and executes function source for runtime tools; module globals are not guaranteed. handlers: dict[str, callable] = {} diff --git a/examples/02-canonical-email-chat-cron.py b/examples/02-canonical-email-chat-cron.py index 1ccec42..05a37e3 100644 --- a/examples/02-canonical-email-chat-cron.py +++ b/examples/02-canonical-email-chat-cron.py @@ -54,8 +54,9 @@ def _looks_like_email(value: str) -> bool: return re.fullmatch(r"[^@\s]+@[^@\s]+\.[^@\s]+", candidate) is not None -@app.tool(id="send_email", description="Send one email via Resend API.") +@app.tool(id="send_email") def send_email(to: str, subject: str, body: str) -> dict: + """Send one email via Resend API.""" api_key = (os.getenv("RESEND_API_KEY") or "").strip() sender = (os.getenv("CRON_EMAIL_FROM") or "").strip() recipient = (to or "").strip() diff --git a/src/ara_sdk/__init__.py b/src/ara_sdk/__init__.py index ccbacc9..c82d2d1 100644 --- a/src/ara_sdk/__init__.py +++ b/src/ara_sdk/__init__.py @@ -17,7 +17,6 @@ local_file, runtime, run_auth_cli, - run_cli, run_runtime_cli, sandbox, schedule, @@ -42,7 +41,6 @@ "local_file", "runtime", "run_auth_cli", - "run_cli", "run_runtime_cli", "sandbox", "schedule", diff --git a/src/ara_sdk/__main__.py b/src/ara_sdk/__main__.py index 7b72084..c7bbfdb 100644 --- a/src/ara_sdk/__main__.py +++ b/src/ara_sdk/__main__.py @@ -5,7 +5,7 @@ import sys from types import ModuleType -from .core import App, run_auth_cli, run_cli, run_runtime_cli +from .core import _run_app_cli, App, run_auth_cli, run_runtime_cli def _print_help(bin_name: str) -> None: @@ -77,7 +77,7 @@ def main() -> None: raise SystemExit(f"Script not found: {script}") module = _load_module(script) app = _discover_app(module) - run_cli(app, argv=[command, *sys.argv[3:]], default_command=command) + _run_app_cli(app, argv=[command, *sys.argv[3:]], default_command=command) if __name__ == "__main__": diff --git a/src/ara_sdk/core.py b/src/ara_sdk/core.py index 4d5681a..cc4ef45 100644 --- a/src/ara_sdk/core.py +++ b/src/ara_sdk/core.py @@ -1009,7 +1009,6 @@ def tool( self, *, id: Optional[str] = None, - description: str = "", parameters: Optional[dict[str, Any]] = None, ) -> Callable[[Callable[..., Any]], Callable[..., Any]]: def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: @@ -1024,7 +1023,7 @@ def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: if not source.startswith("def "): raise ValueError("@app.tool only supports standard def functions") params_schema = dict(parameters) if isinstance(parameters, dict) else _callable_parameters_schema(fn) - tool_description = str(description or fn.__doc__ or "").strip() + tool_description = str(fn.__doc__ or "").strip() item = { "type": "function", "function": { @@ -2791,7 +2790,7 @@ def run_auth_cli(argv: Optional[list[str]] = None) -> None: ) -def run_cli(app: App | dict[str, Any], argv: Optional[list[str]] = None, *, default_command: str = "deploy") -> None: +def _run_app_cli(app: App | dict[str, Any], argv: Optional[list[str]] = None, *, default_command: str = "deploy") -> None: app_obj = app if isinstance(app, App) else None manifest = app_obj.manifest if app_obj is not None else dict(app) diff --git a/tests/test_manifest.py b/tests/test_manifest.py index 98e317e..86e5fb0 100644 --- a/tests/test_manifest.py +++ b/tests/test_manifest.py @@ -118,14 +118,14 @@ def send_email(to: str, subject: str, body: str) -> dict: """Send an email payload.""" return {"ok": True, "to": to, "subject": subject, "body": body} - app.tool(id="send_email", description="Send email via tool.")(send_email) + app.tool(id="send_email")(send_email) manifest = app.manifest tools = manifest["agent"]["tools"] assert tools[0]["type"] == "function" assert tools[0]["function"]["name"] == "send_email" - assert tools[0]["function"]["description"] == "Send email via tool." + assert tools[0]["function"]["description"] == "Send an email payload." assert tools[0]["function"]["parameters"]["properties"]["subject"]["type"] == "string" assert tools[0]["function_name"] == "send_email" assert tools[0]["source"].startswith("def send_email") @@ -136,9 +136,9 @@ def test_tool_supports_multiline_decorator_arguments(): @app.tool( id="send_email", - description="Send an email payload.", ) def send_email(to: str): + """Send an email payload.""" return {"ok": True, "to": to} tools = app.manifest["agent"]["tools"] @@ -818,7 +818,7 @@ def deploy(self, **kwargs): classmethod(lambda cls, *, manifest, cwd=None: stub), ) - core.run_cli( + core._run_app_cli( _manifest_with_runtime(runtime_profile={}), argv=["up", "--warm", "true"], ) @@ -850,7 +850,7 @@ def setup_auth(self, **kwargs): "from_env", classmethod(lambda cls, *, manifest, cwd=None: stub), ) - core.run_cli( + core._run_app_cli( _manifest_with_runtime(runtime_profile={}), argv=["setup-auth", "--x-key-name", "demo-x", "--x-key-rpm", "55", "--ensure-runtime-key", "true"], ) @@ -861,7 +861,7 @@ def setup_auth(self, **kwargs): def test_cli_rejects_unknown_subcommand(capsys): with pytest.raises(SystemExit) as exc: - core.run_cli(_manifest_with_runtime(runtime_profile={}), argv=["not-a-command"]) + core._run_app_cli(_manifest_with_runtime(runtime_profile={}), argv=["not-a-command"]) assert exc.value.code == 2 err = capsys.readouterr().err assert "invalid choice" in err @@ -893,7 +893,7 @@ def logs(self, runtime_key=None, app_header_key=None): classmethod(lambda cls, *, manifest, cwd=None: stub), ) - core.run_cli( + core._run_app_cli( _manifest_with_runtime(runtime_profile={}), argv=["logs"], ) diff --git a/tests/test_standalone_cli.py b/tests/test_standalone_cli.py index feea89a..7efbfde 100644 --- a/tests/test_standalone_cli.py +++ b/tests/test_standalone_cli.py @@ -5,7 +5,7 @@ from ara_sdk import __main__ as sdk_main -def test_standalone_cli_dispatches_command_to_run_cli(tmp_path, monkeypatch): +def test_standalone_cli_dispatches_command_to_app_cli(tmp_path, monkeypatch): script = tmp_path / "app.py" script.write_text( "\n".join( @@ -25,7 +25,7 @@ def _run_cli(app, argv=None, *, default_command="deploy"): captured["argv"] = list(argv or []) captured["default_command"] = default_command - monkeypatch.setattr(sdk_main, "run_cli", _run_cli) + monkeypatch.setattr(sdk_main, "_run_app_cli", _run_cli) monkeypatch.setattr( sdk_main.sys, "argv",