Live `AsyncSession.send_tool_response` fails with `TypeError` when `FunctionResponse.parts` carries inline bytes
メンテナーはふだん 1 日以内に返信
評価
調査の方向性
Start at AsyncSession.send_tool_response and compare its _common.convert_to_dict/json.dumps path with send_client_content's model_dump(mode='json') serialization. Reproduce the no-key example using t.t_tool_response and a FunctionResponse containing inline bytes; done means nested FunctionResponseBlob.data is base64-encoded and parts remain intact, with the deprecated AsyncSession.send path no longer dropping parts or scheduling.
索引モデルが issue の本文から書いたものです。
説明
Environment details
- Programming language: Python 3.14 (also reproduced on 3.10)
- OS: Linux
- Package version:
google-genai2.25.0
Steps to reproduce
The Live API's function responses accept multimodal parts, and the Gemini 3.x Live models read an image sent that way. For example, gemini-3.8-live and gemini-3.1-flash-live-preview correctly describe a PNG, JPEG or WebP returned from a tool. However, AsyncSession.send_tool_response can't send such a response. It serializes with _common.convert_to_dict, which leaves FunctionResponseBlob.data as raw bytes, and then passes the result to json.dumps:
import asyncio
from google import genai
from google.genai import types
async def main():
client = genai.Client()
tool = types.Tool(function_declarations=[types.FunctionDeclaration(name='take_photo', description='Take a photo.')])
config = types.LiveConnectConfig(response_modalities=['AUDIO'], tools=[tool])
async with client.aio.live.connect(model='gemini-3.8-live', config=config) as session:
await session.send_client_content(
turns=types.Content(role='user', parts=[types.Part(text='Take a photo and describe it.')])
)
async for message in session.receive():
if message.tool_call:
call = message.tool_call.function_calls[0]
await session.send_tool_response(
function_responses=types.FunctionResponse(
id=call.id,
name=call.name,
response={'output': 'Photo taken.'},
parts=[
types.FunctionResponsePart(
inline_data=types.FunctionResponseBlob(data=b'\x89PNG...', mime_type='image/png')
)
],
)
)
break
asyncio.run(main())
TypeError: Object of type bytes is not JSON serializable
A minimal repro that needs no API key:
import json
from google.genai import _common, _transformers as t, types
response = types.FunctionResponse(
id='call_1',
name='take_photo',
response={'output': 'Photo taken.'},
parts=[types.FunctionResponsePart(inline_data=types.FunctionResponseBlob(data=b'\x89PNG', mime_type='image/png'))],
)
json.dumps({'tool_response': _common.convert_to_dict(t.t_tool_response(response), convert_keys=True)})
# TypeError: Object of type bytes is not JSON serializable
Expected behavior
The bytes are base64-encoded, as send_client_content does for Blob.data: it serializes with model_dump(mode='json'), which honors the models' ser_json_bytes='base64'.
Workarounds
-
Serialize the message with the SDK's own types and send it on the session's socket:
message = types.LiveClientMessage(tool_response=types.LiveClientToolResponse(function_responses=[response])) await session._ws.send(message.model_dump_json(by_alias=True, exclude_none=True))This works, but it reaches into the private
_ws. -
types.FunctionResponseBlob.model_construct(data=base64.b64encode(data).decode(), mime_type=...)also gets the message sent, butconvert_to_dictthen emitsPydanticSerializationUnexpectedValue(Expected bytes) on every call.
Suggested fix
Serialize the tool response the way send_client_content does, with model_dump(mode='json', exclude_none=True), before json.dumps, so that nested bytes are base64-encoded.
The deprecated AsyncSession.send(input=FunctionResponse(...)) path has a related problem. It rebuilds each function response from name, response and id only, so it silently drops parts (and scheduling).
- 主要言語
- Python
- スター
- 4k
- フォーク
- 1k
- 平均マージ
- 1日 18時間
- マージ済み PR(30日)
- 52
環境構築
- Dockerfile・Docker Compose ファイルなし
- プルリクエストのテンプレートなし
- コントリビューションガイドを読む
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
googleapis/python-genai のほかの issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 62/100
googleapis/python-genai#3051 ·
メンテナーはふだん 1 日以内に返信
-
[Bug]: Unsubscripted typing.List and typing.Dict crash convert_if_exist_pydantic_model in AFC対応中かも @chauvuusvn が 2 日前に担当しました。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 82/100
googleapis/python-genai#3044 ·
メンテナーはふだん 1 日以内に返信
-
Name collision in google.genai.interactions: triggers.Interaction shadows response model Interaction in static type checkers対応中かも @Venkaiahbabuneelam が 7 日前に担当しました。 オープンpriority: p2 type: bug
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
googleapis/python-genai#3013 · コメント 1 件 · 担当者 1 名 ·
メンテナーはふだん 1 日以内に返信
-
難易度 3/5 1〜2日 初心者へのやさしさ 78/100
googleapis/python-genai#3031 · コメント 2 件 ·
メンテナーはふだん 1 日以内に返信
-
[Live API] gemini-3.8-live-extended-thinking: announces tool call then says "I apologize, but a system error occurred." with no functionCall frame - 61% of tool runs (n=64)対応中かも @Venkaiahbabuneelam が 7 日前に担当しました。 オープンpriority: p2 status:awaiting user response type: bug
googleapis/python-genai#3015 · コメント 1 件 · 担当者 1 名 ·
メンテナーはふだん 1 日以内に返信
googleapis/python-genai の issue をすべて見る
似ている issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 82/100
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 半日 初心者へのやさしさ 70/100
メンテナーはふだん 1 日以内に返信
-
bug
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
Qiskit/qiskit-ibm-runtime#3431 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
[Lesson] A compatibility-gate rejection is a verdict, not something to overwrite with --accept-riskオープンlesson-submission needs-ac pending-review
難易度 2/5 1〜3時間 初心者へのやさしさ 65/100
Ikalus1988/MisakaNet#2870 ·
メンテナーはふだん 1 日以内に返信
-
feature:LinkChecker
難易度 2/5 1〜3時間 初心者へのやさしさ 66/100
digitalfabrik/integreat-cms#4594 ·
メンテナーはふだん 5 日以内に返信