Skip to content
4 min read · 817 words

Routers — agents.py

The platform's core API: 15 endpoints under /agents covering agent CRUD, chat, email intelligence, task management, workflow orchestration, and dashboard aggregation — plus the shared helpers that implement the observability contract.

Purpose

backend/app/routers/agents.py (~940 lines) is the orchestration layer. It validates requests, gates tools, invokes services, persists results, and logs every automated step. It contains no prompt text and no direct OpenAI calls — those live in services.

Endpoint inventory

Method & pathHandlerSummary
POST /agents/create_agentCreate an agent from AgentCreate
GET /agents/get_agentsList agents, newest first
GET /agents/{id}get_agentFetch one agent (404 if missing)
POST /agents/{id}/chatchat_with_agentOne chat exchange; persists messages + AgentRun
GET /agents/{id}/runsget_agent_runsRun history, newest first
GET /agents/{id}/messagesget_agent_messagesPaginated chat history (limit/offset)
POST /agents/{id}/email/analyzeanalyze_agent_emailSingle-agent analysis + optional task creation
GET /agents/{id}/tasksget_agent_tasksTasks, newest first
PUT /agents/tasks/{task_id}/statusupdate_task_statusStatus transition with allow-list validation
GET /agents/{id}/tool-callsget_agent_tool_callsTool-call log, newest first
POST /agents/{id}/workflows/emailrun_multi_agent_email_workflowThe 4-stage pipeline (deep dive)
GET /agents/{id}/workflow-runsget_agent_workflow_runsWorkflow run history
GET /agents/workflow-runs/{run_id}/stepsget_workflow_run_stepsPer-step trace, oldest first
GET /agents/{id}/dashboardget_agent_dashboardAggregated metrics for the UI
GET /agents/{id}/statsget_agent_stats⚠️ Broken practice code — actually attempts a delete; see Known Issues

Request/response details for each endpoint live in the API Reference.

Shared helpers

These module-level functions encode the conventions every endpoint follows:

agent_has_tool(agent, tool_name) -> bool

Splits agent.enabled_tools on commas, strips whitespace, and tests membership. Called before any tool executes; the endpoint returns 403 when the tool is missing. Canonical names: Tools.

log_tool_call(db, agent_id, tool_name, ...) -> ToolCall

Constructs a ToolCall row (converting success: bool to the stored "true"/"false" string) and db.adds it without committing — the caller owns the transaction, so a tool log and its business data commit atomically.

create_workflow_run(db, agent_id, workflow_name, input_text)

Creates a WorkflowRun with status="running" and commits immediately — so a crash mid-pipeline still leaves a discoverable run to mark failed.

log_workflow_step(...)

Adds a WorkflowStep (no commit), one per pipeline stage.

normalize_due_date(deadline) -> str | None

Deadline sanitizer for task creation:

python
simple_deadlines = ["today", "tomorrow", "next week", "this week"]  # kept as-is
# ISO dates: parsed; past dates → None (don't save stale deadlines)
# anything else: passed through unchanged

safe_float / safe_int

Defensive parsers for the string-typed estimated_cost / quality_score columns, used by the dashboard aggregation.

Orchestration in detail

Chat

Fully traced in Request Lifecycle. Key properties: the LLM call is wrapped in try/except (failures degrade to a fallback message + status="failed" run), latency wraps the whole handler, and messages + run commit in one transaction.

Email analyze (single-agent)

Rendering diagram…

On any exception: a failed ToolCall named email_analyzer_or_task_creator is logged, committed, and 500 raised. Note the single-agent path logs tool calls without cost estimates — only the multi-agent workflow attaches model_name/tokens/cost (Known Issues).

Multi-agent email workflow

The largest handler (~280 lines): creates the run, executes Analysis → Task → Reply → Reviewer with a WorkflowStep + costed ToolCall per stage, finalizes status, quality_score, and a final_output JSON blob. Stage-by-stage documentation: Email Workflow.

Dashboard aggregation

Loads all tasks, tool calls, and workflow runs for the agent and computes in Python: counts by status, success/failure splits, average_latency_ms over non-null latencies, estimated_total_tokens (via safe_int), estimated_total_cost (summed as floats, reformatted to 6 decimals), and average_quality_score over parseable scores. Contract: Observability.

Error handling summary

ConditionResponse
Agent/task/run not found404
Tool not enabled403
Invalid task status400 (allow-list: open, in_progress, completed)
LLM failure during chat200 with fallback text; run marked failed
LLM/other failure in tools & workflows500 with exception text; failed ToolCall committed

Full strategy: Error Handling.

Security notes

No authentication — every endpoint is open (Security). Exception strings are returned verbatim in 500 details, which can leak internals; acceptable for local dev, flagged for hardening.

Future improvements

  • Remove or fix the stats endpoint (it references a nonexistent Agent.agent_id attribute and is a delete in disguise).
  • Attach cost estimates in the single-agent analyze path for parity with the workflow.
  • Extract the workflow handler into a service for testability.