Skip to content

Commit 7012ab4

Browse files
authored
fix: remove obsolete llm agent protocol from evals (#1359)
1 parent bb02d33 commit 7012ab4

3 files changed

Lines changed: 7 additions & 184 deletions

File tree

src/uipath/_cli/_evals/_runtime.py

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,8 @@
88
Awaitable,
99
Iterable,
1010
Iterator,
11-
Protocol,
1211
Sequence,
1312
Tuple,
14-
runtime_checkable,
1513
)
1614

1715
import coverage
@@ -100,25 +98,6 @@
10098
logger = logging.getLogger(__name__)
10199

102100

103-
@runtime_checkable
104-
class LLMAgentRuntimeProtocol(Protocol):
105-
"""Protocol for runtimes that can provide agent model information.
106-
107-
Runtimes that implement this protocol can be queried for
108-
the agent's configured LLM model, enabling features like 'same-as-agent'
109-
model resolution for evaluators.
110-
"""
111-
112-
def get_agent_model(self) -> str | None:
113-
"""Return the agent's configured LLM model name.
114-
115-
Returns:
116-
The model name from agent settings (e.g., 'gpt-4o-2024-11-20'),
117-
or None if no model is configured.
118-
"""
119-
...
120-
121-
122101
class ExecutionSpanExporter(SpanExporter):
123102
"""Custom exporter that stores spans grouped by execution ids."""
124103

src/uipath/_cli/cli_eval.py

Lines changed: 3 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
from uipath.runtime import (
1111
UiPathRuntimeContext,
1212
UiPathRuntimeFactoryRegistry,
13-
UiPathRuntimeProtocol,
1413
UiPathRuntimeSchema,
1514
)
1615

@@ -19,7 +18,6 @@
1918
from uipath._cli._evals._models._evaluation_set import EvaluationSet
2019
from uipath._cli._evals._progress_reporter import StudioWebProgressReporter
2120
from uipath._cli._evals._runtime import (
22-
LLMAgentRuntimeProtocol,
2321
UiPathEvalContext,
2422
)
2523
from uipath._cli._evals._telemetry import EvalTelemetrySubscriber
@@ -70,34 +68,7 @@ def setup_reporting_prereq(no_report: bool) -> bool:
7068
return True
7169

7270

73-
def _find_agent_model_in_runtime(runtime: UiPathRuntimeProtocol) -> str | None:
74-
"""Recursively search for get_agent_model in runtime and its delegates.
75-
76-
Runtimes may be wrapped (e.g., ResumableRuntime wraps TelemetryWrapper
77-
which wraps the base runtime). This method traverses the wrapper chain
78-
to find a runtime that implements LLMAgentRuntimeProtocol.
79-
80-
Args:
81-
runtime: The runtime to check (may be a wrapper)
82-
83-
Returns:
84-
The model name if found, None otherwise.
85-
"""
86-
# Check if this runtime implements the protocol
87-
if isinstance(runtime, LLMAgentRuntimeProtocol):
88-
return runtime.get_agent_model()
89-
90-
# Check for delegate property (used by UiPathResumableRuntime, TelemetryRuntimeWrapper)
91-
delegate = getattr(runtime, "delegate", None) or getattr(runtime, "_delegate", None)
92-
if delegate is not None:
93-
return _find_agent_model_in_runtime(delegate)
94-
95-
return None
96-
97-
98-
async def _get_agent_model(
99-
runtime: UiPathRuntimeProtocol, schema: UiPathRuntimeSchema
100-
) -> str | None:
71+
async def _get_agent_model(schema: UiPathRuntimeSchema) -> str | None:
10172
"""Get agent model from the runtime schema metadata.
10273
10374
The model is read from schema.metadata["settings"]["model"] which is
@@ -113,12 +84,7 @@ async def _get_agent_model(
11384
if model:
11485
logger.debug(f"Got agent model from schema.metadata: {model}")
11586
return model
116-
117-
# Fallback to protocol-based approach for backwards compatibility
118-
model = _find_agent_model_in_runtime(runtime)
119-
if model:
120-
logger.debug(f"Got agent model from runtime protocol: {model}")
121-
return model
87+
return None
12288
except Exception:
12389
return None
12490

@@ -395,7 +361,7 @@ async def execute_eval():
395361
eval_context.evaluators = await EvalHelpers.load_evaluators(
396362
resolved_eval_set_path,
397363
eval_context.evaluation_set,
398-
await _get_agent_model(runtime, eval_context.runtime_schema),
364+
await _get_agent_model(eval_context.runtime_schema),
399365
)
400366

401367
# Runtime is not required anymore.

tests/cli/eval/test_eval_runtime_metadata.py

Lines changed: 4 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
"""Tests for UiPathEvalRuntime metadata loading functionality.
22
33
This module tests:
4-
- _ensure_metadata_loaded() - single runtime creation for both schema and agent model
54
- _get_agent_model() - cached agent model retrieval
65
- get_schema() - cached schema retrieval
7-
- _find_agent_model_in_runtime() - recursive delegate traversal
8-
- LLMAgentRuntimeProtocol - protocol implementation detection
96
"""
107

118
import uuid
@@ -26,12 +23,10 @@
2623
from uipath.runtime.schema import UiPathRuntimeSchema
2724

2825
from uipath._cli._evals._runtime import (
29-
LLMAgentRuntimeProtocol,
3026
UiPathEvalContext,
3127
UiPathEvalRuntime,
3228
)
3329
from uipath._cli.cli_eval import (
34-
_find_agent_model_in_runtime,
3530
_get_agent_model,
3631
)
3732
from uipath._events._event_bus import EventBus
@@ -80,16 +75,6 @@ async def dispose(self) -> None:
8075
pass
8176

8277

83-
class AgentModelRuntime(BaseTestRuntime):
84-
"""Test runtime that implements LLMAgentRuntimeProtocol."""
85-
86-
def __init__(self, model: str | None = "gpt-4o-2024-11-20"):
87-
self._model = model
88-
89-
def get_agent_model(self) -> str | None:
90-
return self._model
91-
92-
9378
class WrapperRuntime(BaseTestRuntime):
9479
"""Test runtime that wraps another runtime (like UiPathResumableRuntime)."""
9580

@@ -136,123 +121,38 @@ async def dispose(self) -> None:
136121
pass
137122

138123

139-
class TestLLMAgentRuntimeProtocol:
140-
"""Tests for LLMAgentRuntimeProtocol detection."""
141-
142-
def test_protocol_detects_implementing_class(self):
143-
"""Test that protocol correctly identifies implementing classes."""
144-
runtime = AgentModelRuntime("gpt-4")
145-
assert isinstance(runtime, LLMAgentRuntimeProtocol)
146-
147-
def test_protocol_rejects_non_implementing_class(self):
148-
"""Test that protocol correctly rejects non-implementing classes."""
149-
runtime = BaseTestRuntime()
150-
assert not isinstance(runtime, LLMAgentRuntimeProtocol)
151-
152-
def test_protocol_rejects_wrapper_without_method(self):
153-
"""Test that wrapper without get_agent_model is not detected."""
154-
inner = AgentModelRuntime("gpt-4")
155-
wrapper = WrapperRuntime(inner)
156-
assert not isinstance(wrapper, LLMAgentRuntimeProtocol)
157-
158-
159-
class TestFindAgentModelInRuntime:
160-
"""Tests for _find_agent_model_in_runtime recursive search."""
161-
162-
def test_finds_model_in_direct_runtime(self):
163-
"""Test finding agent model directly on runtime."""
164-
runtime = AgentModelRuntime("gpt-4o")
165-
result = _find_agent_model_in_runtime(runtime)
166-
assert result == "gpt-4o"
167-
168-
def test_finds_model_in_wrapped_runtime(self):
169-
"""Test finding agent model through wrapper's delegate."""
170-
inner = AgentModelRuntime("claude-3")
171-
wrapper = WrapperRuntime(inner)
172-
result = _find_agent_model_in_runtime(wrapper)
173-
assert result == "claude-3"
174-
175-
def test_finds_model_in_deeply_wrapped_runtime(self):
176-
"""Test finding agent model through multiple wrapper layers."""
177-
inner = AgentModelRuntime("gpt-4-turbo")
178-
wrapper1 = WrapperRuntime(inner)
179-
wrapper2 = WrapperRuntime(wrapper1)
180-
result = _find_agent_model_in_runtime(wrapper2)
181-
assert result == "gpt-4-turbo"
182-
183-
def test_finds_model_via_private_delegate(self):
184-
"""Test finding agent model through _delegate attribute."""
185-
inner = AgentModelRuntime("gemini-pro")
186-
wrapper = PrivateDelegateRuntime(inner)
187-
result = _find_agent_model_in_runtime(wrapper)
188-
assert result == "gemini-pro"
189-
190-
def test_returns_none_when_no_model(self):
191-
"""Test returns None when no runtime implements the protocol."""
192-
runtime = BaseTestRuntime()
193-
result = _find_agent_model_in_runtime(runtime)
194-
assert result is None
195-
196-
def test_returns_none_for_none_model(self):
197-
"""Test returns None when runtime returns None for model."""
198-
runtime = AgentModelRuntime(None)
199-
result = _find_agent_model_in_runtime(runtime)
200-
assert result is None
201-
202-
203124
class TestGetAgentModel:
204125
"""Tests for _get_agent_model function."""
205126

206127
@pytest.mark.asyncio
207128
async def test_returns_agent_model(self):
208129
"""Test that _get_agent_model returns the correct model from schema."""
209-
runtime = AgentModelRuntime("gpt-4o-2024-11-20")
210130
schema = MockRuntimeSchema()
211131
schema.metadata = {"settings": {"model": "gpt-4o-2024-11-20"}}
212132

213-
model = await _get_agent_model(runtime, schema)
133+
model = await _get_agent_model(schema)
214134
assert model == "gpt-4o-2024-11-20"
215135

216136
@pytest.mark.asyncio
217137
async def test_returns_none_when_no_model(self):
218138
"""Test that _get_agent_model returns None when runtime has no model."""
219-
runtime = BaseTestRuntime()
220139
schema = MockRuntimeSchema()
221140

222-
model = await _get_agent_model(runtime, schema)
141+
model = await _get_agent_model(schema)
223142
assert model is None
224143

225144
@pytest.mark.asyncio
226145
async def test_returns_model_consistently(self):
227146
"""Test that _get_agent_model returns consistent results."""
228-
runtime = AgentModelRuntime("consistent-model")
229147
schema = MockRuntimeSchema()
230148
schema.metadata = {"settings": {"model": "consistent-model"}}
231149

232150
# Multiple calls should return the same value
233-
model1 = await _get_agent_model(runtime, schema)
234-
model2 = await _get_agent_model(runtime, schema)
151+
model1 = await _get_agent_model(schema)
152+
model2 = await _get_agent_model(schema)
235153

236154
assert model1 == model2 == "consistent-model"
237155

238-
@pytest.mark.asyncio
239-
async def test_handles_exception_gracefully(self, monkeypatch):
240-
"""Test that _get_agent_model returns None when _find_agent_model_in_runtime raises exception."""
241-
runtime = BaseTestRuntime()
242-
schema = MockRuntimeSchema()
243-
244-
# Mock _find_agent_model_in_runtime to raise an exception
245-
def mock_find_agent_model_error(r):
246-
raise RuntimeError("Unexpected error during model lookup")
247-
248-
monkeypatch.setattr(
249-
"uipath._cli.cli_eval._find_agent_model_in_runtime",
250-
mock_find_agent_model_error,
251-
)
252-
253-
model = await _get_agent_model(runtime, schema)
254-
assert model is None
255-
256156

257157
class TestGetSchema:
258158
"""Tests for get_schema method."""
@@ -314,25 +214,3 @@ async def create_runtime():
314214
# Should be the same object
315215
assert schema1 is schema2
316216
assert schema1.file_path == schema2.file_path == "test.py"
317-
318-
319-
class TestWrappedRuntimeModelResolution:
320-
"""Tests for model resolution through realistic wrapper chains."""
321-
322-
def test_resolves_model_through_resumable_telemetry_chain(self):
323-
"""Test model resolution through ResumableRuntime -> TelemetryWrapper -> BaseRuntime chain.
324-
325-
This mimics the real wrapper chain:
326-
UiPathResumableRuntime -> TelemetryRuntimeWrapper -> AgentsLangGraphRuntime
327-
"""
328-
# Base runtime with model
329-
base_runtime = AgentModelRuntime("gpt-4o-from-agent-json")
330-
331-
# Simulate TelemetryRuntimeWrapper
332-
telemetry_wrapper = WrapperRuntime(base_runtime)
333-
334-
# Simulate UiPathResumableRuntime
335-
resumable_runtime = WrapperRuntime(telemetry_wrapper)
336-
337-
model = _find_agent_model_in_runtime(resumable_runtime)
338-
assert model == "gpt-4o-from-agent-json"

0 commit comments

Comments
 (0)