Skip to content

[AI-793] Polish vision_agents.testing: conversation judge, assertions, Agent support - #652

Open
darkoatanasovski wants to merge 3 commits into
mainfrom
AI-793
Open

darkoatanasovski wants to merge 3 commits into
mainfrom
AI-793

Conversation

@darkoatanasovski

Copy link
Copy Markdown

Why

The eval package in agents-core/vision_agents/testing could only judge one ChatMessageEvent against one intent, and TestSession only wrapped a bare LLM. Real agent behaviour is a sequence of turns, tool calls and tool results, and production agents carry instructions and MCP tools that tests should exercise too. _session.py also had two known TODOs: tool observation monkeypatched the function registry (unsafe under overlapping simple_response calls) and parallel calls of the same tool could not be paired with their outputs.

Linear: https://linear.app/stream/issue/AI-793/polish-vision-agentstesting-conversation-judge-assertions-agent

Changes

  • LLMJudge.evaluate_conversation(events, criteria, instructions=...) judges a whole transcript (list of RunEvent) and returns a JudgeVerdict with pass/fail, mean score and a CriterionVerdict (pass/fail, score, reason) per criterion. Criteria can be Criterion objects or plain strings. The old evaluate(event, intent) is kept and routed through the same path.
  • Built-in criteria: SAY_DO_CONSISTENCY, STAYS_IN_SCOPE, CONCISE, RESPONDS_IN_USER_LANGUAGE. Each has a real-LLM test with a passing and a failing transcript (@pytest.mark.integration, Gemini), plus LLM-free unit tests for prompt building and verdict parsing.
  • TestResponse.assert_function_not_called(name, arguments=...) and assert_function_call_order([...]). Failure messages list the actual calls and, for order checks, the actual order.
  • TestSession(agent=agent) wraps an Agent: the agent's instructions are used and its MCP servers are connected on start() so their tools are registered on the LLM and captured like any other tool call.
  • Tool calls are captured from the LLM's ToolStartEvent / ToolEndEvent instead of wrapping registry functions. Turns are separated with a ContextVar, so overlapping simple_response calls no longer interfere with each other or with the registry, and parallel calls of the same tool are paired via tool_call_id.
  • Each turn's events now start with the user ChatMessageEvent, and TestSession.transcript exposes the accumulated conversation for the judge.
  • Pytest fixtures test_session, judge, agent_llm, agent_instructions, judge_llm in vision_agents.testing.fixtures (enable with pytest_plugins = ["vision_agents.testing.fixtures"]). The simple agent example uses them and judges conversations with the built-in criteria.

Note: the Gemini-backed integration tests (built-in criteria and examples/01_simple_agent_example) need GOOGLE_API_KEY and were not run locally; the unit suite, ruff and mypy pass via dev.py check. The nine plugins/anthropic unit-test errors seen locally are pre-existing and environmental (the Anthropic SDK reads a credentials profile path when no ANTHROPIC_API_KEY is set, tripping blockbuster); they pass with the key present.

…n_agents.testing

LLMJudge now evaluates a whole transcript (list of RunEvent) against named
criteria and returns a per-criterion verdict with pass/fail, score and
reason. Four built-in criteria ship: say-do consistency, stays in scope,
concise, and responds in the user's language. The single-message
evaluate(event, intent) API is kept and routed through the same path.

TestResponse gains assert_function_not_called and assert_function_call_order,
both listing the actual calls on failure.

TestSession can wrap an Agent so the agent's instructions and MCP tools are
exercised as in production. Tool calls are now captured from the LLM's
ToolStartEvent/ToolEndEvent instead of monkeypatching the function
registry, which removes the two TODOs: overlapping simple_response calls no
longer touch registry restoration (turns are separated via a ContextVar),
and parallel calls of the same tool pair by tool_call_id. Each turn's events
start with the user message and the session exposes the accumulated
transcript.

Pytest fixtures (test_session, judge, agent_llm, agent_instructions,
judge_llm) live in vision_agents.testing.fixtures; the simple agent example
uses them together with the conversation judge.
@coderabbitai

coderabbitai Bot commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

📝 Walkthrough

Walkthrough

The testing package now evaluates complete event transcripts against multiple criteria and returns per-criterion scores. TestSession supports agents, MCP lifecycle management, multi-turn transcripts, and event-based tool capture. TestResponse adds assertions for absent calls and call order. Pytest fixtures are registered project-wide, and the example tests use them. New tests cover judge parsing, fixtures, session behavior, tool assertions, concurrency, and MCP integration.

Priority: ⬇️ Low

Merge Risk: 🟡 Moderate · up to 3ed6a

Existing consumers using evaluate may receive failing verdicts from unchanged judge responses; preserve compatibility before merging.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 043c6d2e-05e2-4df3-937c-1e2bfd34ac88

📥 Commits

Reviewing files that changed from the base of the PR and between ba2fbf1 and 276902b.

📒 Files selected for processing (14)
  • agents-core/vision_agents/testing/__init__.py
  • agents-core/vision_agents/testing/_judge.py
  • agents-core/vision_agents/testing/_run_result.py
  • agents-core/vision_agents/testing/_session.py
  • agents-core/vision_agents/testing/fixtures.py
  • conftest.py
  • examples/01_simple_agent_example/README.md
  • examples/01_simple_agent_example/conftest.py
  • examples/01_simple_agent_example/test_simple_agent.py
  • tests/test_testing/fake_llms.py
  • tests/test_testing/test_eval.py
  • tests/test_testing/test_fixtures.py
  • tests/test_testing/test_judge.py
  • tests/test_testing/test_session.py

Included review availability: Your plan provides up to 8 included reviews per hour; 7 remain after this review.

Comment thread agents-core/vision_agents/testing/_session.py Outdated
Comment thread examples/01_simple_agent_example/test_simple_agent.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to GitHub limitations.

⚠️ Outside diff range comments (1)

🟠 Major · Preserve the legacy response path in evaluate(). · _judge.py:162-218

agents-core/vision_agents/testing/_judge.py:162-218
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Preserve the legacy response path in evaluate(). LLMJudge.evaluate() remains part of the public Judge protocol, but it now delegates to evaluate_conversation(), whose _parse_verdict() requires a top-level results list. The previous implementation documented and parsed {"verdict": "pass" or "fail", "reason": "..."}. A caller or custom judge LLM that still returns that contract now receives JudgeVerdict(success=False, reason="Missing 'results' list ...") for every evaluation. No changelog entry documents this breaking change. Keep the legacy prompt/parser in evaluate(), or add an explicit compatibility branch.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: dc42db6a-6a9a-46e2-9aea-63df8bbd0232

📥 Commits

Reviewing files that changed from the base of the PR and between 276902b and 3ed6acd.

📒 Files selected for processing (2)
  • agents-core/vision_agents/testing/_session.py
  • examples/01_simple_agent_example/test_simple_agent.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • agents-core/vision_agents/testing/_session.py

Included review availability: Your plan provides up to 8 included reviews per hour; 7 remain after this review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant