Files
ss-tools/specs/033-gradio-agent-chat/research.md
2026-06-09 11:44:20 +03:00

3.2 KiB

#region AgentChat.Research [C:3] [TYPE ADR] [SEMANTICS research,agent-chat,langgraph,gradio] @BRIEF Research — LangGraph agent with interrupt/resume, PostgresSaver, structured metadata. Final architecture choices.

1. Agent Framework: LangGraph create_react_agent()

Decision: create_react_agent(model, tools, checkpointer=PostgresSaver) from langgraph.prebuilt. Uses interrupt() for HITL, Command(resume=...) for resume. Rationale: Native HITL support, PostgresSaver checkpointer, astream_events() for structured streaming. Rejected: LangChain AgentExecutor — no native interrupt/checkpointer, more boilerplate.

2. Tools: Native @tool

Decision: @tool-decorated functions with Pydantic args_schema. Each tool calls FastAPI REST via HTTP with user JWT. Rationale: Single source of metadata. Auto OpenAI function schema. Old @assistant_tool@DEPRECATED. Rejected: Dual registration (@assistant_tool + StructuredTool) — redundant.

3. Confirmation: interrupt_before=DANGEROUS_TOOLS

Decision: LangGraph native interrupt_before=DANGEROUS_TOOLS + Command(resume=...). No custom HumanInTheLoopMiddleware — LangGraph provides HITL natively via checkpointing and resume. Second submit() with additional_inputs[1]="confirm"/"deny" triggers resume. Rationale: Zero REST endpoints. LangGraph checkpoint ensures safe resume. Rejected: REST confirmation endpoints + polling — more code, more latency. Custom middleware — LangGraph has native interrupt support.

4. History: RunnableWithMessageHistory

Decision: LangChain auto-loads/saves message history. Svelte does NOT pass history — only conversation_id. Rationale: Eliminates manual history management on frontend and backend. Rejected: Manual history parameter in submit() — fragile.

5. Checkpoints: PostgresSaver

Decision: PostgreSQL via langgraph-checkpoint-postgres. Same instance as FastAPI. Survives all container restarts. Thread ID = conversation_id. Rationale: Agent state persists across container restarts. Rejected: In-memory only — lost on restart.

6. Gradio Native Features

Decision: additional_inputs, gr.Request, concurrency_limit=1, examples. All used. Rationale: Eliminates custom code for conversation_id passing, JWT extraction, message queuing, welcome chips. Rejected: Custom headers, manual queues — redundant.

7. Tool Execution: HTTP to FastAPI

Decision: All @tool functions call FastAPI REST with user JWT (not service JWT). Rationale: RBAC enforced by FastAPI under user identity. Service JWT only for bootstrap. Rejected: Direct import — Gradio has no DB connection.

8. Streaming: Structured JSON Metadata

Decision: LangGraph events yield ChatMessage objects with metadata.type (stream_token, tool_start, tool_end, tool_error, confirm_required, confirm_resolved, error). Frontend parses structured JSON from event.data, not emoji strings. Rationale: Structured metadata is deterministic, typed, and localizable. Emoji string parsing is brittle and collides with model output. Rejected: Plain-text prefix protocol — rejected because emoji prefix parsing is fragile, hard to localize, and error-prone.

#endregion AgentChat.Research