OpenBox LangGraph SDK — Span & OTel Flow

Interactive visualization of governance event flow

LangGraph
Handler
SpanProcessor
SpanCollector
OTel / HTTP hooks
hook_governance
OpenBox Core
Regular Tool
Subagent (task)
LLM Call
Architecture
Phase A: on_tool_start on_tool_start("search_web") _map_event() → ToolStarted activate_span_collector() set_activity_context() register_trace(trace_id→activity) evaluate_event() → send ToolStarted Core: ActivityStarted → verdict mark_activity_started() + internal span "started" Phase B: Tool executes httpx call search_web() → httpx.get("https://wikipedia.org") Path 1: OTel Request Hook _httpx_async_request_hook store span + governance "started" evaluate_async() → _build_payload() SpanProcessor.get_activity_context_by_trace() Path 2: Patched AsyncClient.send _patched_async_send execute + capture body + "completed" evaluate_async() → completed with request_body + response_body Core: hook_trigger=true → policy eval Phase C: on_tool_end on_tool_end("search_web") drain_spans() clear_activity_context() internal span "completed" + send ToolCompleted Core: ActivityCompleted → close row
Subagent: on_tool_start("task", subagent_type="researcher") on_tool_start("task") is_subagent? YES SKIP all span setup activate_span_collector — SKIP set_activity_context — SKIP register_trace — SKIP mark_activity_started — SKIP internal span hooks — SKIP Event-level governance: ToolStarted __openbox: {subagent_name: "researcher", tool_type: "a2a"} Core: Rego policy checks subagent_name Subagent executes internally (invisible) subagent.invoke() → new LangGraph graph → new asyncio.Tasks Internal HTTP calls get new trace_ids → SpanProcessor has no context → spans silently dropped (correct behavior) on_tool_end("task") Event-level only: send ToolCompleted Core: ActivityCompleted → close row
LLM Call: on_chat_model_start / end on_chat_model_start("ChatOpenAI") _GuardrailsCallbackHandler.on_chat_model_start() Fires BEFORE LLM call — extracts user prompt Send LLMStarted (activity_type="llm_call") Core: Run guardrails (PII, content filter, toxicity) → return redacted_input if PII detected Mutate messages in-place (PII redaction) LLM calls api.openai.com / api.anthropic.com httpx hook fires but URL is in _LLM_PROVIDER_PREFIXES _should_ignore_url() → IGNORED on_chat_model_end Core: LLMCompleted (tokens, model, completion)
LangGraph Event Stream langgraph_handler.py _map_event() _process_event() _pre_screen_input() _GuardrailsCallback subagent bypass verdict enforcement SpanCollector per-tool instance ContextVar-based lookup OpenBox Core /api/v1/governance/evaluate Rego policy + guardrails + HITL SpanProcessor singleton (OTel interface) trace_id → activity map hook_governance.py evaluate_sync() / evaluate_async() hook_trigger=true OTel Instrumentation Layer http_governance_hooks requests | httpx | urllib3 | urllib db_governance_hooks psycopg2 | asyncpg | mongo | redis | SA file_governance_hooks builtins.open() patch otel_setup.py — orchestrates all instrumentors tracing.py — @traced decorator for internal function spans