Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 0 additions & 1 deletion scripts/observability/local_observe.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,3 @@ Then run `iac-code` normally.
- Pipeline-level evidence is split into lifecycle, normal chat after pipeline, and other session evidence.
- Use **Clear** before a new manual test.
- Use **Export JSONL** to save the captured records.

2 changes: 1 addition & 1 deletion src/iac_code/a2a/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -517,7 +517,7 @@ def run_server(
try:
import uvicorn
except ImportError as exc:
raise RuntimeError("A2A server dependencies are missing. Install iac-code with the 'a2a' extra.") from exc
raise RuntimeError("A2A server dependencies are missing. Install with: pip install 'iac-code[a2a]'") from exc

uvicorn.run(
app,
Expand Down
19 changes: 14 additions & 5 deletions src/iac_code/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,15 @@
)
app.add_typer(a2a_client_app, name="a2a-client")


def _a2a_client_missing_dependencies_message() -> str:
return _("A2A client dependencies are missing. Install with: pip install 'iac-code[a2a]'")


def _a2a_server_missing_dependencies_message() -> str:
return _("A2A server dependencies are missing. Install with: pip install 'iac-code[a2a]'")


# `install-git-bash` is a Windows-only helper that installs Git for Windows
# via the npmmirror mirror. We register it conditionally so it does not
# show up in --help on non-Windows platforms (where it could not work).
Expand All @@ -71,7 +80,7 @@ def a2a_client(
import iac_code.a2a.client # noqa: F401
except ImportError:
typer.echo(
_("A2A client dependencies are missing. Install with: pip install 'iac-code[a2a]'"),
_a2a_client_missing_dependencies_message(),
err=True,
)
raise typer.Exit(1)
Expand Down Expand Up @@ -658,7 +667,7 @@ def a2a(
)
except ImportError as exc:
typer.echo(
_("A2A server dependencies are missing. Install with: pip install 'iac-code[a2a]'"),
_a2a_server_missing_dependencies_message(),
err=True,
)
raise typer.Exit(1) from exc
Expand Down Expand Up @@ -1513,7 +1522,7 @@ def _build_a2a_auth_config(
from iac_code.a2a.transport import A2AAuthConfig
except ImportError as exc:
typer.echo(
_("A2A client dependencies are missing. Install with: pip install 'iac-code[a2a]'"),
_a2a_client_missing_dependencies_message(),
err=True,
)
raise typer.Exit(1) from exc
Expand Down Expand Up @@ -1874,7 +1883,7 @@ def _parse_a2a_route_spec(value: str):
from iac_code.a2a.router import A2ARoute
except ImportError as exc:
typer.echo(
_("A2A client dependencies are missing. Install with: pip install 'iac-code[a2a]'"),
_a2a_client_missing_dependencies_message(),
err=True,
)
raise typer.Exit(1) from exc
Expand Down Expand Up @@ -1913,7 +1922,7 @@ def _save_a2a_route_snapshots(persistence_dir: str, routes: list[Any]) -> None:
from iac_code.a2a.persistence import A2APersistenceStore, A2ARouteSnapshot
except ImportError as exc:
typer.echo(
_("A2A client dependencies are missing. Install with: pip install 'iac-code[a2a]'"),
_a2a_client_missing_dependencies_message(),
err=True,
)
raise typer.Exit(1) from exc
Expand Down
30 changes: 30 additions & 0 deletions tests/a2a/test_app.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import asyncio
import builtins
import json
from base64 import b64encode
from pathlib import Path
Expand Down Expand Up @@ -30,6 +31,7 @@
resolve_api_key,
resolve_basic_credentials,
resolve_token,
run_server,
)
from iac_code.a2a.persistence import A2AContextSnapshot, A2APersistenceStore, A2ATaskSnapshot
from iac_code.a2a.pipeline_journal import A2APipelineJournal
Expand Down Expand Up @@ -94,6 +96,34 @@ def test_health_route() -> None:
assert response.json() == {"status": "healthy"}


def test_run_server_reports_aligned_missing_uvicorn_hint(monkeypatch, tmp_path) -> None:
real_import = builtins.__import__

def fake_import(name, *args, **kwargs):
if name == "uvicorn":
raise ImportError("No module named 'uvicorn'")
return real_import(name, *args, **kwargs)

monkeypatch.setattr(builtins, "__import__", fake_import)
monkeypatch.setattr("iac_code.a2a.app.create_app", lambda **kwargs: object())

with pytest.raises(
RuntimeError,
match=r"A2A server dependencies are missing\. Install with: pip install 'iac-code\[a2a\]'",
):
run_server(
host="127.0.0.1",
port=41242,
token=None,
model="qwen3.6-plus",
basic_username=None,
basic_password=None,
api_key=None,
api_key_header="X-API-Key",
persistence_dir=tmp_path / "a2a",
)


def test_pipeline_state_endpoint_requires_context_id(tmp_path) -> None:
app = create_app(
host="127.0.0.1",
Expand Down
41 changes: 33 additions & 8 deletions tests/cli/test_a2a_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@

from iac_code.a2a.persistence import A2APersistenceStore, A2ARouteSnapshot
from iac_code.a2a.transport import A2AAuthConfig
from iac_code.cli.main import app
from iac_code.cli.main import (
_a2a_client_missing_dependencies_message,
_a2a_server_missing_dependencies_message,
app,
)
from iac_code.config import DEFAULT_MODEL

_ANSI_ESCAPE_RE = re.compile(r"\x1b\[[0-9;]*[A-Za-z]")
Expand Down Expand Up @@ -51,13 +55,15 @@ def test_a2a_client_help_groups_client_commands() -> None:
assert "push-config-create" in stdout
assert "extended-card" in stdout
assert "route-preview" in stdout
assert "a2a-route-preview" not in stdout


def test_removed_top_level_a2a_client_command_is_rejected() -> None:
result = CliRunner().invoke(app, ["a2a-call", "--help"])
def test_removed_top_level_a2a_client_commands_are_rejected() -> None:
for command in ("a2a-call", "a2a-route-preview"):
result = CliRunner().invoke(app, [command, "--help"])

assert result.exit_code == 2
assert "No such command" in result.stderr
assert result.exit_code == 2
assert "No such command" in result.stderr


def test_a2a_command_passes_config_options_to_server(monkeypatch, tmp_path) -> None:
Expand Down Expand Up @@ -972,7 +978,7 @@ def test_a2a_route_preview_resolves_and_saves_routes(tmp_path) -> None:

def test_a2a_command_reports_missing_extra(monkeypatch) -> None:
def fake_run_server(**kwargs) -> None:
raise RuntimeError("A2A server dependencies are missing. Install iac-code with the 'a2a' extra.")
raise RuntimeError(_a2a_server_missing_dependencies_message())

monkeypatch.setattr("iac_code.a2a.app.run_server", fake_run_server)
monkeypatch.setattr("iac_code.a2a.app.resolve_token", lambda token: None)
Expand All @@ -983,7 +989,7 @@ def fake_run_server(**kwargs) -> None:

assert result.exit_code == 1
combined_output = (result.stdout or "") + (result.stderr or "") + (result.output or "")
assert "a2a" in combined_output
assert _a2a_server_missing_dependencies_message() in combined_output


def test_a2a_command_reports_import_error(monkeypatch) -> None:
Expand All @@ -1002,7 +1008,26 @@ def fake_import(name, *args, **kwargs):

assert result.exit_code == 1
combined_output = (result.stdout or "") + (result.stderr or "") + (result.output or "")
assert "a2a" in combined_output
assert _a2a_server_missing_dependencies_message() in combined_output


def test_a2a_client_command_reports_import_error(monkeypatch) -> None:
import builtins

real_import = builtins.__import__

def fake_import(name, *args, **kwargs):
if name == "iac_code.a2a.client":
raise ImportError("missing optional a2a dependency")
return real_import(name, *args, **kwargs)

monkeypatch.setattr(builtins, "__import__", fake_import)

result = CliRunner().invoke(app, ["a2a-client", "discover", "--url", "http://agent.example/rpc"])

assert result.exit_code == 1
combined_output = (result.stdout or "") + (result.stderr or "") + (result.output or "")
assert _a2a_client_missing_dependencies_message() in combined_output


def test_a2a_command_bootstraps_telemetry_around_run_server(monkeypatch) -> None:
Expand Down
8 changes: 4 additions & 4 deletions website/docs/a2a/command-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ This page documents every A2A-related `iac-code` command. Use it when you need e
| `iac-code a2a-client push-config-list` | List task push notification configs |
| `iac-code a2a-client push-config-delete` | Delete a task push notification config |
| `iac-code a2a-client extended-card` | Fetch the authenticated extended Agent Card |
| `iac-code a2a-route-preview` | Preview local route selection for `a2a-client call` |
| `iac-code a2a-client route-preview` | Preview local route selection for `a2a-client call` |

All HTTP client commands accept the same authentication options:

Expand Down Expand Up @@ -465,12 +465,12 @@ iac-code a2a-client --config a2a-client.yml extended-card \

The public Agent Card advertises `capabilities.extendedAgentCard=true`. The extended card adds authenticated runtime details, including task management and push configuration capability metadata.

## `iac-code a2a-route-preview`
## `iac-code a2a-client route-preview`

Preview how `a2a-client call` resolves configured routes when `--url` is omitted.

```bash
iac-code a2a-route-preview \
iac-code a2a-client route-preview \
--route "template=http://127.0.0.1:41242/;skills=iac_generation;tags=ros,template" \
--skill iac_generation \
--prompt "Create a ROS VPC template"
Expand All @@ -488,7 +488,7 @@ iac-code a2a-route-preview \
Save route snapshots:

```bash
iac-code a2a-route-preview \
iac-code a2a-client route-preview \
--route "ros=http://127.0.0.1:41242/;skills=iac_generation;tags=ros" \
--route-state-dir ~/.iac-code/a2a \
--save-routes
Expand Down
2 changes: 1 addition & 1 deletion website/docs/a2a/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ iac-code a2a-client --config a2a-client.yml push-config-create \
Preview route selection before calling a routed agent:

```bash
iac-code a2a-route-preview \
iac-code a2a-client route-preview \
--route "ros=http://127.0.0.1:41242/;skills=iac_generation;tags=ros,template" \
--skill iac_generation \
--prompt "Create a ROS VPC template"
Expand Down
2 changes: 1 addition & 1 deletion website/docs/a2a/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ iac-code a2a-client --config a2a-client.yml call \
For multi-agent routing, preview route selection before calling:

```bash
iac-code a2a-route-preview \
iac-code a2a-client route-preview \
--route "template=http://127.0.0.1:41242/;skills=iac_generation;tags=ros,template" \
--skill iac_generation \
--route-state-dir ~/.iac-code/a2a
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Diese Seite dokumentiert jeden A2A-bezogenen `iac-code`-Befehl. Verwenden Sie si
| `iac-code a2a-client push-config-list` | Task-Push-Notification-Configs auflisten |
| `iac-code a2a-client push-config-delete` | Eine Task-Push-Notification-Config loeschen |
| `iac-code a2a-client extended-card` | Die authentifizierte erweiterte Agent Card abrufen |
| `iac-code a2a-route-preview` | Lokale Routenauswahl fuer `a2a-client call` voranzeigen |
| `iac-code a2a-client route-preview` | Lokale Routenauswahl fuer `a2a-client call` voranzeigen |

Alle HTTP-Clientbefehle akzeptieren dieselben Authentifizierungsoptionen:

Expand Down Expand Up @@ -465,12 +465,12 @@ iac-code a2a-client --config a2a-client.yml extended-card \

Die oeffentliche Agent Card bewirbt `capabilities.extendedAgentCard=true`. Die erweiterte Karte fuegt authentifizierte Laufzeitdetails hinzu, einschliesslich Task-Verwaltung und Push-Konfigurationsfaehigkeitsmetadaten.

## `iac-code a2a-route-preview`
## `iac-code a2a-client route-preview`

Vorschau, wie `a2a-client call` konfigurierte Routen aufloest, wenn `--url` ausgelassen ist.

```bash
iac-code a2a-route-preview \
iac-code a2a-client route-preview \
--route "template=http://127.0.0.1:41242/;skills=iac_generation;tags=ros,template" \
--skill iac_generation \
--prompt "Create a ROS VPC template"
Expand All @@ -488,7 +488,7 @@ iac-code a2a-route-preview \
Routen-Snapshots speichern:

```bash
iac-code a2a-route-preview \
iac-code a2a-client route-preview \
--route "ros=http://127.0.0.1:41242/;skills=iac_generation;tags=ros" \
--route-state-dir ~/.iac-code/a2a \
--save-routes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ iac-code a2a-client --config a2a-client.yml push-config-create \
Zeigen Sie vor dem Aufrufen eines gerouteten Agent die Routenauswahl in der Vorschau an:

```bash
iac-code a2a-route-preview \
iac-code a2a-client route-preview \
--route "ros=http://127.0.0.1:41242/;skills=iac_generation;tags=ros,template" \
--skill iac_generation \
--prompt "Create a ROS VPC template"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ iac-code a2a-client --config a2a-client.yml call \
Zeigen Sie bei Multi-Agent-Routing die Routenauswahl vor dem Aufruf in der Vorschau an:

```bash
iac-code a2a-route-preview \
iac-code a2a-client route-preview \
--route "template=http://127.0.0.1:41242/;skills=iac_generation;tags=ros,template" \
--skill iac_generation \
--route-state-dir ~/.iac-code/a2a
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Esta página documenta todos los comandos de `iac-code` relacionados con A2A. Ú
| `iac-code a2a-client push-config-list` | Listar configuraciones de notificación push de tarea |
| `iac-code a2a-client push-config-delete` | Eliminar una configuración de notificación push de tarea |
| `iac-code a2a-client extended-card` | Obtener la Agent Card extendida autenticada |
| `iac-code a2a-route-preview` | Previsualizar la selección local de ruta para `a2a-client call` |
| `iac-code a2a-client route-preview` | Previsualizar la selección local de ruta para `a2a-client call` |

Todos los comandos de cliente HTTP aceptan las mismas opciones de autenticación:

Expand Down Expand Up @@ -465,12 +465,12 @@ iac-code a2a-client --config a2a-client.yml extended-card \

La Agent Card pública anuncia `capabilities.extendedAgentCard=true`. La tarjeta extendida agrega detalles autenticados del runtime, incluidos metadatos de capacidades de gestión de tareas y configuración push.

## `iac-code a2a-route-preview`
## `iac-code a2a-client route-preview`

Previsualiza cómo `a2a-client call` resuelve rutas configuradas cuando `--url` se omite.

```bash
iac-code a2a-route-preview \
iac-code a2a-client route-preview \
--route "template=http://127.0.0.1:41242/;skills=iac_generation;tags=ros,template" \
--skill iac_generation \
--prompt "Create a ROS VPC template"
Expand All @@ -488,7 +488,7 @@ iac-code a2a-route-preview \
Guardar instantáneas de rutas:

```bash
iac-code a2a-route-preview \
iac-code a2a-client route-preview \
--route "ros=http://127.0.0.1:41242/;skills=iac_generation;tags=ros" \
--route-state-dir ~/.iac-code/a2a \
--save-routes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ iac-code a2a-client --config a2a-client.yml push-config-create \
Previsualiza la selección de ruta antes de llamar a un agente enrutado:

```bash
iac-code a2a-route-preview \
iac-code a2a-client route-preview \
--route "ros=http://127.0.0.1:41242/;skills=iac_generation;tags=ros,template" \
--skill iac_generation \
--prompt "Create a ROS VPC template"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ iac-code a2a-client --config a2a-client.yml call \
Para enrutamiento multiagente, previsualiza la selección de ruta antes de llamar:

```bash
iac-code a2a-route-preview \
iac-code a2a-client route-preview \
--route "template=http://127.0.0.1:41242/;skills=iac_generation;tags=ros,template" \
--skill iac_generation \
--route-state-dir ~/.iac-code/a2a
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Cette page documente chaque commande `iac-code` liée à A2A. Utilisez-la lorsqu
| `iac-code a2a-client push-config-list` | Lister les configurations de notification push de tâche |
| `iac-code a2a-client push-config-delete` | Supprimer une configuration de notification push de tâche |
| `iac-code a2a-client extended-card` | Récupérer l'Agent Card étendue authentifiée |
| `iac-code a2a-route-preview` | Prévisualiser la sélection de route locale pour `a2a-client call` |
| `iac-code a2a-client route-preview` | Prévisualiser la sélection de route locale pour `a2a-client call` |

Toutes les commandes client HTTP acceptent les mêmes options d'authentification :

Expand Down Expand Up @@ -465,12 +465,12 @@ iac-code a2a-client --config a2a-client.yml extended-card \

L'Agent Card publique annonce `capabilities.extendedAgentCard=true`. La carte étendue ajoute des détails runtime authentifiés, y compris les métadonnées de capacités de gestion des tâches et de configuration push.

## `iac-code a2a-route-preview`
## `iac-code a2a-client route-preview`

Prévisualise la manière dont `a2a-client call` résout les routes configurées lorsque `--url` est omis.

```bash
iac-code a2a-route-preview \
iac-code a2a-client route-preview \
--route "template=http://127.0.0.1:41242/;skills=iac_generation;tags=ros,template" \
--skill iac_generation \
--prompt "Create a ROS VPC template"
Expand All @@ -488,7 +488,7 @@ iac-code a2a-route-preview \
Enregistrer les instantanés de route :

```bash
iac-code a2a-route-preview \
iac-code a2a-client route-preview \
--route "ros=http://127.0.0.1:41242/;skills=iac_generation;tags=ros" \
--route-state-dir ~/.iac-code/a2a \
--save-routes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ iac-code a2a-client --config a2a-client.yml push-config-create \
Prévisualisez la sélection de route avant d'appeler un agent routé :

```bash
iac-code a2a-route-preview \
iac-code a2a-client route-preview \
--route "ros=http://127.0.0.1:41242/;skills=iac_generation;tags=ros,template" \
--skill iac_generation \
--prompt "Create a ROS VPC template"
Expand Down
Loading
Loading