feat: complete phase 2 -- multi-agent routing, interrupt TTL, escalation, templates
- Intent classification with LLM structured output (single/multi/ambiguous) - Discount agent with apply_discount and generate_coupon tools - Interrupt manager with 30-min TTL auto-expiration and retry prompts - Webhook escalation module with exponential backoff retry (max 3) - Three vertical industry templates (e-commerce, SaaS, fintech) - Template loading in AgentRegistry - Enhanced supervisor prompt with dynamic agent descriptions - 153 tests passing, 90.18% coverage
This commit is contained in:
@@ -13,7 +13,10 @@ from fastapi.staticfiles import StaticFiles
|
||||
from app.callbacks import TokenUsageCallbackHandler
|
||||
from app.config import Settings
|
||||
from app.db import create_checkpointer, create_pool, setup_app_tables
|
||||
from app.escalation import NoOpEscalator, WebhookEscalator
|
||||
from app.graph import build_graph
|
||||
from app.intent import LLMIntentClassifier
|
||||
from app.interrupt_manager import InterruptManager
|
||||
from app.llm import create_llm
|
||||
from app.registry import AgentRegistry
|
||||
from app.session_manager import SessionManager
|
||||
@@ -36,23 +39,46 @@ async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
|
||||
checkpointer = await create_checkpointer(pool)
|
||||
await setup_app_tables(pool)
|
||||
|
||||
registry = AgentRegistry.load(AGENTS_YAML)
|
||||
# Load agents from template or default YAML
|
||||
if settings.template_name:
|
||||
registry = AgentRegistry.load_template(settings.template_name)
|
||||
else:
|
||||
registry = AgentRegistry.load(AGENTS_YAML)
|
||||
|
||||
llm = create_llm(settings)
|
||||
graph = build_graph(registry, llm, checkpointer)
|
||||
intent_classifier = LLMIntentClassifier(llm)
|
||||
graph = build_graph(registry, llm, checkpointer, intent_classifier=intent_classifier)
|
||||
|
||||
session_manager = SessionManager(
|
||||
session_ttl_seconds=settings.session_ttl_minutes * 60,
|
||||
)
|
||||
interrupt_manager = InterruptManager(
|
||||
ttl_seconds=settings.interrupt_ttl_minutes * 60,
|
||||
)
|
||||
|
||||
# Configure escalation
|
||||
if settings.webhook_url:
|
||||
escalator = WebhookEscalator(
|
||||
url=settings.webhook_url,
|
||||
timeout_seconds=settings.webhook_timeout_seconds,
|
||||
max_retries=settings.webhook_max_retries,
|
||||
)
|
||||
else:
|
||||
escalator = NoOpEscalator()
|
||||
|
||||
app.state.graph = graph
|
||||
app.state.session_manager = session_manager
|
||||
app.state.interrupt_manager = interrupt_manager
|
||||
app.state.escalator = escalator
|
||||
app.state.settings = settings
|
||||
app.state.pool = pool
|
||||
|
||||
logger.info(
|
||||
"Smart Support started: %d agents loaded, LLM=%s/%s",
|
||||
"Smart Support started: %d agents loaded, LLM=%s/%s, template=%s",
|
||||
len(registry),
|
||||
settings.llm_provider,
|
||||
settings.llm_model,
|
||||
settings.template_name or "(default)",
|
||||
)
|
||||
|
||||
yield
|
||||
@@ -60,7 +86,7 @@ async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
|
||||
await pool.close()
|
||||
|
||||
|
||||
app = FastAPI(title="Smart Support", version="0.1.0", lifespan=lifespan)
|
||||
app = FastAPI(title="Smart Support", version="0.2.0", lifespan=lifespan)
|
||||
|
||||
|
||||
@app.websocket("/ws")
|
||||
@@ -68,13 +94,17 @@ async def websocket_endpoint(ws: WebSocket) -> None:
|
||||
await ws.accept()
|
||||
graph = app.state.graph
|
||||
session_manager = app.state.session_manager
|
||||
interrupt_manager = app.state.interrupt_manager
|
||||
settings = app.state.settings
|
||||
callback_handler = TokenUsageCallbackHandler(model_name=settings.llm_model)
|
||||
|
||||
try:
|
||||
while True:
|
||||
raw_data = await ws.receive_text()
|
||||
await dispatch_message(ws, graph, session_manager, callback_handler, raw_data)
|
||||
await dispatch_message(
|
||||
ws, graph, session_manager, callback_handler, raw_data,
|
||||
interrupt_manager=interrupt_manager,
|
||||
)
|
||||
except WebSocketDisconnect:
|
||||
logger.info("WebSocket client disconnected")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user