Live `AsyncSession.send_tool_response` fails with `TypeError` when `FunctionResponse.parts` carries inline bytes
Los mantenedores suelen responder en 1 día
Evaluación
- Dificultad
- 3/5
- Tiempo estimado
- 1-2 días
- Aptitud para principiantes
- 72/100
Línea de trabajo
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.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
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).
- Lenguaje dominante
- Python
- Estrellas
- 4k
- Forks
- 1k
- Merge medio
- 1 d 17 h
- PR fusionados (30 d)
- 52
Preparar el entorno
- Sin Dockerfile ni archivo de Docker Compose
- Sin plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de googleapis/python-genai
-
priority: p2 status:awaiting user response type: bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 62/100
googleapis/python-genai#3051 · 1 comentario · 1 asignado ·
Los mantenedores suelen responder en 1 día
-
[Bug]: Unsubscripted typing.List and typing.Dict crash convert_if_exist_pydantic_model in AFCPosiblemente ocupada @chauvuusvn la tomó hace 3 días. Abiertopriority: p2 type: bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
googleapis/python-genai#3044 · 1 comentario · 1 asignado ·
Los mantenedores suelen responder en 1 día
-
Name collision in google.genai.interactions: triggers.Interaction shadows response model Interaction in static type checkersPosiblemente ocupada @Venkaiahbabuneelam la tomó hace 8 días. Abiertopriority: p2 type: bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
googleapis/python-genai#3013 · 1 comentario · 1 asignado ·
Los mantenedores suelen responder en 1 día
-
Dificultad 3/5 1-2 días Aptitud para principiantes 74/100
googleapis/python-genai#3056 ·
Los mantenedores suelen responder en 1 día
-
Vertex batch embeddings (gemini-embedding-001): no way to attach billing labels; job and row labels never reach Cloud BillingPosiblemente ocupada @Venkaiahbabuneelam la tomó hace 1 día. Abiertopriority: p3 type: question
googleapis/python-genai#3053 · 1 asignado ·
Los mantenedores suelen responder en 1 día
Todos los issues de googleapis/python-genai
Issues similares
-
needs-human needs-triage
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
gke-labs/kube-agents#2400 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
Device Details tables: FS/SF columns contradict each other (nfet_01v8 Vt row, pfet_01v8 Idsat row)Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 75/100
google/skywater-pdk#450 ·
-
Drained trajectory arrays are overwritten when the sequence buffer is reusedPosiblemente ocupada @sylvesterkaczmarek la tomó hoy. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
google-deepmind/bsuite#56 ·
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
LearningCircuit/local-deep-research#7206 ·
Los mantenedores suelen responder en 1 día
-
[TASK] Document technology stackAbierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
chingu-voyages/V62-tier3-team-33#285 ·
Los mantenedores suelen responder en 1 día