Skip to content

docs: replace asyncio.create_task pattern with ctx.run() in all examples - #61

Merged
shloimy-wiesel merged 2 commits into
mainfrom
claude/issue-58-20260526-1255
May 30, 2026
Merged

shloimy-wiesel merged 2 commits into
mainfrom
claude/issue-58-20260526-1255

Conversation

@shloimy-wiesel

Copy link
Copy Markdown
Owner

Replace the three locations that still showed the old asyncio.create_task + try/finally pattern with the recommended ctx.run() approach: the init module docstring quickstart, the context.py module docstring FastAPI example, and the live example/backend/routes/chat.py endpoint.

The example backend also drops the _background_tasks GC-protection set and the asyncio import, which ctx.run() makes unnecessary.

Closes #58

Generated with Claude Code

Update the three locations that still showed the old manual
asyncio.create_task + try/finally pattern to use the recommended
ctx.run() approach: the __init__ module docstring quickstart,
the context.py module docstring FastAPI example, and the live
example/backend/routes/chat.py endpoint.

The example backend also drops the _background_tasks GC-protection set
and the asyncio import, which ctx.run() makes unnecessary.

Co-authored-by: shloimy-wiesel <shloimy-wiesel@users.noreply.github.com>
@vercel

vercel Bot commented May 28, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
ai-sdk-stream-python Ready Ready Preview, Comment, Open in v0 May 30, 2026 7:31pm

@shloimy-wiesel

Copy link
Copy Markdown
Owner Author

@claude code review this pr

@claude

claude Bot commented May 28, 2026 •

Copy link
Copy Markdown

Claude finished @shloimy-wiesel's task in 1m 38s —— View job


Code Review

  • Gather context and read changed files
  • Review src/ai_sdk_stream_python/__init__.py
  • Review src/ai_sdk_stream_python/context.py
  • Review example/backend/routes/chat.py
  • Post final review

Overall this is a clean, correct docs-and-example migration. The three changed files are consistent with each other and with how ctx.run() is implemented in context.py. No old asyncio.create_task / _background_tasks references remain in the codebase.

Findings

example/backend/routes/chat.py — Minor concern

await ctx.run(lambda c: llm_service.chat(messages, ctx=c))

This is functionally correct — the lambda returns a coroutine which ctx.run() awaits in _safe(). However, it is stylistically inconsistent with the named async def _work(c: StreamContext) -> None pattern shown in both docstring examples that this very PR updates. The lambda form is slightly harder to read, and Pyright will also infer a weaker type ((...) -> Coroutine[...] rather than the explicit Callable[[StreamContext], Awaitable[None]]).

A named function (matching the docstring pattern) would be more consistent:

async def _work(c: StreamContext) -> None:
    messages = [m.model_dump() for m in request.messages]
    await llm_service.chat(messages, ctx=c)

await ctx.run(_work)

This also lets you move the messages computation inside _work, which is a natural fit. Fix this →

src/ai_sdk_stream_python/context.py — Left-over prose (pre-existing, out of scope)

The StreamContext class docstring (line 117) still reads:

"spin up a background task that writes events through the helpers"

It doesn't mention ctx.run(). This is a pre-existing issue not introduced by this PR, but since this PR is specifically about surfacing ctx.run() in documentation, it would be a natural place to add a one-liner like "Use ctx.run() to schedule the background task." Not a blocker.

Everything else — looks good

  • Removing import asyncio and _background_tasks from chat.py is correct; ctx.run() internalises both the task creation and GC anchoring.
  • The __init__.py quickstart now shows the type-annotated _work(c: StreamContext) -> None signature — good for discoverability.
  • The context.py module docstring example correctly uses c as the parameter name throughout (no variable shadowing with the outer ctx).
  • ctx.run() is well-tested (8 test cases in tests/test_context.py:922), so no test additions are needed for this docs-only change.
  • await ctx.run(...) discards the returned asyncio.Task. That's intentional by design — the task is GC-anchored on self._task inside run() — but worth knowing if a reviewer asks why the return value isn't captured.

Summary

One minor style inconsistency (lambda vs. named function in chat.py) worth addressing; everything else is correct and clean. ✓

@shloimy-wiesel

Copy link
Copy Markdown
Owner Author

@claude fix the findings from the pr comment

@claude

claude Bot commented May 30, 2026 •

Copy link
Copy Markdown

Claude finished @shloimy-wiesel's task in 1m 6s —— View job


Fixed PR Review Findings

  • Read current state of changed files
  • Fix lambda → named async def _work in example/backend/routes/chat.py
  • Update StreamContext class docstring in context.py to mention ctx.run()
  • Verify diff (lint tools unavailable in this environment — no formatting changes needed)
  • Commit and push changes

Changes Made

example/backend/routes/chat.py — Replaced the lambda with a named async def _work(c: StreamContext) -> None function, moving the messages computation inside it. This matches the pattern shown in both docstring examples updated by this PR and gives Pyright a stronger type.

src/ai_sdk_stream_python/context.py — Updated the StreamContext class docstring from:

"spin up a background task that writes events through the helpers"

to:

"use ctx.run() to schedule the background work"
| Branch

…t docstring

- example/backend/routes/chat.py: replace `lambda c: ...` with a named
  `async def _work(c: StreamContext)` consistent with the docstring examples
- context.py: update StreamContext class docstring to mention `ctx.run()`
  instead of the old "spin up a background task" phrasing

Co-authored-by: shloimy-wiesel <shloimy-wiesel@users.noreply.github.com>
@shloimy-wiesel
shloimy-wiesel merged commit 4510999 into main May 30, 2026
2 checks passed
shloimy-wiesel added a commit that referenced this pull request May 30, 2026
* 0.1.2

Automatically generated by python-semantic-release

* Update issue templates

* chore: Claude Code GitHub Workflow (#28)

* "Claude PR Assistant workflow"

* "Claude Code Review workflow"

* feat: complete v0.2.0 feature set — collection, typed info, events, OpenAI adapter, and request types

Add the full StreamContext feature surface for AI SDK v6 compatibility:

Stream collection & persistence:
- collect=True accumulates text, reasoning, tool calls, sources, files,
  data parts, token counts, and timing into StreamRecord (ctx.record)
- on_finish callback for post-stream DB persistence
- Per-call collect=False to skip recording ephemeral writes
- RuntimeError when collect=True on non-collecting context

StreamContext enhancements:
- Generic[_InfoT] typed custom_information (ctx.info) for request-scoped metadata
- ctx.run() safe task runner: auto-finish, auto-error, GC-safe
- ctx.error() emits ErrorEvent then terminates stream
- ctx.abort() emits AbortEvent per v6 spec (with optional reason)
- ctx.write_file() for FileEvent support
- ctx.write_data() for dynamic data-{name} parts (any JSON-serializable value)
- start_tool_input/stream_tool_input_delta/finish_tool_input for streaming tool input
- start_metadata parameter for start event messageMetadata
- Auto-count streaming tokens with configurable count_func + set_usage() override
- Timing fields (created_at, finished_at, duration_ms) on StreamRecord

New event types:
- ErrorEvent, AbortEvent, FileEvent, DataEvent (dynamic type)
- ToolInputStartEvent, ToolInputDeltaEvent, ToolInputAvailableEvent

OpenAI contrib module (contrib/openai):
- consume_openai_stream() maps OpenAI chunks to ctx.write_* calls
- convert_to_openai_messages() converts UIMessage parts to ChatCompletionMessageParam
- Duck-typed — no hard openai package dependency

Request body types (types.py):
- ChatRequest, UIMessage, and all v6 part models (TextUIPart, ToolUIPart, etc.)
- Discriminated union for MessagePart with dynamic prefix support
- extra="allow" for forward compatibility

Bug fixes:
- Tool name deduplication in consume_openai_stream
- Tool call ID consistency using ToolCallHandle.toolCallId
- write_data() accepts any JSON-serializable value, not just dict
- DataEvent.encode() uses model_dump_json() for non-native types

Also adds chatbot-backend example (FastAPI + Redis + 5 tools),
hierarchical AGENTS.md knowledge base, and 237 passing tests.

* 0.2.0

Automatically generated by python-semantic-release

* fix: update pydantic dependency version to 2.0.0

* 0.2.1

Automatically generated by python-semantic-release

* fix: respect context-level collect flag in _should_collect

* 0.2.2

Automatically generated by python-semantic-release

* chore: Delete .claude/settings.json

* feat: add stream_exclude and store_exclude parameters to StreamContext (#47)

feat: add stream_exclude and store_exclude parameters to StreamContext

Agent-Logs-Url: https://github.com/shloimy-wiesel/ai-sdk-stream-python/sessions/ed622073-4b0a-4004-a3d8-eb53d3363f9d

* 0.3.0

Automatically generated by python-semantic-release

* feat: add `tool_calls_in_reasoning` option to StreamContext (#49)

feat: add tool_calls_in_reasoning option to StreamContext

When enabled, begin_tool_call and start_tool_input skip closing the
reasoning block, allowing tool calls to be emitted inline within
reasoning for frontends that support interleaved rendering.

Agent-Logs-Url: https://github.com/shloimy-wiesel/ai-sdk-stream-python/sessions/5576925b-99a1-4b5e-8f28-bd20be5b267b

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: shloimy-wiesel <144027408+shloimy-wiesel@users.noreply.github.com>

* 0.4.0

Automatically generated by python-semantic-release

* chore: add keywords and classifiers to pyproject.toml

* ci: update permissions and checkout action version in Claude workflow

* docs: replace asyncio.create_task pattern with ctx.run() in all examples (#61)

* docs: replace asyncio.create_task pattern with ctx.run() in all examples

Update the three locations that still showed the old manual
asyncio.create_task + try/finally pattern to use the recommended
ctx.run() approach: the __init__ module docstring quickstart,
the context.py module docstring FastAPI example, and the live
example/backend/routes/chat.py endpoint.

The example backend also drops the _background_tasks GC-protection set
and the asyncio import, which ctx.run() makes unnecessary.

Co-authored-by: shloimy-wiesel <shloimy-wiesel@users.noreply.github.com>

* fix: replace lambda with named _work function and update StreamContext docstring

- example/backend/routes/chat.py: replace `lambda c: ...` with a named
  `async def _work(c: StreamContext)` consistent with the docstring examples
- context.py: update StreamContext class docstring to mention `ctx.run()`
  instead of the old "spin up a background task" phrasing

Co-authored-by: shloimy-wiesel <shloimy-wiesel@users.noreply.github.com>

---------

Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: shloimy-wiesel <shloimy-wiesel@users.noreply.github.com>

---------

Co-authored-by: semantic-release <semantic-release>
Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: shloimy-wiesel <shloimy-wiesel@users.noreply.github.com>

This branch was successfully deployed

1 active deployment
Preview — 4ff11121 Deployed May 30, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

docs: module docstrings and example/backend show pre-ctx.run() pattern

1 participant