feat(api): enhance error handling and add structured hints for agent operations

This commit is contained in:
Abhimanyu Saharan
2026-02-15 02:00:54 +05:30
parent ccdff4835d
commit ee1cf05d5d
6 changed files with 1708 additions and 72 deletions

View File

@@ -16,6 +16,10 @@ def _op_description(schema: dict[str, object], *, path: str, method: str) -> str
return str(op.get("description", "")).strip()
def _schema_by_name(schema: dict[str, object], name: str) -> dict[str, object]:
return schema["components"]["schemas"][name] # type: ignore[return-value]
def test_openapi_agent_role_tags_are_exposed() -> None:
"""Role tags should be queryable without path-based heuristics."""
schema = app.openapi()
@@ -30,6 +34,21 @@ def test_openapi_agent_role_tags_are_exposed() -> None:
path="/api/v1/agent/boards/{board_id}/tasks",
method="get",
)
assert "agent-main" in _op_tags(
schema,
path="/api/v1/agent/boards",
method="get",
)
assert "agent-main" in _op_tags(
schema,
path="/api/v1/agent/boards/{board_id}",
method="get",
)
assert "agent-main" in _op_tags(
schema,
path="/api/v1/agent/agents",
method="get",
)
assert "agent-main" in _op_tags(
schema,
path="/api/v1/agent/gateway/leads/broadcast",
@@ -78,3 +97,121 @@ def test_openapi_agent_role_endpoint_descriptions_exist() -> None:
path="/api/v1/boards/{board_id}/group-snapshot",
method="get",
)
def test_openapi_agent_tool_endpoints_include_llm_hints() -> None:
"""Tool-facing agent endpoints should expose structured usage hints and operation IDs."""
schema = app.openapi()
op_ids: set[str] = set()
expected_paths = [
("/api/v1/agent/boards", "get"),
("/api/v1/agent/boards/{board_id}", "get"),
("/api/v1/agent/agents", "get"),
("/api/v1/agent/heartbeat", "post"),
("/api/v1/agent/boards/{board_id}/tasks", "post"),
("/api/v1/agent/boards/{board_id}/tasks", "get"),
("/api/v1/agent/boards/{board_id}/tags", "get"),
("/api/v1/agent/boards/{board_id}/tasks/{task_id}", "patch"),
("/api/v1/agent/boards/{board_id}/tasks/{task_id}/comments", "get"),
("/api/v1/agent/boards/{board_id}/tasks/{task_id}/comments", "post"),
("/api/v1/agent/boards/{board_id}/memory", "get"),
("/api/v1/agent/boards/{board_id}/memory", "post"),
("/api/v1/boards/{board_id}/group-memory", "get"),
("/api/v1/boards/{board_id}/group-memory", "post"),
("/api/v1/boards/{board_id}/group-memory/stream", "get"),
("/api/v1/agent/boards/{board_id}/approvals", "get"),
("/api/v1/agent/boards/{board_id}/approvals", "post"),
("/api/v1/agent/boards/{board_id}/onboarding", "post"),
("/api/v1/agent/boards/{board_id}/agents/{agent_id}/soul", "get"),
("/api/v1/agent/agents", "post"),
("/api/v1/agent/boards/{board_id}/agents/{agent_id}/nudge", "post"),
("/api/v1/agent/boards/{board_id}/agents/{agent_id}/soul", "put"),
("/api/v1/agent/boards/{board_id}/agents/{agent_id}", "delete"),
("/api/v1/agent/boards/{board_id}/gateway/main/ask-user", "post"),
("/api/v1/agent/gateway/boards/{board_id}/lead/message", "post"),
("/api/v1/agent/gateway/leads/broadcast", "post"),
]
for path, method in expected_paths:
op = schema["paths"][path][method]
assert "x-llm-intent" in op
assert isinstance(op["x-llm-intent"], str)
assert op["x-llm-intent"]
assert "x-negative-guidance" in op
assert isinstance(op["x-negative-guidance"], list)
assert op["x-negative-guidance"]
assert all(isinstance(item, str) and item for item in op["x-negative-guidance"])
assert "x-when-to-use" in op
assert op["x-when-to-use"]
assert "x-routing-policy" in op
assert op["x-routing-policy"]
assert isinstance(op["x-routing-policy"], list)
assert op["x-routing-policy"]
assert all(isinstance(item, str) and item for item in op["x-routing-policy"])
assert "x-required-actor" in op
assert "operationId" in op
assert isinstance(op["operationId"], str)
assert op["operationId"]
assert "x-routing-policy-examples" in op
assert isinstance(op["x-routing-policy-examples"], list)
assert op["x-routing-policy-examples"]
assert all(
isinstance(example, dict)
and "decision" in example
and "input" in example
and isinstance(example["decision"], str)
and example["decision"].strip()
and isinstance(example["input"], dict)
and "intent" in example["input"]
and isinstance(example["input"]["intent"], str)
and example["input"]["intent"].strip()
for example in op["x-routing-policy-examples"]
)
op_ids.add(op["operationId"])
responses = op.get("responses", {})
assert responses
assert len(op_ids) == len(expected_paths)
def test_openapi_agent_schemas_include_discoverability_hints() -> None:
"""Schema-level metadata should advertise usage context for model-driven tooling."""
schema = app.openapi()
expected_schema_hints = [
("AgentCreate", "agent_profile"),
("AgentUpdate", "agent_profile_update"),
("AgentRead", "agent_profile_lookup"),
("GatewayLeadMessageRequest", "lead_direct_message"),
("GatewayLeadMessageResponse", "lead_direct_message_result"),
("GatewayLeadBroadcastResponse", "lead_broadcast_summary"),
("GatewayMainAskUserRequest", "human_escalation_request"),
("GatewayMainAskUserResponse", "human_escalation_result"),
("AgentNudge", "agent_nudge"),
]
for schema_name, intent in expected_schema_hints:
component = _schema_by_name(schema, schema_name)
assert "x-llm-intent" in component
assert component["x-llm-intent"] == intent
assert component.get("x-when-to-use")
assert component.get("x-required-actor") or component_name_is_query(schema_name)
def schema_name_is_query(schema_name: str) -> bool:
"""Some pure response shapes are actor-agnostic and expose interpretation instead."""
return schema_name in {"GatewayLeadBroadcastResponse", "GatewayMainAskUserResponse"}
def test_openapi_agent_schema_fields_have_context() -> None:
"""Request/response fields should include field-level usage hints."""
schema = app.openapi()
request_schema = _schema_by_name(schema, "GatewayLeadMessageRequest")
props = request_schema["properties"] # type: ignore[assignment]
assert "kind" in props
assert props["kind"]["description"]
assert props["kind"]["description"].startswith("Routing mode")
nudge_schema = _schema_by_name(schema, "AgentNudge")
nudge_props = nudge_schema["properties"] # type: ignore[assignment]
assert "message" in nudge_props
assert nudge_props["message"]["description"]