From 975d16a3cea49282137dd2a901b219820a641b64 Mon Sep 17 00:00:00 2001 From: Mark Daoust Date: Thu, 14 May 2026 22:55:49 -0700 Subject: [PATCH] feat: interaction.{output_text,output_image,output_audio,output_video} Add SDK methods for shortcut properties on the response object to access the output generated in response to the current request, without forcing developers to iterate through the full steps timeline or filter out previous conversation history: interaction.output_text: Concatenated last consecutive text generated by the model in response to the current request. .output_image: The last image generated by the model in response to the current request. .output_audio: The last audio clip generated by the model in response to the current request. .output_video: The last video generated by the model in response to the current request. PiperOrigin-RevId: 915804808 --- .../genai/_interactions/types/interaction.py | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/google/genai/_interactions/types/interaction.py b/google/genai/_interactions/types/interaction.py index 3fe883f92..7ad82eafb 100644 --- a/google/genai/_interactions/types/interaction.py +++ b/google/genai/_interactions/types/interaction.py @@ -33,7 +33,9 @@ from .video_content import VideoContent from .._legacy_lyria import is_legacy_lyria_response_body from .webhook_config import WebhookConfig +from .user_input_step import UserInputStep from .document_content import DocumentContent +from .model_output_step import ModelOutputStep from .dynamic_agent_config import DynamicAgentConfig from .text_response_format import TextResponseFormat from .audio_response_format import AudioResponseFormat @@ -212,3 +214,70 @@ def _coerce_outputs_to_steps(cls, values: Any) -> Any: def _coerce_outputs_to_steps(cls, data: Any) -> Any: coerced, _ = cls._maybe_coerce_outputs(data) return coerced + + @property + def _last_model_output_steps(self) -> List[ModelOutputStep]: + if not self.steps: + return [] + trailing: List[ModelOutputStep] = [] + for step in reversed(self.steps): + if isinstance(step, ModelOutputStep): + trailing.append(step) + else: + break + trailing.reverse() + return trailing + + @property + def output_text(self) -> str: + """Concatenated last consecutive text from the last consecutive model output steps.""" + parts: List[str] = [] + done = False + for step in reversed(self._last_model_output_steps): + if done: + break + if step.content: + for content in reversed(step.content): + if isinstance(content, TextContent): + parts.append(content.text) + else: + done = True + break + parts.reverse() + return "".join(parts) + + @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): + if isinstance(step, UserInputStep): + break + if isinstance(step, ModelOutputStep) and step.content: + for content in reversed(step.content): + if isinstance(content, ImageContent): + return content + return None + + @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): + if isinstance(step, UserInputStep): + break + if isinstance(step, ModelOutputStep) and step.content: + for content in reversed(step.content): + if isinstance(content, AudioContent): + return content + return None + + @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): + if isinstance(step, UserInputStep): + break + if isinstance(step, ModelOutputStep) and step.content: + for content in reversed(step.content): + if isinstance(content, VideoContent): + return content + return None