fix(agent): seed trace_id for agent process lifecycle

Agent logs had trace_id='no-trace' because the Gradio process
never called seed_trace_id(). The CotJsonFormatter reads trace_id
from ContextVar — without seeding, it defaults to empty string
displayed as 'no-trace'.

Fix:
- app.py: seed_trace_id() on every agent_handler invocation
- run.py: seed_trace_id() on agent startup (for LLM config fetch)

Each Gradio submit gets a fresh trace_id, making agent logs
correlatable with downstream FastAPI calls.
This commit is contained in:
2026-06-30 17:33:21 +03:00
parent 79d0f8f678
commit 2f238dee13
2 changed files with 9 additions and 3 deletions

View File

@@ -49,6 +49,7 @@ from src.agent.langgraph_setup import create_agent
from src.agent.middleware import log_tool_event from src.agent.middleware import log_tool_event
from src.agent.tools import get_tools_for_query from src.agent.tools import get_tools_for_query
from src.core.auth.jwt import decode_token from src.core.auth.jwt import decode_token
from src.core.cot_logger import seed_trace_id
from src.core.logger import logger from src.core.logger import logger
JWT_SECRET = os.getenv("JWT_SECRET", "super-secret-key") JWT_SECRET = os.getenv("JWT_SECRET", "super-secret-key")
@@ -162,6 +163,7 @@ async def agent_handler( # noqa: C901 — intentionally complex C4 orchestratio
try: try:
# ── Resolve conversation ID early (needed for file persistence) ── # ── Resolve conversation ID early (needed for file persistence) ──
conv_id = conversation_id or str(uuid.uuid4()) conv_id = conversation_id or str(uuid.uuid4())
_trace_id = seed_trace_id()
is_resume = action in ("confirm", "deny") is_resume = action in ("confirm", "deny")
logger.reason( logger.reason(
"Agent handler invoked", "Agent handler invoked",
@@ -237,11 +239,12 @@ async def agent_handler( # noqa: C901 — intentionally complex C4 orchestratio
"metadata": {"type": "error", "code": "STREAM_CLEANUP_TIMEOUT"}, "metadata": {"type": "error", "code": "STREAM_CLEANUP_TIMEOUT"},
}) })
return return
# Capture tool_name BEFORE handle_resume pops the pending confirmation
pending_pre = _pending_confirmations.get(conv_id, {}) if conv_id else {}
tool_name = pending_pre.get("tool_name", "") if pending_pre else ""
async for chunk in handle_resume(conv_id, action, user_jwt_str, env_id): async for chunk in handle_resume(conv_id, action, user_jwt_str, env_id):
yield chunk yield chunk
# Build descriptive title from pending confirmation info # Build descriptive title from captured tool_name
pending = _pending_confirmations.get(conv_id, {}) if conv_id else {}
tool_name = pending.get("tool_name", "") if pending else ""
title = f"{'' if action == 'confirm' else '⏹️'} {tool_name or 'Операция'}" if tool_name else f"HITL: {action}" title = f"{'' if action == 'confirm' else '⏹️'} {tool_name or 'Операция'}" if tool_name else f"HITL: {action}"
await save_conversation(conv_id or str(uuid.uuid4()), title, user_id) await save_conversation(conv_id or str(uuid.uuid4()), title, user_id)
return return

View File

@@ -12,6 +12,7 @@ import os
import socket import socket
import httpx import httpx
from src.core.cot_logger import seed_trace_id
from src.core.logger import logger from src.core.logger import logger
FASTAPI_URL = os.getenv("FASTAPI_URL", "http://localhost:8000") FASTAPI_URL = os.getenv("FASTAPI_URL", "http://localhost:8000")
@@ -85,6 +86,8 @@ if __name__ == "__main__":
from src.agent.context import set_service_jwt from src.agent.context import set_service_jwt
from src.agent.langgraph_setup import configure_from_api, init_checkpointer from src.agent.langgraph_setup import configure_from_api, init_checkpointer
seed_trace_id() # Seed trace for agent startup lifecycle
# Propagate SERVICE_JWT to ContextVar for tool calls # Propagate SERVICE_JWT to ContextVar for tool calls
service_token = os.getenv("SERVICE_JWT", "") service_token = os.getenv("SERVICE_JWT", "")
if service_token: if service_token: