Skip to content

Commit 04e66ad

Browse files
refactor(cli): align chat command with study folder auto-detection
- Remove --study-db option from chat, chat list, chat ask - Use same auto-detection pattern as other commands (sample, network, simulate, etc.) - Study folder resolved via --study global flag or cwd detection - Update docs/commands.md with new interface
1 parent 6157c25 commit 04e66ad

2 files changed

Lines changed: 54 additions & 51 deletions

File tree

docs/commands.md

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -448,17 +448,17 @@ extropy config reset
448448

449449
## extropy chat
450450

451-
Interactive chat with simulated agents.
451+
Interactive chat with simulated agents. Uses the same study folder auto-detection as other commands.
452452

453453
### Interactive REPL
454454

455455
```bash
456-
extropy chat --study-db austin/study.db
456+
extropy chat # auto-detect study, use latest run/first agent
457+
extropy chat --run-id run_123 --agent-id a_42
457458
```
458459

459460
| Flag | Type | Default | Description |
460461
|------|------|---------|-------------|
461-
| `--study-db` | path | required | Study database path |
462462
| `--run-id` | string | latest | Simulation run ID |
463463
| `--agent-id` | string | first agent in run | Agent ID |
464464
| `--session-id` | string | auto | Chat session ID |
@@ -470,33 +470,30 @@ REPL commands: `/context`, `/timeline <n>`, `/history`, `/exit`
470470
Show recent runs and sample agents so users can pick chat targets quickly.
471471

472472
```bash
473-
extropy chat list --study-db austin/study.db
473+
extropy chat list
474+
extropy chat list --limit-runs 5
474475
```
475476

476477
| Flag | Type | Default | Description |
477478
|------|------|---------|-------------|
478-
| `--study-db` | path | required | Study database path |
479479
| `--limit-runs` | int | `10` | Number of recent runs to list |
480480
| `--agents-per-run` | int | `5` | Number of sample agent IDs per run |
481-
| `--json` | flag | false | Output JSON response |
482481

483482
### extropy chat ask
484483

485484
Non-interactive API for automation.
486485

487486
```bash
488-
extropy chat ask --study-db austin/study.db \
489-
--prompt "What changed your mind?" --json
487+
extropy chat ask --prompt "What changed your mind?"
488+
extropy chat ask --run-id r1 --agent-id a1 --prompt "What changed?"
490489
```
491490

492491
| Flag | Type | Default | Description |
493492
|------|------|---------|-------------|
494-
| `--study-db` | path | required | Study database path |
495493
| `--run-id` | string | latest | Simulation run ID |
496494
| `--agent-id` | string | first agent in run | Agent ID |
497495
| `--prompt` | string | required | Question to ask |
498496
| `--session-id` | string | auto | Chat session ID |
499-
| `--json` | flag | false | Output JSON response |
500497

501498
---
502499

extropy/cli/commands/chat.py

Lines changed: 47 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,31 @@
1414

1515
from ...config import get_config
1616
from ...core.llm import simple_call
17-
from ..app import app, console, get_json_mode
17+
from ..app import app, console, get_study_path, is_agent_mode
18+
from ..study import StudyContext, detect_study_folder
19+
from ..utils import Output
1820

1921
chat_app = typer.Typer(help="Chat with simulated agents using DB-backed history")
2022
app.add_typer(chat_app, name="chat")
2123

2224

25+
def _get_study_db() -> Path:
26+
"""Resolve study DB path via auto-detection or --study flag."""
27+
study_path = get_study_path()
28+
detected = detect_study_folder(study_path)
29+
if detected is None:
30+
console.print(
31+
"[red]✗[/red] Not in a study folder. "
32+
"Use --study to specify or run from a study folder."
33+
)
34+
raise typer.Exit(1)
35+
study_ctx = StudyContext(detected)
36+
if not study_ctx.db_path.exists():
37+
console.print(f"[red]✗[/red] Study DB not found: {study_ctx.db_path}")
38+
raise typer.Exit(1)
39+
return study_ctx.db_path
40+
41+
2342
def _now_iso() -> str:
2443
return datetime.now().isoformat()
2544

@@ -383,16 +402,14 @@ def _resolve_run_and_agent(
383402

384403
@chat_app.command("list")
385404
def chat_list(
386-
study_db: Path = typer.Option(..., "--study-db"),
387405
limit_runs: int = typer.Option(10, "--limit-runs", min=1, max=100),
388406
agents_per_run: int = typer.Option(5, "--agents-per-run", min=1, max=25),
389-
json_output: bool = typer.Option(False, "--json"),
390407
):
391408
"""List recent runs with sample agents for quick chat selection."""
392-
if not study_db.exists():
393-
console.print(f"[red]✗[/red] Study DB not found: {study_db}")
394-
raise typer.Exit(1)
409+
agent_mode = is_agent_mode()
410+
out = Output(console, json_mode=agent_mode)
395411

412+
study_db = _get_study_db()
396413
conn = sqlite3.connect(str(study_db))
397414
conn.row_factory = sqlite3.Row
398415
try:
@@ -437,10 +454,11 @@ def chat_list(
437454
finally:
438455
conn.close()
439456

440-
payload = {"study_db": str(study_db), "runs": runs}
441-
if json_output or get_json_mode():
442-
console.print_json(data=payload)
443-
return
457+
out.set_data("study_db", str(study_db))
458+
out.set_data("runs", runs)
459+
460+
if agent_mode:
461+
raise typer.Exit(out.finish())
444462

445463
if not runs:
446464
console.print("[yellow]No simulation runs found.[/yellow]")
@@ -457,27 +475,19 @@ def chat_list(
457475
@chat_app.callback(invoke_without_command=True)
458476
def chat_interactive(
459477
ctx: typer.Context,
460-
study_db: Path | None = typer.Option(None, "--study-db"),
461478
run_id: str | None = typer.Option(None, "--run-id"),
462479
agent_id: str | None = typer.Option(None, "--agent-id"),
463480
session_id: str | None = typer.Option(None, "--session-id"),
464481
):
465482
"""Interactive chat REPL.
466483
467484
Example:
468-
extropy chat --study-db study.db --run-id run_123 --agent-id a_42
485+
extropy chat --run-id run_123 --agent-id a_42
469486
"""
470487
if ctx.invoked_subcommand is not None:
471488
return
472489

473-
if not study_db:
474-
console.print("[red]✗[/red] interactive chat requires --study-db")
475-
raise typer.Exit(1)
476-
477-
if not study_db.exists():
478-
console.print(f"[red]✗[/red] Study DB not found: {study_db}")
479-
raise typer.Exit(1)
480-
490+
study_db = _get_study_db()
481491
conn = sqlite3.connect(str(study_db))
482492
conn.row_factory = sqlite3.Row
483493
try:
@@ -579,22 +589,20 @@ def chat_interactive(
579589

580590
@chat_app.command("ask")
581591
def chat_ask(
582-
study_db: Path = typer.Option(..., "--study-db"),
583592
run_id: str | None = typer.Option(None, "--run-id"),
584593
agent_id: str | None = typer.Option(None, "--agent-id"),
585594
prompt: str = typer.Option(..., "--prompt"),
586595
session_id: str | None = typer.Option(None, "--session-id"),
587-
json_output: bool = typer.Option(False, "--json"),
588596
):
589597
"""Non-interactive chat API for automation.
590598
591599
Example:
592-
extropy chat ask --study-db study.db --run-id r1 --agent-id a1 --prompt "What changed?" --json
600+
extropy chat ask --run-id r1 --agent-id a1 --prompt "What changed?"
593601
"""
594-
if not study_db.exists():
595-
console.print(f"[red]✗[/red] Study DB not found: {study_db}")
596-
raise typer.Exit(1)
602+
agent_mode = is_agent_mode()
603+
out = Output(console, json_mode=agent_mode)
597604

605+
study_db = _get_study_db()
598606
started = time.time()
599607
conn = sqlite3.connect(str(study_db))
600608
conn.row_factory = sqlite3.Row
@@ -639,19 +647,17 @@ def chat_ask(
639647
finally:
640648
conn.close()
641649

642-
payload = {
643-
"session_id": sid,
644-
"run_id": resolved_run_id,
645-
"agent_id": resolved_agent_id,
646-
"user_turn_index": user_turn,
647-
"turn_index": assistant_turn,
648-
"assistant_text": answer,
649-
"citations": {"sources": citations},
650-
"token_usage": {"input_tokens": 0, "output_tokens": 0},
651-
"latency_ms": latency_ms,
652-
}
650+
out.set_data("session_id", sid)
651+
out.set_data("run_id", resolved_run_id)
652+
out.set_data("agent_id", resolved_agent_id)
653+
out.set_data("user_turn_index", user_turn)
654+
out.set_data("turn_index", assistant_turn)
655+
out.set_data("assistant_text", answer)
656+
out.set_data("citations", {"sources": citations})
657+
out.set_data("token_usage", {"input_tokens": 0, "output_tokens": 0})
658+
out.set_data("latency_ms", latency_ms)
653659

654-
if json_output or get_json_mode():
655-
console.print_json(data=payload)
656-
else:
657-
console.print(answer)
660+
if agent_mode:
661+
raise typer.Exit(out.finish())
662+
663+
console.print(answer)

0 commit comments

Comments
 (0)