77 lines
3.1 KiB
Python
77 lines
3.1 KiB
Python
"""API for agent orchestration observability."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
try:
|
|
from fastapi import APIRouter
|
|
except Exception:
|
|
APIRouter = None
|
|
|
|
from .agent_observability import AgentExecutionRecord, AgentObservability
|
|
from .agent_observability_dashboard import AgentObservabilityDashboard
|
|
from .agent_observability_store import AgentObservabilityStore
|
|
from .trace_tool_credential_governance import GovernanceActor, TraceContext, authorize_scope
|
|
|
|
store = AgentObservabilityStore()
|
|
dashboard = AgentObservabilityDashboard(store.load())
|
|
|
|
|
|
def record_agent_execution(payload: dict[str, Any], *, actor: GovernanceActor | dict[str, Any] | None = None, trace: TraceContext | None = None):
|
|
scoped = any(payload.get(name) is not None for name in (
|
|
"trace_id", "span_id", "tenant_id", "project_id", "conversation_id", "task_id", "run_id"
|
|
))
|
|
values = dict(payload)
|
|
if scoped:
|
|
if actor is None or trace is None:
|
|
raise RuntimeError("TRACE_ACTOR_CONTEXT_REQUIRED")
|
|
trusted = GovernanceActor.from_value(actor)
|
|
authorize_scope(trusted, trace.scope)
|
|
bound = {
|
|
"trace_id": trace.trace_id, "span_id": trace.span_id,
|
|
"parent_span_id": trace.parent_span_id, "correlation_id": trace.correlation_id,
|
|
"causation_id": trace.causation_id, "tenant_id": trace.scope.tenant_id,
|
|
"user_id": trace.scope.user_id, "project_id": trace.scope.project_id,
|
|
"conversation_id": trace.scope.conversation_id, "task_id": trace.task_id,
|
|
"claim_id": trace.claim_id, "run_id": trace.scope.run_id, "message_id": trace.message_id,
|
|
}
|
|
for name, authoritative in bound.items():
|
|
if values.get(name) not in (None, authoritative):
|
|
raise RuntimeError("TRACE_SCOPE_MISMATCH_DENY:" + name)
|
|
values[name] = authoritative
|
|
record = AgentExecutionRecord(**values)
|
|
return {"status": "ok", "record": store.append(record)}
|
|
|
|
|
|
def get_agent_observability(user_id: str | None = None, group_id: str | None = None, role: str = "user", *, actor: GovernanceActor | dict[str, Any] | None = None):
|
|
records = store.query(user_id, group_id, role, actor=actor)
|
|
visible = AgentObservability()
|
|
visible.records = [AgentExecutionRecord(**record) for record in records]
|
|
return {
|
|
"status": "ok",
|
|
"records": records,
|
|
"summary": visible.summary(None),
|
|
}
|
|
|
|
|
|
def build_router():
|
|
if APIRouter is None:
|
|
return None
|
|
router = APIRouter(prefix="/admin/ai-evolution/agents", tags=["agent-observability"])
|
|
|
|
@router.post("/execution", operation_id="recordAgentExecution")
|
|
def record(payload: dict[str, Any]):
|
|
return record_agent_execution(payload)
|
|
|
|
@router.get("/overview", operation_id="getAgentObservabilityOverview")
|
|
def overview(user_id: str | None = None, group_id: str | None = None, role: str = "user"):
|
|
return get_agent_observability(user_id, group_id, role)
|
|
|
|
return router
|
|
|
|
|
|
router = build_router()
|
|
|
|
__all__ = ["router", "record_agent_execution", "get_agent_observability"]
|