fix(google_genai): list skipped results after every answer to their turn

When the caller split the answers to one model turn across two user turns, the
skipped result went between them and GPT read it as the second answer. A
skipped result now follows the last user turn that answers its model turn
This commit is contained in:
mateo-berri 2026-11-10 05:57:04 -08:00
parent 085b603fee
commit f039b97fa6
2 changed files with 23 additions and 11 deletions

View file

@ -423,16 +423,8 @@ def _genai_turn_messages(
)
def _answered_by_next_turn(turns: Sequence[_GenAITurn], index: int) -> bool:
return index + 1 < len(turns) and bool(turns[index + 1].responses)
def _skipped_results_after(
turns: Sequence[_GenAITurn], skipped_results: Sequence[tuple[ChatCompletionToolMessage, ...]], index: int
) -> tuple[ChatCompletionToolMessage, ...]:
previous: Final = skipped_results[index - 1] if index > 0 and _answered_by_next_turn(turns, index - 1) else ()
own: Final = () if _answered_by_next_turn(turns, index) else skipped_results[index]
return (*previous, *own)
def _last_answering_turn(turns: Sequence[_GenAITurn], index: int) -> int:
return next((later for later in range(index + 1, len(turns)) if not turns[later].responses), len(turns)) - 1
def _genai_contents_messages(contents: Sequence[object]) -> tuple[AllMessageValues, ...]:
@ -458,6 +450,9 @@ def _genai_contents_messages(contents: Sequence[object]) -> tuple[AllMessageValu
)
for turn, (start, end) in zip(turns, pairwise(call_starts))
)
skipped_after: Final = MappingProxyType(
{_last_answering_turn(turns, index): results for index, results in enumerate(skipped_results) if results}
)
return tuple(
chain.from_iterable(
(
@ -466,7 +461,7 @@ def _genai_contents_messages(contents: Sequence[object]) -> tuple[AllMessageValu
call_ids[call_starts[index] : call_starts[index + 1]],
response_ids[response_starts[index] : response_starts[index + 1]],
),
*_skipped_results_after(turns, skipped_results, index),
*skipped_after.get(index, ()),
)
for index, turn in enumerate(turns)
)

View file

@ -699,6 +699,23 @@ def _ids_answered_right_after(messages: list[dict[str, object]], index: int) ->
frozenset({"paris", "rome"}),
id="parallel_call_left_out_of_the_answers",
),
pytest.param(
[
{
"role": "model",
"parts": [
_tagged_call("get_weather", "paris"),
_tagged_call("get_weather", "rome"),
_tagged_call("get_weather", "oslo"),
],
},
{"role": "user", "parts": [_tagged_response("get_weather", "paris")]},
{"role": "user", "parts": [_tagged_response("get_weather", "rome")]},
{"role": "model", "parts": [{"text": "Oslo did not answer"}]},
],
frozenset({"paris", "rome"}),
id="answers_split_across_user_turns",
),
],
)
def test_a_call_a_later_turn_left_unanswered_gets_one_skipped_result_and_the_others_keep_their_answers(