Skip to content

fix: propagate contextvars in sync_to_async for FunctionTool#21558

Open
NIK-TIGER-BILL wants to merge 3 commits intorun-llama:mainfrom
NIK-TIGER-BILL:fix-contextvar-propagation-sync-tools
Open

fix: propagate contextvars in sync_to_async for FunctionTool#21558
NIK-TIGER-BILL wants to merge 3 commits intorun-llama:mainfrom
NIK-TIGER-BILL:fix-contextvar-propagation-sync-tools

Conversation

@NIK-TIGER-BILL
Copy link
Copy Markdown
Contributor

Description

When a sync function is wrapped for async execution via sync_to_async, the default executor thread does not inherit the caller's contextvars context. This breaks ContextVar propagation (e.g. OpenTelemetry spans) for sync tools invoked through acall().

The fix snapshots contextvars.copy_context() and runs fn inside ctx.run(...) within the executor thread, mirroring the existing fix already present in async_utils.py's asyncio_run().

Fixes #21555

New Package?

  • Yes
  • No

Version Bump?

  • Yes
  • No

Type of Change

  • Bug fix (non-breaking change which fixes an issue)

How Has This Been Tested?

  • Added test_function_tool_contextvar_propagation to tests/tools/test_base.py which sets a ContextVar and asserts it is propagated into a sync FunctionTool called via acall().
  • Existing test suite (tests/tools/test_base.py) passes locally (24/24).

Suggested Checklist:

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

When a sync function is wrapped for async execution via sync_to_async,
the default executor thread does not inherit the caller's contextvars context.
This breaks ContextVar propagation (e.g. OpenTelemetry spans) for sync tools
invoked through acall().

Fix by snapshotting contextvars.copy_context() and running fn inside it
via ctx.run() within the executor thread, mirroring the existing fix in
async_utils.py's asyncio_run().

Closes run-llama#21555

Signed-off-by: NIK-TIGER-BILL <nik.tiger.bill@github.com>
@dosubot dosubot Bot added the size:XS This PR changes 0-9 lines, ignoring generated files. label May 5, 2026
@logan-markewich
Copy link
Copy Markdown
Collaborator

This breaks at least one test in core. Linting is also failing

…or compat

Signed-off-by: NIK-TIGER-BILL <nik.tiger.bill@github.com>
@NIK-TIGER-BILL
Copy link
Copy Markdown
Contributor Author

@logan-markewich Thanks for the review — you were absolutely right. The previous ctx.run, fn, *args, **kwargs broke run_in_executor because loop.run_in_executor does not forward **kwargs to the callable. I have pushed a fix that wraps the call in a lambda again (lambda: ctx.run(fn, *args, **kwargs)), so kwargs are preserved while the ContextVar context is still propagated into the executor thread. This should resolve the test and lint failures. Let me know if anything else is needed!

Signed-off-by: NIK-TIGER-BILL <nik.tiger.bill@github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XS This PR changes 0-9 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: ContextVar not propagated on non-async tool execution.

2 participants