Skip to content

Commit feb3307

Browse files
committed
fix(eval): pass simulation instructions to trajectory evaluator
1 parent 4a1489b commit feb3307

3 files changed

Lines changed: 127 additions & 0 deletions

File tree

packages/uipath/src/uipath/eval/runtime/runtime.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1022,6 +1022,9 @@ async def run_evaluator(
10221022
agent_output=output_data,
10231023
agent_trace=execution_output.spans,
10241024
expected_agent_behavior=eval_item.expected_agent_behavior,
1025+
simulation_instructions=eval_item.mocking_strategy.prompt
1026+
if isinstance(eval_item.mocking_strategy, LLMMockingStrategy)
1027+
else "",
10251028
)
10261029

10271030
result = await evaluator.validate_and_evaluate_criteria(

packages/uipath/tests/cli/eval/test_eval_tracing_integration.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@
1212
import pytest
1313

1414
from uipath.eval.evaluators import BaseEvaluator
15+
from uipath.eval.mocks._types import LLMMockingStrategy
1516
from uipath.eval.models import NumericEvaluationResult
1617
from uipath.eval.models.evaluation_set import EvaluationSet
18+
from uipath.eval.models.models import AgentExecution
1719
from uipath.eval.runtime import UiPathEvalContext, UiPathEvalRuntime
1820
from uipath.runtime.schema import UiPathRuntimeSchema
1921

@@ -402,6 +404,64 @@ async def test_run_evaluator_creates_evaluator_span(
402404
assert span["attributes"]["evaluator_name"] == "AccuracyEvaluator"
403405
assert span["attributes"]["eval_item_id"] == "eval-item-456"
404406

407+
@pytest.mark.asyncio
408+
async def test_run_evaluator_passes_simulation_instructions(
409+
self,
410+
mock_trace_manager: MagicMock,
411+
mock_factory: MagicMock,
412+
mock_event_bus: MagicMock,
413+
mock_execution_output: MagicMock,
414+
) -> None:
415+
"""Test that trajectory evaluators receive simulation instructions."""
416+
context = create_eval_context(
417+
eval_set="test.json",
418+
entrypoint="main.py:main",
419+
)
420+
421+
runtime = UiPathEvalRuntime(
422+
context=context,
423+
factory=mock_factory,
424+
trace_manager=mock_trace_manager,
425+
event_bus=mock_event_bus,
426+
)
427+
428+
eval_item = MagicMock()
429+
eval_item.id = "eval-item-with-simulation"
430+
eval_item.name = "Simulated item"
431+
eval_item.inputs = {"input": "test"}
432+
eval_item.expected_agent_behavior = "Agent should use the simulated tool"
433+
eval_item.mocking_strategy = LLMMockingStrategy(
434+
prompt="Return mocked API responses for the tool calls",
435+
tools_to_simulate=[],
436+
)
437+
438+
evaluator = MagicMock(spec=BaseEvaluator)
439+
evaluator.id = "trajectory-evaluator"
440+
evaluator.name = "TrajectoryEvaluator"
441+
442+
async def capture_agent_execution(
443+
agent_execution: AgentExecution,
444+
evaluation_criteria: object,
445+
) -> NumericEvaluationResult:
446+
assert (
447+
agent_execution.simulation_instructions
448+
== "Return mocked API responses for the tool calls"
449+
)
450+
return NumericEvaluationResult(score=1.0)
451+
452+
evaluator.validate_and_evaluate_criteria = AsyncMock(
453+
side_effect=capture_agent_execution
454+
)
455+
456+
await runtime.run_evaluator(
457+
evaluator=evaluator,
458+
execution_output=mock_execution_output,
459+
eval_item=eval_item,
460+
evaluation_criteria=None,
461+
)
462+
463+
evaluator.validate_and_evaluate_criteria.assert_awaited_once()
464+
405465
@pytest.mark.asyncio
406466
async def test_multiple_evaluators_create_multiple_spans(
407467
self,

packages/uipath/tests/evaluators/test_evaluator_methods.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1728,6 +1728,70 @@ async def mock_chat_completions(*args: Any, **kwargs: Any) -> Any:
17281728
assert isinstance(result, NumericEvaluationResult)
17291729
assert result.score == 0.9
17301730

1731+
@pytest.mark.asyncio
1732+
async def test_llm_trajectory_replaces_all_prompt_placeholders(
1733+
self, sample_agent_execution: AgentExecution, mocker: MockerFixture
1734+
) -> None:
1735+
"""Test trajectory prompt interpolation for all built-in placeholders."""
1736+
captured_prompt = ""
1737+
1738+
mock_tool_call = mocker.MagicMock()
1739+
mock_tool_call.id = "call_1"
1740+
mock_tool_call.name = "submit_evaluation"
1741+
mock_tool_call.arguments = {
1742+
"score": 90,
1743+
"justification": "The agent followed the expected behavior",
1744+
}
1745+
1746+
mock_response = mocker.MagicMock()
1747+
mock_response.choices = [
1748+
mocker.MagicMock(
1749+
message=mocker.MagicMock(content=None, tool_calls=[mock_tool_call])
1750+
)
1751+
]
1752+
1753+
async def mock_chat_completions(*args: Any, **kwargs: Any) -> Any:
1754+
nonlocal captured_prompt
1755+
captured_prompt = kwargs["messages"][-1]["content"]
1756+
return mock_response
1757+
1758+
mock_llm_instance = mocker.MagicMock()
1759+
mock_llm_instance.chat_completions = mock_chat_completions
1760+
1761+
mocker.patch("uipath.eval.evaluators.llm_as_judge_evaluator.UiPath")
1762+
mocker.patch(
1763+
"uipath.eval.evaluators.llm_as_judge_evaluator.UiPathLlmChatService",
1764+
return_value=mock_llm_instance,
1765+
)
1766+
1767+
config = {
1768+
"name": "LlmTrajectoryTest",
1769+
"prompt": (
1770+
"input={{UserOrSyntheticInput}}\n"
1771+
"instructions={{SimulationInstructions}}\n"
1772+
"expected={{ExpectedAgentBehavior}}\n"
1773+
"history={{AgentRunHistory}}"
1774+
),
1775+
"model": "gpt-4",
1776+
}
1777+
evaluator = LLMJudgeTrajectoryEvaluator.model_validate(
1778+
{"evaluatorConfig": config, "id": str(uuid.uuid4())}
1779+
)
1780+
agent_execution = sample_agent_execution.model_copy(
1781+
update={"simulation_instructions": "Mock the backend API response"}
1782+
)
1783+
criteria = TrajectoryEvaluationCriteria(
1784+
expected_agent_behavior="Agent should respond helpfully"
1785+
)
1786+
1787+
result = await evaluator.evaluate(agent_execution, criteria)
1788+
1789+
assert isinstance(result, NumericEvaluationResult)
1790+
assert "{{" not in captured_prompt
1791+
assert "Agent should respond helpfully" in captured_prompt
1792+
assert "Mock the backend API response" in captured_prompt
1793+
assert "{'input': 'Test input'}" in captured_prompt
1794+
17311795
@pytest.mark.asyncio
17321796
async def test_llm_trajectory_validate_and_evaluate_criteria(
17331797
self, sample_agent_execution: AgentExecution, mocker: MockerFixture

0 commit comments

Comments
 (0)