171 lines
5.6 KiB
Markdown
171 lines
5.6 KiB
Markdown
|
J
|
# Agent CLI Simplification — Handoff to ria-toolkit-oss
|
||
|
|
|
||
|
|
**Repo:** `ria-toolkit-oss`, branch `screens-connection`
|
||
|
|
**Goal:** Reduce agent setup from 3+ commands to 2 simple ones.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Current UX (painful)
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Step 1: Register via curl against FastAPI directly
|
||
|
|
curl -X POST http://hub:8005/screens/agents/register \
|
||
|
|
-H 'X-API-Key: supersecretapikey' \
|
||
|
|
-d '{"name": "my-agent"}'
|
||
|
|
# → {"agent_id": "agent-55cf3c5b8137f6f3", "token": "45Hbt..."}
|
||
|
|
|
||
|
|
# Step 2: Manually save credentials
|
||
|
|
ria-agent register \
|
||
|
|
--url http://hub:8005 \
|
||
|
|
--token 45HbtlpVDX7_XTF47biDcLcyiVmM51icEZVJ7J_UrEE \
|
||
|
|
--agent-id agent-55cf3c5b8137f6f3
|
||
|
|
|
||
|
|
# Step 3: Stream (with manual URL construction)
|
||
|
|
ria-agent stream \
|
||
|
|
--url "ws://hub:8005/screens/agent/ws?agent_id=agent-55cf3c5b8137f6f3" \
|
||
|
|
--token 45HbtlpVDX7_XTF47biDcLcyiVmM51icEZVJ7J_UrEE
|
||
|
|
```
|
||
|
|
|
||
|
|
Problems:
|
||
|
|
- User must know the FastAPI port (8005), not just the hub URL (3005)
|
||
|
|
- `register` subcommand only saves locally — doesn't call the server
|
||
|
|
- `_derive_ws_url` builds `/api/agent/ws/{agent_id}` but server endpoint is `/screens/agent/ws?agent_id=...`
|
||
|
|
- User must copy-paste agent_id and token between commands
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Target UX
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# One-time setup: register with the hub (hits server, saves config)
|
||
|
|
ria-agent register --hub http://whitehorse:3005 --api-key supersecretapikey --name lab-pluto
|
||
|
|
|
||
|
|
# Stream (reads config, connects automatically)
|
||
|
|
ria-agent stream
|
||
|
|
```
|
||
|
|
|
||
|
|
That's it. Two commands, no copy-pasting.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Changes needed in ria-toolkit-oss
|
||
|
|
|
||
|
|
### 1. `cli.py` — Make `register` call the server
|
||
|
|
|
||
|
|
Current `_cmd_register` just saves to `~/.ria/agent.json`. It should:
|
||
|
|
|
||
|
|
1. POST to `{hub_url}/screens/agents/register` with `X-API-Key` header
|
||
|
|
2. Receive `{agent_id, token}` from the server
|
||
|
|
3. Save everything to `~/.ria/agent.json`
|
||
|
|
|
||
|
|
```python
|
||
|
|
def _cmd_register(args: argparse.Namespace) -> int:
|
||
|
|
import urllib.request
|
||
|
|
import json as _json
|
||
|
|
|
||
|
|
hub_url = args.hub.rstrip("/")
|
||
|
|
api_key = args.api_key
|
||
|
|
|
||
|
|
# Call the server to register
|
||
|
|
url = f"{hub_url}/screens/agents/register"
|
||
|
|
body = _json.dumps({"name": args.name or ""}).encode()
|
||
|
|
req = urllib.request.Request(
|
||
|
|
url,
|
||
|
|
data=body,
|
||
|
|
headers={
|
||
|
|
"Content-Type": "application/json",
|
||
|
|
"X-API-Key": api_key,
|
||
|
|
},
|
||
|
|
)
|
||
|
|
try:
|
||
|
|
with urllib.request.urlopen(req) as resp:
|
||
|
|
data = _json.loads(resp.read())
|
||
|
|
except Exception as e:
|
||
|
|
print(f"error: registration failed: {e}", file=sys.stderr)
|
||
|
|
return 1
|
||
|
|
|
||
|
|
agent_id = data["agent_id"]
|
||
|
|
token = data["token"]
|
||
|
|
|
||
|
|
# Save to config
|
||
|
|
cfg = _config.load()
|
||
|
|
cfg.hub_url = hub_url
|
||
|
|
cfg.agent_id = agent_id
|
||
|
|
cfg.token = token
|
||
|
|
if args.name:
|
||
|
|
cfg.name = args.name
|
||
|
|
cfg.insecure = bool(args.insecure)
|
||
|
|
path = _config.save(cfg)
|
||
|
|
|
||
|
|
print(f"Registered agent: {agent_id}")
|
||
|
|
print(f"Credentials saved to {path}")
|
||
|
|
return 0
|
||
|
|
```
|
||
|
|
|
||
|
|
Update the argparse for `register`:
|
||
|
|
```python
|
||
|
|
p_reg = sub.add_parser("register", help="Register agent with RIA Hub and save credentials")
|
||
|
|
p_reg.add_argument("--hub", required=True, help="RIA Hub URL (e.g. http://whitehorse:3005)")
|
||
|
|
p_reg.add_argument("--api-key", required=True, help="Hub API key for authentication")
|
||
|
|
p_reg.add_argument("--name", default=None, help="Human-friendly agent name")
|
||
|
|
p_reg.add_argument("--insecure", action="store_true", help="Skip TLS verification")
|
||
|
|
```
|
||
|
|
|
||
|
|
Remove `--url`, `--token`, `--agent-id` from register — those are now server-generated.
|
||
|
|
|
||
|
|
### 2. `cli.py` — Fix `_derive_ws_url`
|
||
|
|
|
||
|
|
Current (wrong):
|
||
|
|
```python
|
||
|
|
suffix = f"/api/agent/ws/{agent_id}" if agent_id else "/api/agent/ws"
|
||
|
|
```
|
||
|
|
|
||
|
|
Should be:
|
||
|
|
```python
|
||
|
|
suffix = f"/screens/agent/ws?agent_id={agent_id}" if agent_id else "/screens/agent/ws"
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3. `cli.py` — Make `stream` zero-arg by default
|
||
|
|
|
||
|
|
Current `_cmd_stream` already loads config and derives the URL — it just needs the URL fix above. After that, bare `ria-agent stream` works if `register` was run first.
|
||
|
|
|
||
|
|
### 4. `config.py` — Add `api_key` field (optional)
|
||
|
|
|
||
|
|
Add `api_key: str = ""` to `AgentConfig` so the hub API key can be persisted for re-registration or other API calls. Not strictly required but useful.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Changes already done in ria-hub (Part B)
|
||
|
|
|
||
|
|
The server side is ready:
|
||
|
|
|
||
|
|
- `POST /screens/agents/register` — accepts `{"name": "..."}` with `X-API-Key` header, returns `{"agent_id": "...", "token": "..."}`
|
||
|
|
- `GET /screens/agent/ws?agent_id=...` — WebSocket endpoint, authenticates via `Authorization: Bearer {token}` header
|
||
|
|
- Agent token is hashed (SHA-256) and stored in MongoDB; lookup happens on WS connect
|
||
|
|
|
||
|
|
The Go proxy for `/screens/agents/register` through port 3005 still needs to be added (currently agents must hit FastAPI port 8005 directly). That's a ria-hub task, not ria-toolkit-oss.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Summary of file changes
|
||
|
|
|
||
|
|
| File | Change |
|
||
|
|
|------|--------|
|
||
|
|
| `src/ria_toolkit_oss/agent/cli.py` | `register` calls server API, new flags `--hub`/`--api-key`; fix `_derive_ws_url` path |
|
||
|
|
| `src/ria_toolkit_oss/agent/config.py` | Optional: add `api_key` field to `AgentConfig` |
|
||
|
|
| `tests/agent/test_cli.py` | Update register tests for new server-calling behavior |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Validated E2E flow (what works today)
|
||
|
|
|
||
|
|
We tested the full pipeline on whitehorse with a real Pluto SDR:
|
||
|
|
|
||
|
|
1. Agent connects via WebSocket with bearer token auth ✅
|
||
|
|
2. Server sends `start` with `radio_config` via Redis pub/sub → agent ✅
|
||
|
|
3. Agent opens Pluto, streams interleaved float32 IQ via binary WS frames ✅
|
||
|
|
4. FastAPI pushes frames to Redis list, Celery worker's `AgentDataSource.next_chunk()` BLPOP reads them ✅
|
||
|
|
5. Inference loop runs on live agent data identically to direct SDR mode ✅
|
||
|
|
|
||
|
|
The only manual friction is the multi-step registration and URL construction — which these CLI changes eliminate.
|