fix: DeepEvalAdapter to extract multi-turn conversations from service-normalized SESSION format - #622
Open
ybdarrenwang wants to merge 1 commit into
Open
fix: DeepEvalAdapter to extract multi-turn conversations from service-normalized SESSION format#622ybdarrenwang wants to merge 1 commit into
ybdarrenwang wants to merge 1 commit into
Conversation
…normalized SESSION format
ybdarrenwang
requested a deployment
to
manual-approval
August 5, 2026 17:16 — with
GitHub Actions
Waiting
ybdarrenwang
requested a deployment
to
manual-approval
August 5, 2026 17:16 — with
GitHub Actions
Waiting
ybdarrenwang
requested a deployment
to
manual-approval
August 5, 2026 17:16 — with
GitHub Actions
Waiting
ybdarrenwang
requested a deployment
to
manual-approval
August 5, 2026 17:16 — with
GitHub Actions
Waiting
ybdarrenwang
requested a deployment
to
manual-approval
August 5, 2026 17:16 — with
GitHub Actions
Waiting
ybdarrenwang
requested a deployment
to
manual-approval
August 5, 2026 17:16 — with
GitHub Actions
Waiting
ybdarrenwang
requested a deployment
to
manual-approval
August 5, 2026 17:16 — with
GitHub Actions
Waiting
ybdarrenwang
requested a deployment
to
manual-approval
August 5, 2026 17:16 — with
GitHub Actions
Waiting
ybdarrenwang
requested a deployment
to
manual-approval
August 5, 2026 17:16 — with
GitHub Actions
Waiting
stone-coding
reviewed
Aug 5, 2026
stone-coding
left a comment
Contributor
There was a problem hiding this comment.
Reviewed PR. The fix correctly handles the SESSION-level service-normalized format where multiple turns are collapsed into span_events[*].body. My original _extract_from_service_format() only covered single-turn gen_ai events.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Background: PR #568
PR #568 (
feat: third-party eval metrics adapter (DeepEval + Autoevals) with strands-evals mappers) introduced theDeepEvalAdapter— a generic wrapper that takes any DeepEval metric and runs it inside an AgentCore Lambda evaluator. The adapter uses strands-evals mappers to auto-detect span formats, extract fields, and construct the appropriate DeepEval test case (LLMTestCasefor single-turn,ConversationalTestCasefor multi-turn).The Bug
The adapter's multi-turn extraction path relies on
_session_to_span_map_result()which builds theturnslist from multipleAgentInvocationSpanobjects — one per trace/invocation in the session. This works when the strands-evals mapper successfully parses spans into theSession → Trace → AgentInvocationSpanhierarchy (i.e., when each conversation turn arrives as a separate span in the CloudWatch split format).However, the AgentCore evaluation service normalizes spans before invoking Lambda at SESSION level. It collapses all ADOT span documents sharing the same
session.idinto a single span with multiplespan_eventsentries — eachspan_event.bodycontains one conversation turn'sinput.messagesandoutput.messages.In this service-normalized format:
Sessionwith only oneAgentInvocationSpan(because there's only one physical span)_session_to_span_map_result()producesturnswith only 2 entries (1 user + 1 assistant from the single AgentInvocationSpan)turns if len(turns) > 2 else Nonefilters this toNone_build_conversational_test_case()seesresult.turnsis empty and raises: "Multi-turn metric requires multiple conversation turns but only a single turn was found"The fallback
_extract_from_service_format()only handled single-turn extraction from gen_ai semantic convention events — it had no logic for parsingspan_events[*].body.Impact: ALL multi-turn metrics (GoalAccuracy, RoleAdherence, ToolUse, ConversationCompleteness, KnowledgeRetention, TopicAdherence, TurnContextualPrecision, TurnContextualRecall, TurnFaithfulness) failed when invoked through the adapter at SESSION level. This forced users to bypass the adapter entirely and write custom conversational-template handlers.
The Fix
The fix extends
_extract_from_service_format()inregistry.pyto handle the service-normalized SESSION format:New helper
_extract_message_text()— parses the nested service message structure ({content: {content: [{text: ...}]}}and the{content: {message: [{text: ...}]}}variant) into plain text.Multi-turn extraction in
_extract_from_service_format()— added a new code path that runs first:span_eventswith ≥ 1 entryspan_event.body, extracts user input and assistant output frombody.input.messages/body.output.messagesturnslist (withturns=Nonewhen only 1 turn pair exists, matching existing semantics)SpanMapResultwith populatedturnsfieldTest expectation update — Two existing error-handling tests (
test_15_unrecognized_scope_deepeval,test_02_unrecognized_scopein autoevals) changed fromFIELD_EXTRACTION_ERRORtoMISSING_REQUIRED_FIELDbecause the mapper now succeeds in parsing the span (no extraction error) but finds empty input/output fields (triggering the more specific "missing required field" error).The fix is backward-compatible: the single-turn gen_ai events path remains unchanged as a fallback, and the multi-turn path only activates when
span_eventsentries are present.Testing
Added 9 new test cases:
test_span_mappers.py—TestServiceNormalizedMultiTurn(6 tests):{content: {content: [{text: ...}]}}varianttest_adapter.py—TestDeepEvalAdapterServiceNormalizedMultiTurn(3 tests):Issue #, if available:
Description of changes:
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.