feat(agent): Gradio-powered LangGraph agent chat with streaming, tool calls, file upload, conversation persistence
- Gradio 5.50.0 ChatInterface with type='messages' streaming - LangGraph create_react_agent with InMemorySaver checkpointer - 4 @tool functions: search_dashboards, get_health_summary, list_environments, get_task_status - Structured ChatMessage metadata (7 discriminator types: stream_token, tool_start/end/error, confirm_required, confirm_resolved, error) - HITL resume via second submit() with interrupt_before/Command - Dual-identity RBAC: service JWT + user JWT for tool calls - File upload (10 MB limit, pdfplumber/xlsx/JSON parser) - Conversation persistence via POST /api/agent/conversations/save - REST API: list, history, archive conversations; multi-tab gate; LLM config - LLM provider selection via Admin -> LLM Settings (assistant_planner_provider) - Svelte 5 AgentChatModel with stream event queue, dedup, stream_status watcher - MarkdownRenderer using svelte-markdown with semantic Tailwind tokens - ToolCallCard (3 states: executing/completed/failed) - ConversationList with search, date grouping, infinite scroll - ConnectionIndicator with Gradio health status - /agent route with two-column layout - Vite proxy /api/agent/gradio -> Gradio SSE - Fixed: not_() SQLAlchemy operator, route collision with _admin_routes - Fixed: conversation_id -> id normalization, .pyc cache staleness - Fixed: event.data array parsing (Gradio returns [jsonStr, null]) - Requirements pinned: gradio==5.50.0, pydantic>=2.7,<=2.12.3
This commit is contained in:
75
backend/src/agent/langgraph_setup.py
Normal file
75
backend/src/agent/langgraph_setup.py
Normal file
@@ -0,0 +1,75 @@
|
||||
# backend/src/agent/langgraph_setup.py
|
||||
# #region AgentChat.LangGraph.Setup [C:4] [TYPE Module] [SEMANTICS agent-chat,langgraph,agent]
|
||||
# @defgroup AgentChat LangGraph agent setup: create_react_agent with PostgresSaver.
|
||||
# @PRE LLM provider configured. Priority: 1) llm_config param 2) env vars LLM_API_KEY/LLM_BASE_URL/LLM_MODEL.
|
||||
# @POST Compiled StateGraph ready for astream_events().
|
||||
# @SIDE_EFFECT Initializes checkpointer and message history tables on first call.
|
||||
# @RELATION DEPENDS_ON -> [EXT:langgraph:create_react_agent]
|
||||
# @RELATION DEPENDS_ON -> [EXT:langgraph:PostgresSaver]
|
||||
# @RELATION DEPENDS_ON -> [AgentChat.Tools]
|
||||
# @RATIONALE LangGraph create_react_agent provides built-in tool calling + checkpointing + interrupt/resume.
|
||||
# RunnableWithMessageHistory wrapper is NOT used — PostgresSaver handles history natively.
|
||||
|
||||
import os
|
||||
|
||||
from langchain_openai import ChatOpenAI
|
||||
from langgraph.checkpoint.memory import InMemorySaver
|
||||
from langgraph.prebuilt import create_react_agent
|
||||
|
||||
# ── Dangerous tool names — interrupt_before pauses execution at these nodes ──
|
||||
# ── Dangerous tool names — interrupt_before pauses execution at these nodes ──
|
||||
# These tools don't exist yet in the current tool set. When dangerous tools are
|
||||
# added (deploy, migrate, commit, maintenance), add their names here.
|
||||
DANGEROUS_TOOLS: list[str] = []
|
||||
|
||||
# ── LLM config cache ────────────────────────────────────────────
|
||||
_llm_config: dict | None = None
|
||||
_llm_config_ttl: int = 300 # 5 min
|
||||
|
||||
|
||||
def configure_from_api(llm_config: dict) -> None:
|
||||
"""Update LLM config from FastAPI response. Called at startup."""
|
||||
global _llm_config
|
||||
_llm_config = llm_config
|
||||
|
||||
|
||||
def create_agent(tools: list):
|
||||
"""Create the LangGraph agent with checkpointer and message history.
|
||||
|
||||
LLM configuration priority:
|
||||
1. llm_config from configure_from_api() (fetched from FastAPI /api/agent/llm-config)
|
||||
2. Environment vars: LLM_API_KEY, LLM_BASE_URL, LLM_MODEL
|
||||
3. Defaults: gpt-4o, https://api.openai.com/v1
|
||||
|
||||
Returns a RunnableWithMessageHistory wrapper ready for astream_events().
|
||||
The graph is compiled with interrupt_before=DANGEROUS_TOOLS to enable HITL.
|
||||
"""
|
||||
if _llm_config and _llm_config.get("configured"):
|
||||
api_key = _llm_config["api_key"]
|
||||
base_url = _llm_config.get("base_url") or "https://api.openai.com/v1"
|
||||
model = _llm_config.get("default_model") or "gpt-4o-mini"
|
||||
else:
|
||||
api_key = os.getenv("LLM_API_KEY")
|
||||
base_url = os.getenv("LLM_BASE_URL", "https://api.openai.com/v1")
|
||||
model = os.getenv("LLM_MODEL", "gpt-4o")
|
||||
|
||||
llm = ChatOpenAI(
|
||||
model=model,
|
||||
base_url=base_url,
|
||||
api_key=api_key,
|
||||
temperature=0,
|
||||
)
|
||||
|
||||
# Checkpointer — InMemorySaver for development (no persistence across restarts).
|
||||
# TODO: Replace with AsyncPostgresSaver when langgraph-checkpoint-postgres supports it.
|
||||
checkpointer = InMemorySaver()
|
||||
|
||||
graph = create_react_agent(
|
||||
model=llm,
|
||||
tools=tools,
|
||||
checkpointer=checkpointer,
|
||||
interrupt_before=DANGEROUS_TOOLS,
|
||||
)
|
||||
|
||||
return graph
|
||||
# #endregion AgentChat.LangGraph.Setup
|
||||
Reference in New Issue
Block a user