Skip to content

Commit bb6343b

Browse files
authored
Adds endpoint to get an agent run's data (#1197)
1 parent 2bf8b6d commit bb6343b

File tree

3 files changed

+86
-27
lines changed

3 files changed

+86
-27
lines changed

src/codegen/cli/commands/agent/main.py

Lines changed: 82 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
"""Agent command for creating remote agent runs."""
22

3+
import json
4+
35
import requests
46
import typer
57
from rich import box
68
from rich.console import Console
79
from rich.panel import Panel
10+
from rich.syntax import Syntax
811

912
from codegen.cli.api.endpoints import API_ENDPOINT
10-
from codegen.cli.auth.token_manager import get_current_token
13+
from codegen.cli.auth.token_manager import get_current_org_name, get_current_token
1114
from codegen.cli.rich.spinners import create_spinner
1215
from codegen.cli.utils.org import resolve_org_id
1316

@@ -141,19 +144,91 @@ def agent_callback(ctx: typer.Context):
141144
raise typer.Exit()
142145

143146

144-
# For backward compatibility, also allow `codegen agent --prompt "..."`
147+
# For backward compatibility, also allow `codegen agent --prompt "..."` and `codegen agent --id X --json`
145148
def agent(
146-
prompt: str = typer.Option(None, "--prompt", "-p", help="The prompt to send to the agent"),
149+
prompt: str | None = typer.Option(None, "--prompt", "-p", help="The prompt to send to the agent"),
150+
agent_id: int | None = typer.Option(None, "--id", help="Agent run ID to fetch"),
151+
as_json: bool = typer.Option(False, "--json", help="Output raw JSON response"),
147152
org_id: int | None = typer.Option(None, help="Organization ID (defaults to CODEGEN_ORG_ID/REPOSITORY_ORG_ID or auto-detect)"),
148153
model: str | None = typer.Option(None, help="Model to use for this agent run (optional)"),
149154
repo_id: int | None = typer.Option(None, help="Repository ID to use for this agent run (optional)"),
150155
):
151-
"""Create a new agent run with the given prompt."""
156+
"""Create a new agent run with the given prompt, or fetch an existing agent run by ID."""
152157
if prompt:
153158
# If prompt is provided, create the agent run
154159
create(prompt=prompt, org_id=org_id, model=model, repo_id=repo_id)
160+
elif agent_id:
161+
# If agent ID is provided, fetch the agent run
162+
get(agent_id=agent_id, as_json=as_json, org_id=org_id)
155163
else:
156-
# If no prompt, show help
157-
console.print("[red]Error:[/red] --prompt is required")
158-
console.print("Usage: [cyan]codegen agent --prompt 'Your prompt here'[/cyan]")
164+
# If neither prompt nor agent_id, show help
165+
console.print("[red]Error:[/red] Either --prompt or --id is required")
166+
console.print("Usage:")
167+
console.print(" [cyan]codegen agent --prompt 'Your prompt here'[/cyan] # Create agent run")
168+
console.print(" [cyan]codegen agent --id 123 --json[/cyan] # Fetch agent run as JSON")
169+
raise typer.Exit(1)
170+
171+
172+
@agent_app.command()
173+
def get(
174+
agent_id: int = typer.Option(..., "--id", help="Agent run ID to fetch"),
175+
as_json: bool = typer.Option(False, "--json", help="Output raw JSON response"),
176+
org_id: int | None = typer.Option(None, help="Organization ID (defaults to CODEGEN_ORG_ID/REPOSITORY_ORG_ID or auto-detect)"),
177+
):
178+
"""Fetch and display details for a specific agent run."""
179+
# Get the current token
180+
token = get_current_token()
181+
if not token:
182+
console.print("[red]Error:[/red] Not authenticated. Please run 'codegen login' first.")
183+
raise typer.Exit(1)
184+
185+
try:
186+
# Resolve org id (fast, uses stored data)
187+
resolved_org_id = resolve_org_id(org_id)
188+
if resolved_org_id is None:
189+
console.print("[red]Error:[/red] Organization ID not provided. Pass --org-id, set CODEGEN_ORG_ID, or REPOSITORY_ORG_ID.")
190+
raise typer.Exit(1)
191+
192+
spinner = create_spinner(f"Fetching agent run {agent_id}...")
193+
spinner.start()
194+
195+
try:
196+
headers = {"Authorization": f"Bearer {token}"}
197+
# Fixed: Use /agent/run/{id} not /agent/runs/{id}
198+
url = f"{API_ENDPOINT.rstrip('/')}/v1/organizations/{resolved_org_id}/agent/run/{agent_id}"
199+
response = requests.get(url, headers=headers)
200+
response.raise_for_status()
201+
agent_data = response.json()
202+
finally:
203+
spinner.stop()
204+
205+
# Output the data
206+
if as_json:
207+
# Pretty print JSON with syntax highlighting
208+
formatted_json = json.dumps(agent_data, indent=2, sort_keys=True)
209+
syntax = Syntax(formatted_json, "json", theme="monokai", line_numbers=False)
210+
console.print(syntax)
211+
else:
212+
# Display formatted information (fallback for future enhancement)
213+
formatted_json = json.dumps(agent_data, indent=2, sort_keys=True)
214+
syntax = Syntax(formatted_json, "json", theme="monokai", line_numbers=False)
215+
console.print(syntax)
216+
217+
except requests.HTTPError as e:
218+
# Get organization name for better error messages
219+
org_name = get_current_org_name()
220+
org_display = f"{org_name} ({resolved_org_id})" if org_name else f"organization {resolved_org_id}"
221+
222+
if e.response.status_code == 404:
223+
console.print(f"[red]Error:[/red] Agent run {agent_id} not found in {org_display}.")
224+
elif e.response.status_code == 403:
225+
console.print(f"[red]Error:[/red] Access denied to agent run {agent_id} in {org_display}. Check your permissions.")
226+
else:
227+
console.print(f"[red]Error:[/red] HTTP {e.response.status_code}: {e}")
228+
raise typer.Exit(1)
229+
except requests.RequestException as e:
230+
console.print(f"[red]Error fetching agent run:[/red] {e}")
231+
raise typer.Exit(1)
232+
except Exception as e:
233+
console.print(f"[red]Unexpected error:[/red] {e}")
159234
raise typer.Exit(1)

src/codegen/cli/commands/agents/main.py

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -90,16 +90,8 @@ def list_agents(org_id: int | None = typer.Option(None, help="Organization ID (d
9090
source_type = agent_run.get("source_type", "Unknown")
9191
created_at = agent_run.get("created_at", "Unknown")
9292

93-
# Extract summary from task_timeline_json, similar to frontend
94-
timeline = agent_run.get("task_timeline_json")
95-
summary = None
96-
if timeline and isinstance(timeline, dict) and "summary" in timeline:
97-
if isinstance(timeline["summary"], str):
98-
summary = timeline["summary"]
99-
100-
# Fall back to goal_prompt if no summary
101-
if not summary:
102-
summary = agent_run.get("goal_prompt", "")
93+
# Use summary from API response (backend now handles extraction)
94+
summary = agent_run.get("summary", "") or "No summary"
10395

10496
# Status with colored circles
10597
if status == "COMPLETE":

src/codegen/cli/tui/app.py

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -96,16 +96,8 @@ async def _load_agents_data(self) -> None:
9696
status = agent_run.get("status", "Unknown")
9797
created_at = agent_run.get("created_at", "Unknown")
9898

99-
# Extract summary from task_timeline_json, similar to frontend
100-
timeline = agent_run.get("task_timeline_json")
101-
summary = None
102-
if timeline and isinstance(timeline, dict) and "summary" in timeline:
103-
if isinstance(timeline["summary"], str):
104-
summary = timeline["summary"]
105-
106-
# Fall back to goal_prompt if no summary
107-
if not summary:
108-
summary = agent_run.get("goal_prompt", "")
99+
# Use summary from API response (backend now handles extraction)
100+
summary = agent_run.get("summary", "") or "No summary"
109101

110102
# Status with colored circles
111103
if status == "COMPLETE":

0 commit comments

Comments
 (0)