veille-supervisor 0.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- supervisor/__init__.py +3 -0
- supervisor/adapters/__init__.py +11 -0
- supervisor/adapters/generic.py +52 -0
- supervisor/adapters/langgraph/__init__.py +20 -0
- supervisor/adapters/langgraph/adapter.py +132 -0
- supervisor/adapters/langgraph/port.py +53 -0
- supervisor/adapters/langgraph/stub.py +26 -0
- supervisor/adapters/litellm/__init__.py +0 -0
- supervisor/adapters/litellm/mock.py +50 -0
- supervisor/adapters/openai_agents/__init__.py +3 -0
- supervisor/adapters/openai_agents/adapter.py +37 -0
- supervisor/adapters/openai_responses/__init__.py +3 -0
- supervisor/adapters/openai_responses/adapter.py +36 -0
- supervisor/adapters/ports.py +59 -0
- supervisor/adapters/providers/__init__.py +285 -0
- supervisor/analytics/__init__.py +19 -0
- supervisor/analytics/run_summary.py +288 -0
- supervisor/cli.py +323 -0
- supervisor/console/__init__.py +10 -0
- supervisor/console/config.py +70 -0
- supervisor/console/connections.py +80 -0
- supervisor/console/doctor.py +59 -0
- supervisor/console/explorer.py +213 -0
- supervisor/console/run_registry.py +180 -0
- supervisor/console/server.py +160 -0
- supervisor/context/__init__.py +20 -0
- supervisor/context/diversification.py +107 -0
- supervisor/context/engine.py +64 -0
- supervisor/contracts/__init__.py +27 -0
- supervisor/contracts/events.py +65 -0
- supervisor/contracts/plan.py +50 -0
- supervisor/contracts/policy.py +34 -0
- supervisor/contracts/task.py +36 -0
- supervisor/contracts/validation.py +23 -0
- supervisor/io.py +37 -0
- supervisor/memory/__init__.py +23 -0
- supervisor/memory/governor.py +99 -0
- supervisor/memory/scoring.py +55 -0
- supervisor/memory/store.py +111 -0
- supervisor/optimize/__init__.py +30 -0
- supervisor/optimize/cache.py +164 -0
- supervisor/optimize/dedup.py +66 -0
- supervisor/optimize/keys.py +48 -0
- supervisor/optimize/policy.py +96 -0
- supervisor/planning/__init__.py +7 -0
- supervisor/planning/planner.py +129 -0
- supervisor/policy/__init__.py +41 -0
- supervisor/policy/budgets.py +75 -0
- supervisor/policy/enforcement.py +108 -0
- supervisor/policy/engine.py +356 -0
- supervisor/routing/__init__.py +17 -0
- supervisor/routing/router.py +98 -0
- supervisor/sdk/__init__.py +8 -0
- supervisor/sdk/collector.py +73 -0
- supervisor/sdk/supervisor.py +785 -0
- supervisor/telemetry/__init__.py +17 -0
- supervisor/telemetry/exporter.py +150 -0
- veille_supervisor-0.3.0.dist-info/METADATA +167 -0
- veille_supervisor-0.3.0.dist-info/RECORD +61 -0
- veille_supervisor-0.3.0.dist-info/WHEEL +4 -0
- veille_supervisor-0.3.0.dist-info/entry_points.txt +2 -0
supervisor/__init__.py
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""Framework and provider adapter ports."""
|
|
2
|
+
|
|
3
|
+
from supervisor.adapters.langgraph.port import LangGraphAdapter, LangGraphEventHook
|
|
4
|
+
from supervisor.adapters.litellm.mock import LiteLLMMockAdapter, MockCompletionResult
|
|
5
|
+
|
|
6
|
+
__all__ = [
|
|
7
|
+
"LangGraphAdapter",
|
|
8
|
+
"LangGraphEventHook",
|
|
9
|
+
"LiteLLMMockAdapter",
|
|
10
|
+
"MockCompletionResult",
|
|
11
|
+
]
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""Generic framework adapter base.
|
|
2
|
+
|
|
3
|
+
Wraps any Python agent/callable so it executes *through* Veille's runtime and
|
|
4
|
+
emits normalized events. OpenAI Agents SDK and OpenAI Responses API adapters
|
|
5
|
+
build on this; SDK-specific tracing hooks (handoffs, traces) are future
|
|
6
|
+
integration points layered on top of ``InstrumentedAgent``.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import uuid
|
|
12
|
+
from typing import Any, Protocol, runtime_checkable
|
|
13
|
+
|
|
14
|
+
from supervisor.sdk import Supervisor
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@runtime_checkable
|
|
18
|
+
class FrameworkAgent(Protocol):
|
|
19
|
+
def run(self, input: Any, **kwargs: Any) -> Any: ...
|
|
20
|
+
def invoke(self, input: Any, **kwargs: Any) -> Any: ...
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class InstrumentedAgent:
|
|
24
|
+
"""Runs an agent callable under Veille's run lifecycle."""
|
|
25
|
+
|
|
26
|
+
def __init__(self, agent_fn: Any, supervisor: Supervisor) -> None:
|
|
27
|
+
self._agent_fn = agent_fn
|
|
28
|
+
self._supervisor = supervisor
|
|
29
|
+
|
|
30
|
+
def run(self, input: Any, **kwargs: Any) -> Any:
|
|
31
|
+
self._supervisor.start_run()
|
|
32
|
+
try:
|
|
33
|
+
return self._agent_fn(input, self._supervisor)
|
|
34
|
+
finally:
|
|
35
|
+
self._supervisor.finish_run("pass")
|
|
36
|
+
|
|
37
|
+
def invoke(self, input: Any, **kwargs: Any) -> Any:
|
|
38
|
+
return self.run(input, **kwargs)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class GenericFrameworkAdapter:
|
|
42
|
+
"""Implements the framework-neutral :class:`FrameworkAdapter` port."""
|
|
43
|
+
|
|
44
|
+
def attach(self, target: Any, supervisor: Supervisor, **kwargs: Any) -> InstrumentedAgent:
|
|
45
|
+
return InstrumentedAgent(target, supervisor)
|
|
46
|
+
|
|
47
|
+
def extract_run_id(self, config: dict[str, Any] | None) -> str:
|
|
48
|
+
if config and config.get("run_id"):
|
|
49
|
+
return str(config["run_id"])
|
|
50
|
+
if config and config.get("configurable", {}).get("run_id"):
|
|
51
|
+
return str(config["configurable"]["run_id"])
|
|
52
|
+
return str(uuid.uuid4())
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""LangGraph adapter package."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from supervisor.adapters.langgraph.adapter import (
|
|
6
|
+
InstrumentedGraph,
|
|
7
|
+
LangGraphCallbackHandler,
|
|
8
|
+
LangGraphInstrumentedAdapter,
|
|
9
|
+
)
|
|
10
|
+
from supervisor.adapters.langgraph.port import LangGraphAdapter, LangGraphEventHook
|
|
11
|
+
from supervisor.adapters.langgraph.stub import LangGraphAdapterStub
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"InstrumentedGraph",
|
|
15
|
+
"LangGraphCallbackHandler",
|
|
16
|
+
"LangGraphInstrumentedAdapter",
|
|
17
|
+
"LangGraphAdapter",
|
|
18
|
+
"LangGraphEventHook",
|
|
19
|
+
"LangGraphAdapterStub",
|
|
20
|
+
]
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"""LangGraph adapter implementation (Phase 1).
|
|
2
|
+
|
|
3
|
+
Wraps a compiled LangGraph graph so that a supervised run is captured
|
|
4
|
+
automatically: run lifecycle via the wrapper, and model/tool/retry events via
|
|
5
|
+
a LangChain callback handler for graphs that use LangChain-native LLM/Tool
|
|
6
|
+
primitives. The cited-market-research demo uses the ``Supervisor`` helpers for
|
|
7
|
+
its manual mock calls, so the callback handler is dormant there but available
|
|
8
|
+
for any LangChain-based agent.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from typing import Any
|
|
14
|
+
from uuid import uuid4
|
|
15
|
+
|
|
16
|
+
from langchain_core.callbacks import BaseCallbackHandler
|
|
17
|
+
|
|
18
|
+
from supervisor.contracts.events import EventType
|
|
19
|
+
from supervisor.sdk.supervisor import Supervisor
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class LangGraphCallbackHandler(BaseCallbackHandler):
|
|
23
|
+
"""Maps LangChain callback events to normalized supervisor ``RunEvent`` facts."""
|
|
24
|
+
|
|
25
|
+
def __init__(self, supervisor: Supervisor) -> None:
|
|
26
|
+
super().__init__()
|
|
27
|
+
self._supervisor = supervisor
|
|
28
|
+
|
|
29
|
+
def on_llm_start(
|
|
30
|
+
self, serialized: Any, prompts: Any, *, run_id: Any = None, **kwargs: Any
|
|
31
|
+
) -> None:
|
|
32
|
+
invocation = kwargs.get("invocation_params") or {}
|
|
33
|
+
model = invocation.get("model") or invocation.get("model_name") or "unknown"
|
|
34
|
+
preview = ""
|
|
35
|
+
if isinstance(prompts, (list, tuple)) and prompts:
|
|
36
|
+
preview = str(prompts[0])[:120]
|
|
37
|
+
self._supervisor.collector.emit(
|
|
38
|
+
EventType.MODEL_REQUESTED,
|
|
39
|
+
model_name=str(model),
|
|
40
|
+
attributes={"prompt_preview": preview},
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
def on_llm_end(self, response: Any, *, run_id: Any = None, **kwargs: Any) -> None:
|
|
44
|
+
tokens_in = tokens_out = None
|
|
45
|
+
try:
|
|
46
|
+
usage = getattr(response, "llm_output", {}) or {}
|
|
47
|
+
token_usage = usage.get("token_usage", {}) or {}
|
|
48
|
+
tokens_in = token_usage.get("prompt_tokens")
|
|
49
|
+
tokens_out = token_usage.get("completion_tokens")
|
|
50
|
+
except Exception:
|
|
51
|
+
pass
|
|
52
|
+
self._supervisor.collector.emit(
|
|
53
|
+
EventType.MODEL_COMPLETED,
|
|
54
|
+
input_tokens=tokens_in,
|
|
55
|
+
output_tokens=tokens_out,
|
|
56
|
+
status="ok",
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
def on_tool_start(
|
|
60
|
+
self, serialized: Any, input_str: Any, *, run_id: Any = None, **kwargs: Any
|
|
61
|
+
) -> None:
|
|
62
|
+
name = (
|
|
63
|
+
serialized.get("name")
|
|
64
|
+
if isinstance(serialized, dict)
|
|
65
|
+
else getattr(serialized, "name", None)
|
|
66
|
+
)
|
|
67
|
+
self._supervisor.collector.emit(
|
|
68
|
+
EventType.TOOL_REQUESTED,
|
|
69
|
+
tool_name=name,
|
|
70
|
+
attributes={"input": {"input": str(input_str)[:500]}},
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
def on_tool_end(self, output: Any, *, run_id: Any = None, **kwargs: Any) -> None:
|
|
74
|
+
self._supervisor.collector.emit(EventType.TOOL_COMPLETED, status="ok")
|
|
75
|
+
|
|
76
|
+
def on_tool_error(self, error: Any, *, run_id: Any = None, **kwargs: Any) -> None:
|
|
77
|
+
self._supervisor.collector.emit(
|
|
78
|
+
EventType.TOOL_COMPLETED,
|
|
79
|
+
status="error",
|
|
80
|
+
error_message=str(error)[:500],
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
def on_retry(self, retry_state: Any, *, run_id: Any = None, **kwargs: Any) -> None:
|
|
84
|
+
self._supervisor.collector.emit(EventType.RETRY_SCHEDULED)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class InstrumentedGraph:
|
|
88
|
+
"""A compiled graph wrapped so supervisor run lifecycle is captured."""
|
|
89
|
+
|
|
90
|
+
def __init__(
|
|
91
|
+
self, graph: Any, supervisor: Supervisor, *, auto_run_lifecycle: bool = True
|
|
92
|
+
) -> None:
|
|
93
|
+
self._graph = graph
|
|
94
|
+
self._supervisor = supervisor
|
|
95
|
+
self._handler = LangGraphCallbackHandler(supervisor)
|
|
96
|
+
self._auto = auto_run_lifecycle
|
|
97
|
+
|
|
98
|
+
def _merge_config(self, config: Any) -> dict[str, Any]:
|
|
99
|
+
base: dict[str, Any] = dict(config) if isinstance(config, dict) else {}
|
|
100
|
+
callbacks = list(base.get("callbacks") or [])
|
|
101
|
+
callbacks.append(self._handler)
|
|
102
|
+
base["callbacks"] = callbacks
|
|
103
|
+
return base
|
|
104
|
+
|
|
105
|
+
def invoke(self, input: Any, config: Any = None, **kwargs: Any) -> Any:
|
|
106
|
+
if self._auto:
|
|
107
|
+
self._supervisor.start_run()
|
|
108
|
+
try:
|
|
109
|
+
out = self._graph.invoke(input, config=self._merge_config(config), **kwargs)
|
|
110
|
+
except Exception:
|
|
111
|
+
if self._auto:
|
|
112
|
+
self._supervisor.finish_run("error")
|
|
113
|
+
raise
|
|
114
|
+
if self._auto:
|
|
115
|
+
self._supervisor.finish_run("ok")
|
|
116
|
+
return out
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class LangGraphInstrumentedAdapter:
|
|
120
|
+
"""Phase 1 LangGraph adapter: implements the port's ``attach`` contract."""
|
|
121
|
+
|
|
122
|
+
def attach(
|
|
123
|
+
self, graph: Any, supervisor: Supervisor, *, auto_run_lifecycle: bool = True
|
|
124
|
+
) -> InstrumentedGraph:
|
|
125
|
+
return InstrumentedGraph(graph, supervisor, auto_run_lifecycle=auto_run_lifecycle)
|
|
126
|
+
|
|
127
|
+
def extract_run_id(self, config: dict[str, Any] | None) -> str:
|
|
128
|
+
if config and "configurable" in config:
|
|
129
|
+
run_id = config["configurable"].get("run_id")
|
|
130
|
+
if isinstance(run_id, str):
|
|
131
|
+
return run_id
|
|
132
|
+
return str(uuid4())
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""LangGraph adapter port — Phase 1 will implement full instrumentation."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, Protocol, runtime_checkable
|
|
6
|
+
|
|
7
|
+
from supervisor.contracts.events import RunEvent
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@runtime_checkable
|
|
11
|
+
class LangGraphEventHook(Protocol):
|
|
12
|
+
"""Callback surface for LangGraph lifecycle events."""
|
|
13
|
+
|
|
14
|
+
def on_run_started(self, run_id: str, task_id: str, metadata: dict[str, Any]) -> None: ...
|
|
15
|
+
|
|
16
|
+
def on_run_finished(self, run_id: str, status: str, metadata: dict[str, Any]) -> None: ...
|
|
17
|
+
|
|
18
|
+
def on_agent_event(
|
|
19
|
+
self, run_id: str, agent_id: str, event_type: str, metadata: dict[str, Any]
|
|
20
|
+
) -> None: ...
|
|
21
|
+
|
|
22
|
+
def on_tool_event(
|
|
23
|
+
self,
|
|
24
|
+
run_id: str,
|
|
25
|
+
tool_name: str,
|
|
26
|
+
event_type: str,
|
|
27
|
+
input_data: dict[str, Any],
|
|
28
|
+
metadata: dict[str, Any],
|
|
29
|
+
) -> None: ...
|
|
30
|
+
|
|
31
|
+
def on_model_event(
|
|
32
|
+
self,
|
|
33
|
+
run_id: str,
|
|
34
|
+
model_name: str,
|
|
35
|
+
event_type: str,
|
|
36
|
+
token_usage: dict[str, int],
|
|
37
|
+
metadata: dict[str, Any],
|
|
38
|
+
) -> None: ...
|
|
39
|
+
|
|
40
|
+
def flush(self) -> list[RunEvent]: ...
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@runtime_checkable
|
|
44
|
+
class LangGraphAdapter(Protocol):
|
|
45
|
+
"""Port for wrapping LangGraph agents with supervisor instrumentation."""
|
|
46
|
+
|
|
47
|
+
def attach(self, graph: Any, hook: LangGraphEventHook) -> Any:
|
|
48
|
+
"""Return an instrumented graph. Phase 1 implementation."""
|
|
49
|
+
...
|
|
50
|
+
|
|
51
|
+
def extract_run_id(self, config: dict[str, Any] | None) -> str:
|
|
52
|
+
"""Extract or generate a stable run identifier."""
|
|
53
|
+
...
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""LangGraph adapter stub — returns graph unchanged until Phase 1."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
from uuid import uuid4
|
|
7
|
+
|
|
8
|
+
from supervisor.adapters.langgraph.port import LangGraphEventHook
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class LangGraphAdapterStub:
|
|
12
|
+
"""Phase 0 stub: documents the adapter contract without modifying execution."""
|
|
13
|
+
|
|
14
|
+
def attach(self, graph: Any, hook: LangGraphEventHook) -> Any:
|
|
15
|
+
_ = hook
|
|
16
|
+
return graph
|
|
17
|
+
|
|
18
|
+
def extract_run_id(self, config: dict[str, Any] | None) -> str:
|
|
19
|
+
if config and "configurable" in config:
|
|
20
|
+
run_id = config["configurable"].get("run_id")
|
|
21
|
+
if isinstance(run_id, str):
|
|
22
|
+
return run_id
|
|
23
|
+
return str(uuid4())
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# LangGraphAdapterStub implements the LangGraphAdapter protocol.
|
|
File without changes
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""LiteLLM mock adapter — deterministic costs without API calls."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass(frozen=True)
|
|
9
|
+
class MockCompletionResult:
|
|
10
|
+
model: str
|
|
11
|
+
content: str
|
|
12
|
+
input_tokens: int
|
|
13
|
+
output_tokens: int
|
|
14
|
+
cost_usd: float
|
|
15
|
+
latency_ms: float
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class LiteLLMMockAdapter:
|
|
19
|
+
"""Returns deterministic token counts and costs for demo and tests."""
|
|
20
|
+
|
|
21
|
+
DEFAULT_PRICING: dict[str, tuple[float, float]] = {
|
|
22
|
+
"mock-research": (0.000001, 0.000002),
|
|
23
|
+
"mock-synthesis": (0.000002, 0.000004),
|
|
24
|
+
"mock-review": (0.000003, 0.000005),
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
def __init__(self, use_mock: bool = True) -> None:
|
|
28
|
+
self.use_mock = use_mock
|
|
29
|
+
|
|
30
|
+
def complete(
|
|
31
|
+
self, model: str, prompt: str, max_output_tokens: int = 512
|
|
32
|
+
) -> MockCompletionResult:
|
|
33
|
+
if not self.use_mock:
|
|
34
|
+
raise RuntimeError(
|
|
35
|
+
"Real LiteLLM calls are opt-in. Set USE_MOCK_MODELS=true or install litellm extra."
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
input_tokens = max(50, len(prompt) // 4)
|
|
39
|
+
output_tokens = min(max_output_tokens, max(80, len(prompt) // 8))
|
|
40
|
+
input_rate, output_rate = self.DEFAULT_PRICING.get(model, (0.000001, 0.000002))
|
|
41
|
+
cost = (input_tokens * input_rate) + (output_tokens * output_rate)
|
|
42
|
+
|
|
43
|
+
return MockCompletionResult(
|
|
44
|
+
model=model,
|
|
45
|
+
content=f"[mock response from {model}]",
|
|
46
|
+
input_tokens=input_tokens,
|
|
47
|
+
output_tokens=output_tokens,
|
|
48
|
+
cost_usd=round(cost, 6),
|
|
49
|
+
latency_ms=120.0,
|
|
50
|
+
)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""OpenAI Agents SDK adapter.
|
|
2
|
+
|
|
3
|
+
Executes OpenAI Agents SDK agents through Veille's runtime. The base
|
|
4
|
+
``GenericFrameworkAdapter`` runs the agent and emits normalized events; SDK-specific
|
|
5
|
+
tracing (handoffs, guardrails, session traces) is layered on here when the
|
|
6
|
+
``openai-agents`` package is installed. Until then, any Python agent callable is
|
|
7
|
+
run through Veille unchanged.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
from supervisor.adapters.generic import GenericFrameworkAdapter, InstrumentedAgent
|
|
15
|
+
from supervisor.sdk import Supervisor
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class OpenAIAgentsAdapter(GenericFrameworkAdapter):
|
|
19
|
+
"""Adapter for OpenAI Agents SDK agents."""
|
|
20
|
+
|
|
21
|
+
name = "openai_agents"
|
|
22
|
+
|
|
23
|
+
def attach(self, target: Any, supervisor: Supervisor, **kwargs: Any) -> InstrumentedAgent:
|
|
24
|
+
# If the real SDK is present, prefer its Runner so tracing integrates;
|
|
25
|
+
# otherwise fall back to the generic instrumented callable.
|
|
26
|
+
try: # pragma: no cover - optional dependency
|
|
27
|
+
from agents import Runner # type: ignore
|
|
28
|
+
|
|
29
|
+
if hasattr(target, "run") or hasattr(target, "invoke"):
|
|
30
|
+
|
|
31
|
+
def _run(input: Any, sup: Supervisor) -> Any:
|
|
32
|
+
return Runner.run(target, input)
|
|
33
|
+
|
|
34
|
+
return InstrumentedAgent(_run, supervisor)
|
|
35
|
+
except Exception: # noqa: BLE001
|
|
36
|
+
pass
|
|
37
|
+
return super().attach(target, supervisor)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""OpenAI Responses API adapter.
|
|
2
|
+
|
|
3
|
+
Runs agents built on the OpenAI Responses API through Veille's runtime. Shares
|
|
4
|
+
the generic instrumented execution; Responses-API tracing (response objects,
|
|
5
|
+
tool calls) is integrated here when the SDK is available.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from supervisor.adapters.generic import GenericFrameworkAdapter, InstrumentedAgent
|
|
13
|
+
from supervisor.sdk import Supervisor
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class OpenAIResponsesAdapter(GenericFrameworkAdapter):
|
|
17
|
+
"""Adapter for OpenAI Responses API agents."""
|
|
18
|
+
|
|
19
|
+
name = "openai_responses"
|
|
20
|
+
|
|
21
|
+
def attach(self, target: Any, supervisor: Supervisor, **kwargs: Any) -> InstrumentedAgent:
|
|
22
|
+
# When the real SDK is present, route through the Responses client so the
|
|
23
|
+
# native trace is captured; otherwise run the generic instrumented callable.
|
|
24
|
+
try: # pragma: no cover - optional dependency
|
|
25
|
+
from openai import OpenAI # type: ignore
|
|
26
|
+
|
|
27
|
+
if hasattr(target, "run") or hasattr(target, "invoke"):
|
|
28
|
+
|
|
29
|
+
def _run(input: Any, sup: Supervisor) -> Any:
|
|
30
|
+
client = OpenAI()
|
|
31
|
+
return client.responses.create(model="gpt-4o", input=input)
|
|
32
|
+
|
|
33
|
+
return InstrumentedAgent(_run, supervisor)
|
|
34
|
+
except Exception: # noqa: BLE001
|
|
35
|
+
pass
|
|
36
|
+
return super().attach(target, supervisor)
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""Framework-neutral adapter ports.
|
|
2
|
+
|
|
3
|
+
Generalizes the LangGraph-specific port pair into provider-agnostic contracts so
|
|
4
|
+
every future agent framework (CrewAI, AutoGen, Semantic Kernel, Mastra, PydanticAI,
|
|
5
|
+
custom Python agents) implements the same interface. Adapters turn framework-native
|
|
6
|
+
lifecycle into normalized ``RunEvent``s through the supervisor's collector.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from typing import Any, Protocol, runtime_checkable
|
|
12
|
+
|
|
13
|
+
from supervisor.contracts.events import RunEvent
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@runtime_checkable
|
|
17
|
+
class RunEventHook(Protocol):
|
|
18
|
+
"""Sink for normalized runtime events emitted by any framework adapter."""
|
|
19
|
+
|
|
20
|
+
def on_run_started(self, run_id: str, task_id: str, metadata: dict[str, Any]) -> None: ...
|
|
21
|
+
|
|
22
|
+
def on_run_finished(self, run_id: str, status: str, metadata: dict[str, Any]) -> None: ...
|
|
23
|
+
|
|
24
|
+
def on_agent_event(
|
|
25
|
+
self, run_id: str, agent_id: str, event_type: str, metadata: dict[str, Any]
|
|
26
|
+
) -> None: ...
|
|
27
|
+
|
|
28
|
+
def on_tool_event(
|
|
29
|
+
self,
|
|
30
|
+
run_id: str,
|
|
31
|
+
tool_name: str,
|
|
32
|
+
event_type: str,
|
|
33
|
+
input_data: dict[str, Any],
|
|
34
|
+
metadata: dict[str, Any],
|
|
35
|
+
) -> None: ...
|
|
36
|
+
|
|
37
|
+
def on_model_event(
|
|
38
|
+
self,
|
|
39
|
+
run_id: str,
|
|
40
|
+
model_name: str,
|
|
41
|
+
event_type: str,
|
|
42
|
+
token_usage: dict[str, int],
|
|
43
|
+
metadata: dict[str, Any],
|
|
44
|
+
) -> None: ...
|
|
45
|
+
|
|
46
|
+
def flush(self) -> list[RunEvent]: ...
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@runtime_checkable
|
|
50
|
+
class FrameworkAdapter(Protocol):
|
|
51
|
+
"""Port for wrapping any agent framework with supervisor instrumentation."""
|
|
52
|
+
|
|
53
|
+
def attach(self, graph: Any, hook: RunEventHook) -> Any:
|
|
54
|
+
"""Return an instrumented graph/agent bound to the hook."""
|
|
55
|
+
...
|
|
56
|
+
|
|
57
|
+
def extract_run_id(self, config: dict[str, Any] | None) -> str:
|
|
58
|
+
"""Extract or generate a stable run identifier."""
|
|
59
|
+
...
|