Skip to content

Commit 4fe7eb5

Browse files
committed
refactor: drop ModelRunner/AgentRunner compat from managed layer
ManagedModel and ManagedAgent now require a Runner. The compat shims (_invoke_runner, isinstance(result, RunnerResult) branches, Union type annotations) are removed; result handling is direct on RunnerResult fields. The deprecated ManagedModel.invoke() is preserved for backwards compat but now delegates to run() and adapts the ManagedResult into the legacy ModelResponse shape. ModelRunner and AgentRunner protocol definitions remain in place so downstream provider packages that import them continue to work.
1 parent b4d15df commit 4fe7eb5

4 files changed

Lines changed: 34 additions & 83 deletions

File tree

packages/sdk/server-ai/src/ldai/client.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -443,7 +443,7 @@ async def create_model(
443443
if not runner:
444444
return None
445445

446-
return ManagedModel(config, runner)
446+
return ManagedModel(config, runner) # type: ignore[arg-type]
447447

448448
async def create_chat(
449449
self,
@@ -517,7 +517,7 @@ async def create_agent(
517517
if not runner:
518518
return None
519519

520-
return ManagedAgent(config, runner)
520+
return ManagedAgent(config, runner) # type: ignore[arg-type]
521521

522522
def agent_config(
523523
self,

packages/sdk/server-ai/src/ldai/managed_agent.py

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,22 @@
11
"""ManagedAgent — LaunchDarkly managed wrapper for agent invocations."""
22

3-
from typing import Union
4-
53
from ldai.models import AIAgentConfig
6-
from ldai.providers import AgentResult, AgentRunner
74
from ldai.providers.runner import Runner
8-
from ldai.providers.types import ManagedResult, RunnerResult
5+
from ldai.providers.types import ManagedResult
96

107

118
class ManagedAgent:
129
"""
1310
LaunchDarkly managed wrapper for AI agent invocations.
1411
15-
Holds an AgentRunner or Runner. Handles tracking automatically via
16-
``create_tracker()``.
12+
Holds a Runner. Handles tracking automatically via ``create_tracker()``.
1713
Obtain an instance via ``LDAIClient.create_agent()``.
1814
"""
1915

2016
def __init__(
2117
self,
2218
ai_config: AIAgentConfig,
23-
agent_runner: Union[Runner, AgentRunner],
19+
agent_runner: Runner,
2420
):
2521
self._ai_config = ai_config
2622
self._agent_runner = agent_runner
@@ -33,23 +29,21 @@ async def run(self, input: str) -> ManagedResult:
3329
:return: ManagedResult containing the agent's output and metric summary
3430
"""
3531
tracker = self._ai_config.create_tracker()
36-
result: Union[RunnerResult, AgentResult] = await tracker.track_metrics_of_async(
32+
result = await tracker.track_metrics_of_async(
3733
lambda r: r.metrics,
3834
lambda: self._agent_runner.run(input),
3935
)
40-
# Support both RunnerResult (content) and legacy AgentResult (output)
41-
content = result.content if isinstance(result, RunnerResult) else result.output # type: ignore[union-attr]
4236
return ManagedResult(
43-
content=content,
37+
content=result.content,
4438
metrics=tracker.get_summary(),
4539
raw=result.raw,
4640
)
4741

48-
def get_agent_runner(self) -> Union[Runner, AgentRunner]:
42+
def get_agent_runner(self) -> Runner:
4943
"""
5044
Return the underlying runner for advanced use.
5145
52-
:return: The Runner or AgentRunner instance.
46+
:return: The Runner instance.
5347
"""
5448
return self._agent_runner
5549

packages/sdk/server-ai/src/ldai/managed_model.py

Lines changed: 22 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,27 @@
11
import asyncio
22
import warnings
3-
from typing import List, Union
3+
from typing import List
44

55
from ldai import log
66
from ldai.models import AICompletionConfig, LDMessage
7-
from ldai.providers.model_runner import ModelRunner
87
from ldai.providers.runner import Runner
9-
from ldai.providers.types import JudgeResult, ManagedResult, ModelResponse, RunnerResult
8+
from ldai.providers.types import JudgeResult, LDAIMetrics, ManagedResult, ModelResponse
109
from ldai.tracker import LDAIConfigTracker
1110

1211

1312
class ManagedModel:
1413
"""
1514
LaunchDarkly managed wrapper for AI model invocations.
1615
17-
Holds a Runner (or legacy ModelRunner). Handles conversation management,
18-
judge evaluation dispatch, and tracking automatically via ``create_tracker()``.
16+
Holds a Runner. Handles conversation management, judge evaluation
17+
dispatch, and tracking automatically via ``create_tracker()``.
1918
Obtain an instance via ``LDAIClient.create_model()``.
2019
"""
2120

2221
def __init__(
2322
self,
2423
ai_config: AICompletionConfig,
25-
model_runner: Union[Runner, ModelRunner],
24+
model_runner: Runner,
2625
):
2726
self._ai_config = ai_config
2827
self._model_runner = model_runner
@@ -48,50 +47,27 @@ async def run(self, prompt: str) -> ManagedResult:
4847
config_messages = self._ai_config.messages or []
4948
all_messages = config_messages + self._messages
5049

51-
result: Union[RunnerResult, ModelResponse] = await tracker.track_metrics_of_async(
50+
result = await tracker.track_metrics_of_async(
5251
lambda r: r.metrics,
53-
lambda: self._invoke_runner(all_messages),
52+
lambda: self._model_runner.run(all_messages),
5453
)
5554

56-
# Support both new RunnerResult and legacy ModelResponse
57-
if isinstance(result, RunnerResult):
58-
content = result.content
59-
raw = result.raw
60-
parsed = result.parsed
61-
assistant_message = LDMessage(role='assistant', content=content)
62-
else:
63-
content = result.message.content
64-
raw = getattr(result, 'raw', None)
65-
parsed = getattr(result, 'parsed', None)
66-
assistant_message = result.message
55+
assistant_message = LDMessage(role='assistant', content=result.content)
6756

6857
input_text = '\r\n'.join(m.content for m in self._messages) if self._messages else ''
6958

70-
evaluations_task = self._track_judge_results(tracker, input_text, content)
59+
evaluations_task = self._track_judge_results(tracker, input_text, result.content)
7160

7261
self._messages.append(assistant_message)
7362

7463
return ManagedResult(
75-
content=content,
64+
content=result.content,
7665
metrics=tracker.get_summary(),
77-
raw=raw,
78-
parsed=parsed,
66+
raw=result.raw,
67+
parsed=result.parsed,
7968
evaluations=evaluations_task,
8069
)
8170

82-
async def _invoke_runner(
83-
self, all_messages: List[LDMessage]
84-
) -> Union[RunnerResult, ModelResponse]:
85-
"""
86-
Delegate to the runner. Supports both the new ``Runner`` protocol
87-
(``run(messages) → RunnerResult``) and the legacy ``ModelRunner``
88-
(``invoke_model(messages) → ModelResponse``).
89-
"""
90-
if isinstance(self._model_runner, Runner):
91-
return await self._model_runner.run(all_messages)
92-
# Legacy ModelRunner path
93-
return await self._model_runner.invoke_model(all_messages) # type: ignore[union-attr]
94-
9571
async def invoke(self, prompt: str) -> ModelResponse:
9672
"""
9773
Invoke the model with a prompt string.
@@ -108,26 +84,16 @@ async def invoke(self, prompt: str) -> ModelResponse:
10884
DeprecationWarning,
10985
stacklevel=2,
11086
)
111-
tracker = self._ai_config.create_tracker()
112-
113-
user_message = LDMessage(role='user', content=prompt)
114-
self._messages.append(user_message)
115-
116-
config_messages = self._ai_config.messages or []
117-
all_messages = config_messages + self._messages
118-
119-
response: ModelResponse = await tracker.track_metrics_of_async(
120-
lambda result: result.metrics,
121-
lambda: self._model_runner.invoke_model(all_messages), # type: ignore[union-attr]
87+
result = await self.run(prompt)
88+
return ModelResponse(
89+
message=LDMessage(role='assistant', content=result.content),
90+
metrics=LDAIMetrics(
91+
success=bool(result.metrics.success),
92+
usage=result.metrics.usage,
93+
),
94+
evaluations=result.evaluations,
12295
)
12396

124-
input_text = '\r\n'.join(m.content for m in self._messages) if self._messages else ''
125-
output_text = response.message.content
126-
response.evaluations = self._track_judge_results(tracker, input_text, output_text)
127-
128-
self._messages.append(response.message)
129-
return response
130-
13197
def _track_judge_results(
13298
self,
13399
tracker: LDAIConfigTracker,
@@ -169,11 +135,11 @@ def append_messages(self, messages: List[LDMessage]) -> None:
169135
"""
170136
self._messages.extend(messages)
171137

172-
def get_model_runner(self) -> Union[Runner, ModelRunner]:
138+
def get_model_runner(self) -> Runner:
173139
"""
174140
Return the underlying runner for advanced use.
175141
176-
:return: The Runner or legacy ModelRunner instance.
142+
:return: The Runner instance.
177143
"""
178144
return self._model_runner
179145

packages/sdk/server-ai/tests/test_managed_model.py

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from ldai.evaluator import Evaluator
1010
from ldai.managed_model import ManagedModel
1111
from ldai.models import AICompletionConfig, LDMessage, ModelConfig, ProviderConfig
12-
from ldai.providers.types import JudgeResult, LDAIMetrics, ManagedResult, ModelResponse, RunnerResult
12+
from ldai.providers.types import JudgeResult, LDAIMetrics, ManagedResult, RunnerResult
1313
from ldai.tracker import LDAIConfigTracker, LDAIMetricSummary
1414

1515

@@ -21,13 +21,6 @@ def _make_runner_result(content: str = 'response text') -> RunnerResult:
2121
)
2222

2323

24-
def _make_model_response(content: str = 'response text') -> ModelResponse:
25-
return ModelResponse(
26-
message=LDMessage(role='assistant', content=content),
27-
metrics=LDAIMetrics(success=True, usage=None),
28-
)
29-
30-
3124
def _make_summary() -> LDAIMetricSummary:
3225
summary = LDAIMetricSummary()
3326
summary._success = True
@@ -237,11 +230,9 @@ async def test_invoke_emits_deprecation_warning(self):
237230
"""invoke() should emit a DeprecationWarning."""
238231
evaluator = Evaluator.noop()
239232
mock_runner = MagicMock()
240-
mock_runner.invoke_model = AsyncMock(return_value=_make_model_response())
233+
mock_runner.run = AsyncMock(return_value=_make_runner_result())
241234

242-
config, mock_tracker = _make_config_with_tracker(evaluator)
243-
# invoke() expects a ModelResponse from the tracker, not a RunnerResult.
244-
mock_tracker.track_metrics_of_async = AsyncMock(return_value=_make_model_response())
235+
config, _mock_tracker = _make_config_with_tracker(evaluator)
245236
model = ManagedModel(config, mock_runner)
246237

247238
with pytest.warns(DeprecationWarning, match=r"ManagedModel\.invoke\(\) is deprecated"):

0 commit comments

Comments
 (0)