Skip to content

Python: [Bug]: ResponseStream rejects Task and Future stream sources #8330

Description

@Shy7777

Description

ResponseStream accepts an Awaitable[AsyncIterable], and ResponseStream.from_awaitable() accepts an Awaitable[ResponseStream]. Passing an asyncio.Task or asyncio.Future to either entry point currently fails when resolving the stream. Passing the underlying coroutine directly works.

This comes up when stream initialization has already been scheduled with asyncio.create_task(). The task should be awaited before the resulting stream is consumed, just as it is for a coroutine source.

Code Sample

import asyncio
from collections.abc import AsyncIterable

from agent_framework import ChatResponse, ChatResponseUpdate, Content, ResponseStream


async def updates() -> AsyncIterable[ChatResponseUpdate]:
    yield ChatResponseUpdate(role="assistant", contents=[Content.from_text("hello")])


async def get_stream() -> ResponseStream[ChatResponseUpdate, ChatResponse]:
    return ResponseStream(updates(), finalizer=ChatResponse.from_updates)


async def main() -> None:
    source = asyncio.create_task(get_stream())
    stream = ResponseStream.from_awaitable(source)
    try:
        print((await stream.get_final_response()).text)
    finally:
        await source


asyncio.run(main())

Expected output: hello.

Error Messages / Stack Traces

The example raises RuntimeError: Inner stream not available. Iterating the same wrapper instead raises AttributeError: '_asyncio.Task' object has no attribute '__aiter__'. A Future source fails for the same reason.

Package Versions

agent-framework-core 1.18.0, reproduced from source at 3c670707766a8455da6491a9049cc9d575e019f0.

Python Version

Python 3.12.14 on macOS arm64.

Additional Context

The reproduction runs locally without a model or network request. _get_stream() currently checks asyncio.iscoroutine(), which excludes Tasks and Futures. The module already uses inspect.isawaitable() for other awaitable inputs.

I have a small fix and regression coverage for both entry points, keeping the existing coroutine cases as controls.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

agentsUsage: [Issues, PRs], Target: Single agentpythonUsage: [Issues, PRs], Target: PythonreproducedUsage: [Issues], Target: all issues that can be reproduced by the triage workflow

Type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions