pydantic ext: auto_wrap_tools + async event_stream_handler crashes with 'Object of type coroutine is not JSON serializable' on first tool call
Valutazione
- Difficoltà
- 3/5
- Tempo stimato
- 1-2 giorni
- Idoneità per principianti
- 68/100
Direzione di ricerca
Inizia in restate/ext/pydantic/_agent.py, in corrispondenza di wrapped_event_stream_handler, poi leggi server_context.py intorno a create_run_coroutine per confermare come il callable viene classificato e serializzato. Esegui la riproduzione fornita con un async event_stream_handler, una chiamata a uno strumento e auto_wrap_tools=True; l’attività è completata quando l’handler viene awaitato per gli eventi dello strumento, senza l’errore di serializzazione della coroutine né nuovi tentativi di invocazione.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Summary
With RestateAgent(..., event_stream_handler=<async fn>, auto_wrap_tools=True), the first tool call of a run fails the invocation with:
TypeError: Object of type coroutine is not JSON serializable
Text-only runs work fine — the crash only happens once the model calls a tool, because that's when wrapped_event_stream_handler journals tool-side events.
Environment
- restate-sdk 1.0.3 (bug also present on current
main) - pydantic-ai-slim 2.4.0
- Python 3.12, Restate server 1.7.2
Root cause
restate/ext/pydantic/_agent.py (line 171 on main), in wrapped_event_stream_handler:
await context.run_typed("run event", lambda: fn(ctx, single_event()))
fn is the user's event_stream_handler, which is an async function. The lambda wrapping it is a plain sync callable, so in server_context.create_run_coroutine:
if inspect.iscoroutinefunction(action): # False for the lambda
action_result = await action()
else:
...run in executor...
action_result = await action_result_future # <- the un-awaited coroutine object
buffer = serde.serialize(action_result) # TypeError: coroutine is not JSON serializable
The handler coroutine is never awaited (so the user's handler never runs for tool events), and Restate then tries to JSON-serialize the coroutine object itself.
Reproduction
import restate
from pydantic_ai import Agent
from restate.ext.pydantic import RestateAgent
agent = Agent("openai:gpt-4o")
@agent.tool_plain
def get_weather(city: str) -> str:
return "sunny"
svc = restate.Service("Repro")
@svc.handler()
async def run(ctx: restate.Context, prompt: str) -> str:
async def on_events(run_ctx, events):
async for _ in events:
pass
ragent = RestateAgent(agent, event_stream_handler=on_events, auto_wrap_tools=True)
result = await ragent.run(prompt)
return result.output
app = restate.app([svc])
Invoke with a prompt that triggers the tool, e.g. "What's the weather in Hanoi? Use the tool." → the run event journal entry fails with the TypeError above and the invocation retries forever.
Traceback
File ".../restate/server_context.py", line 857, in create_run_coroutine
buffer = serde.serialize(action_result)
File ".../restate/serde.py", line 317, in serialize
return json.dumps(obj).encode("utf-8")
...
TypeError: Object of type coroutine is not JSON serializable
Suggested fix
Use an async closure so create_run_coroutine awaits it:
async for event in stream:
async def single_event():
yield event
async def deliver() -> None:
await fn(ctx, single_event())
await context.run_typed("run event", deliver)
Related
- Found together with a second streaming+tools bug (turnstile never initialized in
request_stream), reported separately. - #201 fixed an earlier crash on this same streaming path — this combination (streaming handler +
auto_wrap_tools+ an actual tool call) seems to not be covered by tests yet.
- Lingua principale
- Python
- Stelle
- 82
- Fork
- 24
- Merge medio
- 13m
- PR unite (30g)
- 1
Preparare l'ambiente
Questo progetto non fornisce container di sviluppo, Dockerfile né guida per i contributori, quindi l'ambiente è a tuo carico: parti dal suo README e consulta la nostra guida al primo contributo per i passaggi generali.
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di restatedev/sdk-python
-
pydantic ext: request_stream() never initializes the turnstile — KeyError('call_...') on first tool call of a streamed runForse già presa @QuocThuanTruong l’ha presa 72 giorni fa. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
restatedev/sdk-python#223 · 1 commento ·
-
Difficoltà 2/5 1-2 giorni Idoneità per principianti 68/100
restatedev/sdk-python#204 · 1 commento ·
-
RestateModelWrapper should not wrap `SdkInternalBaseException` as generic Exception; classify non-retryable provider errors as terminalForse già presa Una pull request collegata a questa issue è aperta o già unita. Aperta
Difficoltà 4/5 3-5 giorni Idoneità per principianti 48/100
restatedev/sdk-python#225 ·
-
Difficoltà 3/5 1-2 giorni Idoneità per principianti 68/100
restatedev/sdk-python#221 ·
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 38/100
restatedev/sdk-python#205 · 1 commento ·
Tutte le issue di restatedev/sdk-python
Issue simili
-
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 85/100
Vector35/community-plugins#376 ·
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
py-econometrics/pyfixest#1883 ·
I maintainer di solito rispondono entro 1 giorno
-
bad links in rfc5890.htmlAperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 65/100
ietf-tools/rfc2html#81 ·
-
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 88/100
mysql/mysql-operator#60 ·
-
Python: Bug: split_plaintext_paragraph / split_markdown_paragraph can return a chunk larger than max_tokensForse già presa @xThreeh l’ha presa oggi. Apertapython triage
Difficoltà 2/5 1-3 ore Idoneità per principianti 75/100
microsoft/semantic-kernel#14566 ·
I maintainer di solito rispondono entro 4 giorni