Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions google/genai/_interactions/_legacy_lyria.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,13 +103,22 @@
}


def _is_legacy_lyria_model(model: object) -> bool:
if not isinstance(model, str):
return False
return any(
model == known_model or model.endswith(f"/models/{known_model}")
for known_model in LEGACY_LYRIA_MODELS
)


def is_legacy_lyria_request(*, is_vertex: bool, model: object) -> bool:
"""Return True iff the (client, model) combination needs the shim active.

Used at request issue time (in the resource layer) to decide whether to
pick the `LegacyLyriaInteractionStream` subclass for streaming requests.
"""
return bool(is_vertex) and isinstance(model, str) and model in LEGACY_LYRIA_MODELS
return bool(is_vertex) and _is_legacy_lyria_model(model)


def is_legacy_lyria_response_body(data: object) -> bool:
Expand All @@ -125,7 +134,7 @@ def is_legacy_lyria_response_body(data: object) -> bool:
return False
typed_data: Dict[str, Any] = cast("Dict[str, Any]", data)
model = typed_data.get("model")
return isinstance(model, str) and model in LEGACY_LYRIA_MODELS
return _is_legacy_lyria_model(model)


def maybe_remap_legacy_sse_event(data: Dict[str, Any]) -> Dict[str, Any]:
Expand Down
6 changes: 3 additions & 3 deletions google/genai/_interactions/types/interaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,7 @@ def output_text(self) -> str:
@property
def output_image(self) -> Optional[ImageContent]:
"""The last image generated by the model in response to the current request."""
for step in reversed(self.steps):
for step in reversed(self.steps or []):
if isinstance(step, UserInputStep):
break
if isinstance(step, ModelOutputStep) and step.content:
Expand All @@ -442,7 +442,7 @@ def output_image(self) -> Optional[ImageContent]:
@property
def output_audio(self) -> Optional[AudioContent]:
"""The last audio generated by the model in response to the current request."""
for step in reversed(self.steps):
for step in reversed(self.steps or []):
if isinstance(step, UserInputStep):
break
if isinstance(step, ModelOutputStep) and step.content:
Expand All @@ -454,7 +454,7 @@ def output_audio(self) -> Optional[AudioContent]:
@property
def output_video(self) -> Optional[VideoContent]:
"""The last video generated by the model in response to the current request."""
for step in reversed(self.steps):
for step in reversed(self.steps or []):
if isinstance(step, UserInputStep):
break
if isinstance(step, ModelOutputStep) and step.content:
Expand Down
68 changes: 68 additions & 0 deletions google/genai/tests/interactions/test_legacy_lyria.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

from ..._interactions._legacy_lyria import (
is_legacy_lyria_request,
is_legacy_lyria_response_body,
)
from ..._interactions.types.interaction import Interaction


_LYRIA_MODEL_PATH = (
"projects/123/locations/global/publishers/google/models/lyria-3-clip-preview"
)


def test_legacy_lyria_vertex_model_path_rewrites_outputs_to_steps():
interaction = Interaction.model_validate(
{
"id": "interaction-1",
"created": "2026-01-01T00:00:00Z",
"updated": "2026-01-01T00:00:01Z",
"status": "completed",
"model": _LYRIA_MODEL_PATH,
"outputs": [
{
"type": "audio",
"data": "abc",
"mime_type": "audio/wav",
}
],
}
)

assert len(interaction.steps) == 1
assert interaction.output_audio is not None
assert interaction.output_audio.data == "abc"
assert interaction.model_extra is None or "outputs" not in interaction.model_extra


def test_legacy_lyria_model_path_detection():
assert is_legacy_lyria_request(is_vertex=True, model=_LYRIA_MODEL_PATH)
assert is_legacy_lyria_response_body({"model": _LYRIA_MODEL_PATH})


def test_output_accessors_tolerate_missing_steps():
interaction = Interaction.construct(
id="interaction-1",
created="2026-01-01T00:00:00Z",
updated="2026-01-01T00:00:01Z",
status="completed",
steps=None,
)

assert interaction.output_image is None
assert interaction.output_audio is None
assert interaction.output_video is None