|
1 | 1 | """ManagedAgentGraph — LaunchDarkly managed wrapper for agent graph execution.""" |
2 | 2 |
|
3 | | -from typing import Any |
| 3 | +from typing import Any, Dict |
4 | 4 |
|
5 | | -from ldai.providers import AgentGraphResult, AgentGraphRunner |
6 | | -from ldai.providers.types import GraphMetricSummary, ManagedGraphResult |
| 5 | +from ldai.agent_graph import AgentGraphDefinition |
| 6 | +from ldai.providers import AgentGraphRunner |
| 7 | +from ldai.providers.types import ( |
| 8 | + AgentGraphRunnerResult, |
| 9 | + LDAIMetrics, |
| 10 | + ManagedGraphResult, |
| 11 | +) |
| 12 | +from ldai.tracker import LDAIMetricSummary |
7 | 13 |
|
8 | 14 |
|
9 | 15 | class ManagedAgentGraph: |
10 | 16 | """ |
11 | 17 | LaunchDarkly managed wrapper for AI agent graph execution. |
12 | 18 |
|
13 | | - Holds an AgentGraphRunner. Wraps the runner result in a |
14 | | - :class:`~ldai.providers.types.ManagedGraphResult` and builds a |
15 | | - :class:`~ldai.providers.types.GraphMetricSummary` from the runner's metrics. |
| 19 | + Holds an AgentGraphRunner and an AgentGraphDefinition. Delegates execution |
| 20 | + to the runner, then drives all graph-level and per-node tracking from the |
| 21 | + returned :class:`~ldai.providers.types.AgentGraphRunnerResult`. |
16 | 22 |
|
17 | 23 | Obtain an instance via ``LDAIClient.create_agent_graph()``. |
18 | 24 | """ |
19 | 25 |
|
20 | 26 | def __init__( |
21 | 27 | self, |
| 28 | + graph: AgentGraphDefinition, |
22 | 29 | runner: AgentGraphRunner, |
23 | 30 | ): |
24 | 31 | """ |
25 | 32 | Initialize ManagedAgentGraph. |
26 | 33 |
|
| 34 | + :param graph: The AgentGraphDefinition used to drive graph-level and |
| 35 | + per-node tracking from the runner result metrics. |
27 | 36 | :param runner: The AgentGraphRunner to delegate execution to |
28 | 37 | """ |
| 38 | + self._graph = graph |
29 | 39 | self._runner = runner |
30 | 40 |
|
31 | 41 | async def run(self, input: Any) -> ManagedGraphResult: |
32 | 42 | """ |
33 | 43 | Run the agent graph with the given input. |
34 | 44 |
|
| 45 | + Delegates to the underlying AgentGraphRunner, then drives all |
| 46 | + LaunchDarkly tracking from ``result.metrics``. |
| 47 | +
|
35 | 48 | :param input: The input prompt or structured input for the graph |
36 | | - :return: ManagedGraphResult containing the content, metric summary, raw response, |
37 | | - and an optional evaluations task (currently always ``None`` for graphs — |
38 | | - per-graph evaluations will be added in a future PR). |
| 49 | + :return: ManagedGraphResult containing the content, metric summary, |
| 50 | + and raw response. |
39 | 51 | """ |
40 | | - result: AgentGraphResult = await self._runner.run(input) |
41 | | - |
42 | | - # Build a GraphMetricSummary from the runner result's LDAIMetrics. |
43 | | - # path and node_metrics will be populated once graph runners are migrated |
44 | | - # to return AgentGraphRunnerResult with GraphMetrics (PR 11). |
45 | | - metrics = result.metrics |
46 | | - summary = GraphMetricSummary( |
47 | | - success=metrics.success, |
48 | | - usage=metrics.usage, |
49 | | - duration_ms=getattr(metrics, 'duration_ms', None), |
| 52 | + graph_tracker = self._graph.create_tracker() |
| 53 | + result = await graph_tracker.track_graph_metrics_of_async( |
| 54 | + lambda r: r.metrics, |
| 55 | + lambda: self._runner.run(input), |
50 | 56 | ) |
51 | 57 |
|
| 58 | + summary = graph_tracker.get_summary() |
| 59 | + summary.node_metrics = self._track_node_metrics(result.metrics.node_metrics) |
| 60 | + |
52 | 61 | return ManagedGraphResult( |
53 | | - content=result.output, |
| 62 | + content=result.content, |
54 | 63 | metrics=summary, |
55 | 64 | raw=result.raw, |
56 | 65 | evaluations=None, |
57 | 66 | ) |
58 | 67 |
|
| 68 | + def _track_node_metrics( |
| 69 | + self, node_metrics: Dict[str, LDAIMetrics] |
| 70 | + ) -> Dict[str, LDAIMetricSummary]: |
| 71 | + """ |
| 72 | + Drive per-node LaunchDarkly tracking events and collect node metric summaries. |
| 73 | +
|
| 74 | + For each node key present in ``node_metrics``, obtains the node's |
| 75 | + config tracker via the graph definition, fires tracking events, and |
| 76 | + returns a map of node key to the tracker's metric summary. |
| 77 | + """ |
| 78 | + node_summaries: Dict[str, LDAIMetricSummary] = {} |
| 79 | + for node_key, node_ldai_metrics in node_metrics.items(): |
| 80 | + node = self._graph.get_node(node_key) |
| 81 | + if node is None: |
| 82 | + continue |
| 83 | + node_tracker = node.get_config().create_tracker() |
| 84 | + |
| 85 | + if node_ldai_metrics.usage is not None: |
| 86 | + node_tracker.track_tokens(node_ldai_metrics.usage) |
| 87 | + if node_ldai_metrics.duration_ms is not None: |
| 88 | + node_tracker.track_duration(node_ldai_metrics.duration_ms) |
| 89 | + if node_ldai_metrics.tool_calls: |
| 90 | + node_tracker.track_tool_calls(node_ldai_metrics.tool_calls) |
| 91 | + if node_ldai_metrics.success: |
| 92 | + node_tracker.track_success() |
| 93 | + else: |
| 94 | + node_tracker.track_error() |
| 95 | + |
| 96 | + node_summaries[node_key] = node_tracker.get_summary() |
| 97 | + return node_summaries |
| 98 | + |
59 | 99 | def get_agent_graph_runner(self) -> AgentGraphRunner: |
60 | 100 | """ |
61 | 101 | Return the underlying AgentGraphRunner for advanced use. |
|
0 commit comments