test(core): migrate streaming tests to stream_events(version='v3')

Rename test_stream_v2.py -> test_chat_model_v3_stream.py, replace all
.stream_v2() / .astream_v2() call sites in tests with the new public API
.stream_events(version="v3") / .astream_events(version="v3"), and update
function names + docstrings to match.
This commit is contained in:
Nick Hollon
2026-04-30 15:01:34 -04:00
parent d1054edb0e
commit 271fcc8424
13 changed files with 162 additions and 153 deletions

View File

@@ -1561,16 +1561,17 @@ def test_invocation_params_passed_to_tracer_metadata() -> None:
assert run.metadata == run.extra["metadata"]
def test_stream_v2_invocation_params_passed_to_tracer_metadata() -> None:
"""`stream_v2()` must preserve filtered invocation params for tracing."""
def test_stream_events_v3_invocation_params_passed_to_tracer_metadata() -> None:
"""`stream_events(version="v3")` must preserve filtered invocation params for tracing."""
llm = FakeStreamingChatModelWithInvocationParams()
collector = LangChainTracerRunCollector()
with collector.tracing_callback() as tracer:
_ = llm.stream_v2(
_ = llm.stream_events(
[HumanMessage(content="Hello")],
config={"callbacks": [tracer]},
stop=["done"],
version="v3",
).output
assert len(collector.runs) == 1
@@ -1581,16 +1582,17 @@ def test_stream_v2_invocation_params_passed_to_tracer_metadata() -> None:
assert metadata["temperature"] == 0.7
async def test_astream_v2_invocation_params_passed_to_tracer_metadata() -> None:
"""`astream_v2()` must preserve filtered invocation params for tracing."""
async def test_astream_events_v3_invocation_params_passed_to_tracer_metadata() -> None:
"""`astream_events(version="v3")` must preserve filtered invocation params for tracing."""
llm = FakeStreamingChatModelWithInvocationParams()
collector = LangChainTracerRunCollector()
with collector.tracing_callback() as tracer:
stream = await llm.astream_v2(
stream = await llm.astream_events(
[HumanMessage(content="Hello")],
config={"callbacks": [tracer]},
stop=["done"],
version="v3",
)
_ = await stream

View File

@@ -1,4 +1,4 @@
"""Tests for BaseChatModel.stream_v2() / astream_v2()."""
"""Tests for `BaseChatModel.stream_events(version="v3")` / `astream_events(version="v3")`."""
from __future__ import annotations
@@ -32,11 +32,11 @@ if TYPE_CHECKING:
class TestStreamV2Sync:
"""Test BaseChatModel.stream_v2() with FakeListChatModel."""
"""Test `BaseChatModel.stream_events(version="v3")` with `FakeListChatModel`."""
def test_stream_text(self) -> None:
model = FakeListChatModel(responses=["Hello world!"])
stream = model.stream_v2("test")
stream = model.stream_events("test", version="v3")
assert isinstance(stream, ChatModelStream)
deltas = list(stream.text)
@@ -45,7 +45,7 @@ class TestStreamV2Sync:
def test_stream_output(self) -> None:
model = FakeListChatModel(responses=["Hello!"])
stream = model.stream_v2("test")
stream = model.stream_events("test", version="v3")
msg = stream.output
assert isinstance(msg.content, list)
@@ -54,7 +54,7 @@ class TestStreamV2Sync:
def test_stream_usage_none_for_fake(self) -> None:
model = FakeListChatModel(responses=["Hi"])
stream = model.stream_v2("test")
stream = model.stream_events("test", version="v3")
# Drain
for _ in stream.text:
pass
@@ -62,7 +62,7 @@ class TestStreamV2Sync:
def test_stream_raw_events(self) -> None:
model = FakeListChatModel(responses=["ab"])
stream = model.stream_v2("test")
stream = model.stream_events("test", version="v3")
events = list(stream)
event_types = [e.get("event") for e in events]
@@ -72,12 +72,12 @@ class TestStreamV2Sync:
class TestAstreamV2:
"""Test BaseChatModel.astream_v2() with FakeListChatModel."""
"""Test `BaseChatModel.astream_events(version="v3")` with `FakeListChatModel`."""
@pytest.mark.asyncio
async def test_astream_text_await(self) -> None:
model = FakeListChatModel(responses=["Hello!"])
stream = await model.astream_v2("test")
stream = await model.astream_events("test", version="v3")
assert isinstance(stream, AsyncChatModelStream)
full = await stream.text
@@ -86,7 +86,7 @@ class TestAstreamV2:
@pytest.mark.asyncio
async def test_astream_text_deltas(self) -> None:
model = FakeListChatModel(responses=["Hi"])
stream = await model.astream_v2("test")
stream = await model.astream_events("test", version="v3")
deltas = [d async for d in stream.text]
assert "".join(deltas) == "Hi"
@@ -94,7 +94,7 @@ class TestAstreamV2:
@pytest.mark.asyncio
async def test_astream_await_output(self) -> None:
model = FakeListChatModel(responses=["Hey"])
stream = await model.astream_v2("test")
stream = await model.astream_events("test", version="v3")
msg = await stream
assert msg.content == [{"type": "text", "text": "Hey", "index": 0}]
@@ -193,13 +193,13 @@ class _EmptyStreamModel(BaseChatModel):
class TestCallbacks:
"""Verify stream_v2 fires on_llm_end / on_llm_error callbacks."""
"""Verify `stream_events(version="v3")` fires `on_llm_end` / `on_llm_error` callbacks."""
def test_stream_v2_defers_on_chat_model_start_until_consumed(self) -> None:
def test_stream_events_v3_defers_on_chat_model_start_until_consumed(self) -> None:
handler = _RecordingHandler()
model = FakeListChatModel(responses=["done"], callbacks=[handler])
stream = model.stream_v2("test")
stream = model.stream_events("test", version="v3")
assert handler.events == []
@@ -210,7 +210,7 @@ class TestCallbacks:
def test_on_llm_end_fires_after_drain(self) -> None:
handler = _RecordingHandler()
model = FakeListChatModel(responses=["done"], callbacks=[handler])
stream = model.stream_v2("test")
stream = model.stream_events("test", version="v3")
for _ in stream.text:
pass
_ = stream.output
@@ -225,18 +225,18 @@ class TestCallbacks:
async def test_on_llm_end_fires_async(self) -> None:
handler = _AsyncRecordingHandler()
model = FakeListChatModel(responses=["done"], callbacks=[handler])
stream = await model.astream_v2("test")
stream = await model.astream_events("test", version="v3")
_ = await stream
assert "on_chat_model_start" in handler.events
assert "on_llm_end" in handler.events
@pytest.mark.asyncio
async def test_astream_v2_defers_on_chat_model_start_until_consumed(self) -> None:
async def test_astream_events_v3_defers_on_chat_model_start_until_consumed(self) -> None:
handler = _AsyncRecordingHandler()
model = FakeListChatModel(responses=["done"], callbacks=[handler])
stream = await model.astream_v2("test")
stream = await model.astream_events("test", version="v3")
assert handler.events == []
@@ -251,7 +251,7 @@ class TestCallbacks:
"""
handler = _RecordingHandler()
model = FakeListChatModel(responses=["hello"], callbacks=[handler])
stream = model.stream_v2("test")
stream = model.stream_events("test", version="v3")
_ = stream.output
response = handler.last_llm_end_response
@@ -265,7 +265,7 @@ class TestCallbacks:
async def test_on_llm_end_receives_assembled_message_async(self) -> None:
handler = _AsyncRecordingHandler()
model = FakeListChatModel(responses=["hello"], callbacks=[handler])
stream = await model.astream_v2("test")
stream = await model.astream_events("test", version="v3")
_ = await stream
response = handler.last_llm_end_response
@@ -277,7 +277,7 @@ class TestCallbacks:
def test_empty_stream_reports_error_without_finish_only_lifecycle(self) -> None:
handler = _RecordingHandler()
stream = _EmptyStreamModel(callbacks=[handler]).stream_v2("test")
stream = _EmptyStreamModel(callbacks=[handler]).stream_events("test", version="v3")
with pytest.raises(ValueError, match="No generation chunks were returned"):
list(stream)
@@ -289,7 +289,7 @@ class TestCallbacks:
@pytest.mark.asyncio
async def test_empty_astream_reports_error(self) -> None:
handler = _AsyncRecordingHandler()
stream = await _EmptyStreamModel(callbacks=[handler]).astream_v2("test")
stream = await _EmptyStreamModel(callbacks=[handler]).astream_events("test", version="v3")
with pytest.raises(ValueError, match="No generation chunks were returned"):
await stream
@@ -303,12 +303,12 @@ class TestCallbacks:
class TestOnStreamEvent:
"""`on_stream_event` must fire once per protocol event from stream_v2."""
"""`on_stream_event` must fire once per protocol event from `stream_events(version="v3")`."""
def test_on_stream_event_fires_for_every_event_sync(self) -> None:
handler = _RecordingHandler()
model = FakeListChatModel(responses=["Hi"], callbacks=[handler])
stream = model.stream_v2("test")
stream = model.stream_events("test", version="v3")
_ = stream.output
# Every event the stream sees should also reach the observer.
@@ -322,7 +322,7 @@ class TestOnStreamEvent:
async def test_on_stream_event_fires_for_every_event_async(self) -> None:
handler = _AsyncRecordingHandler()
model = FakeListChatModel(responses=["Hi"], callbacks=[handler])
stream = await model.astream_v2("test")
stream = await model.astream_events("test", version="v3")
_ = await stream
event_types = [e["event"] for e in handler.stream_events]
@@ -334,7 +334,7 @@ class TestOnStreamEvent:
"""Stream events must all fire between on_chat_model_start and on_llm_end."""
handler = _RecordingHandler()
model = FakeListChatModel(responses=["Hi"], callbacks=[handler])
stream = model.stream_v2("test")
stream = model.stream_events("test", version="v3")
_ = stream.output
# on_stream_event doesn't show up in `events` (different list), but
@@ -346,10 +346,10 @@ class TestOnStreamEvent:
class TestCancellation:
"""Cancellation of `astream_v2` must propagate, not be swallowed."""
"""Cancellation of `astream_events(version="v3")` must propagate, not be swallowed."""
@pytest.mark.asyncio
async def test_astream_v2_cancellation_propagates(self) -> None:
async def test_astream_events_v3_cancellation_propagates(self) -> None:
"""Cancelling the producer task must raise CancelledError.
Regression test: the producer's `except BaseException` previously
@@ -357,7 +357,7 @@ class TestCancellation:
`on_llm_error` + `stream._fail` pair that never propagated.
"""
model = FakeListChatModel(responses=["abcdefghij"], sleep=0.05)
stream = await model.astream_v2("test")
stream = await model.astream_events("test", version="v3")
aiter_ = stream.text.__aiter__()
await aiter_.__anext__()
task = stream._producer_task
@@ -401,7 +401,7 @@ class _KwargRecordingModel(FakeListChatModel):
class TestRunnableBindingForwarding:
"""`RunnableBinding.stream_v2` must merge bound kwargs into the call.
"""`RunnableBinding.stream_events(version="v3")` must merge bound kwargs into the call.
Without the explicit override on `RunnableBinding`, `__getattr__`
forwards the call but drops `self.kwargs` — so tools bound via
@@ -409,12 +409,12 @@ class TestRunnableBindingForwarding:
ignored.
"""
def test_bound_kwargs_reach_stream_v2(self) -> None:
def test_bound_kwargs_reach_stream_events_v3(self) -> None:
model = _KwargRecordingModel(responses=["hi"])
model.received_kwargs = []
bound = model.bind(my_marker="sentinel-42")
stream = bound.stream_v2("test")
stream = bound.stream_events("test", version="v3")
for _ in stream.text:
pass
@@ -426,19 +426,19 @@ class TestRunnableBindingForwarding:
model.received_kwargs = []
bound = model.bind(my_marker="from-bind")
stream = bound.stream_v2("test", my_marker="from-call")
stream = bound.stream_events("test", my_marker="from-call", version="v3")
for _ in stream.text:
pass
assert model.received_kwargs[0].get("my_marker") == "from-call"
@pytest.mark.asyncio
async def test_bound_kwargs_reach_astream_v2(self) -> None:
async def test_bound_kwargs_reach_astream_events_v3(self) -> None:
model = _KwargRecordingModel(responses=["hi"])
model.received_kwargs = []
bound = model.bind(my_marker="sentinel-async")
stream = await bound.astream_v2("test")
stream = await bound.astream_events("test", version="v3")
_ = await stream
assert len(model.received_kwargs) == 1

View File

@@ -1,4 +1,4 @@
"""Tests for stream_v2 / astream_v2 and ChatModelStream."""
"""Tests for `stream_events(version="v3")` / `astream_events(version="v3")` and `ChatModelStream`."""
from __future__ import annotations
@@ -301,20 +301,20 @@ class TestAsyncChatModelStream:
class TestStreamV2:
"""Test BaseChatModel.stream_v2() with FakeListChatModel."""
"""Test `BaseChatModel.stream_events(version="v3")` with `FakeListChatModel`."""
def test_stream_v2_text(self) -> None:
def test_stream_events_v3_text(self) -> None:
model = FakeListChatModel(responses=["Hello world!"])
stream = model.stream_v2("test")
stream = model.stream_events("test", version="v3")
assert isinstance(stream, ChatModelStream)
deltas = list(stream.text)
assert "".join(deltas) == "Hello world!"
assert stream.done
def test_stream_v2_usage(self) -> None:
def test_stream_events_v3_usage(self) -> None:
model = FakeListChatModel(responses=["Hi"])
stream = model.stream_v2("test")
stream = model.stream_events("test", version="v3")
# Drain stream
for _ in stream.text:
@@ -323,10 +323,10 @@ class TestStreamV2:
assert stream.output.usage_metadata is None
assert stream.done
def test_stream_v2_malformed_tool_args_produce_invalid_tool_call(self) -> None:
def test_stream_events_v3_malformed_tool_args_produce_invalid_tool_call(self) -> None:
"""End-to-end: malformed tool-call JSON becomes invalid_tool_calls."""
model = _MalformedToolCallModel()
stream = model.stream_v2("test")
stream = model.stream_events("test", version="v3")
msg = stream.output
assert msg.tool_calls == []
@@ -336,10 +336,10 @@ class TestStreamV2:
assert itc["args"] == '{"q": '
assert itc["id"] == "call_1"
def test_stream_v2_translates_anthropic_server_tool_use_to_protocol(self) -> None:
def test_stream_events_v3_translates_anthropic_server_tool_use_to_protocol(self) -> None:
"""Phase E end-to-end: server_tool_use becomes server_tool_call in output."""
model = _AnthropicStyleServerToolModel()
stream = model.stream_v2("weather?")
stream = model.stream_events("weather?", version="v3")
msg = stream.output
assert isinstance(msg.content, list)
@@ -353,21 +353,21 @@ class TestStreamV2:
class TestAstreamV2:
"""Test BaseChatModel.astream_v2() with FakeListChatModel."""
"""Test `BaseChatModel.astream_events(version="v3")` with `FakeListChatModel`."""
@pytest.mark.asyncio
async def test_astream_v2_text(self) -> None:
async def test_astream_events_v3_text(self) -> None:
model = FakeListChatModel(responses=["Hello!"])
stream = await model.astream_v2("test")
stream = await model.astream_events("test", version="v3")
assert isinstance(stream, AsyncChatModelStream)
full = await stream.text
assert full == "Hello!"
@pytest.mark.asyncio
async def test_astream_v2_deltas(self) -> None:
async def test_astream_events_v3_deltas(self) -> None:
model = FakeListChatModel(responses=["Hi"])
stream = await model.astream_v2("test")
stream = await model.astream_events("test", version="v3")
deltas = [d async for d in stream.text]
assert "".join(deltas) == "Hi"
@@ -690,33 +690,34 @@ class TestStructuredOutputKwargStripping:
`stream()` / `astream()` pop `ls_structured_output_format` and
`structured_output_format` before forwarding kwargs to `_stream`
provider clients reject unknown kwargs. `stream_v2` / `astream_v2`
must do the same, or `.with_structured_output().stream_v2()` breaks.
provider clients reject unknown kwargs. `stream_events(version="v3")` /
`astream_events(version="v3")` must do the same, or
`.with_structured_output().stream_events(version="v3")` breaks.
"""
def test_stream_v2_strips_ls_structured_output_format(self) -> None:
def test_stream_events_v3_strips_ls_structured_output_format(self) -> None:
model = _RecordingStreamModel()
bound = model.bind(ls_structured_output_format={"schema": {"type": "object"}})
stream = bound.stream_v2("test")
stream = bound.stream_events("test", version="v3")
_ = stream.output # drain
recorded = _RecordingStreamModel.last_stream_kwargs
assert "ls_structured_output_format" not in recorded
assert "structured_output_format" not in recorded
def test_stream_v2_strips_structured_output_format(self) -> None:
def test_stream_events_v3_strips_structured_output_format(self) -> None:
model = _RecordingStreamModel()
bound = model.bind(structured_output_format={"schema": {"type": "object"}})
stream = bound.stream_v2("test")
stream = bound.stream_events("test", version="v3")
_ = stream.output
recorded = _RecordingStreamModel.last_stream_kwargs
assert "ls_structured_output_format" not in recorded
assert "structured_output_format" not in recorded
@pytest.mark.asyncio
async def test_astream_v2_strips_ls_structured_output_format(self) -> None:
async def test_astream_events_v3_strips_ls_structured_output_format(self) -> None:
model = _RecordingStreamModel()
bound = model.bind(ls_structured_output_format={"schema": {"type": "object"}})
stream = await bound.astream_v2("test")
stream = await bound.astream_events("test", version="v3")
_ = await stream
assert (
"ls_structured_output_format"
@@ -727,10 +728,10 @@ class TestStructuredOutputKwargStripping:
)
@pytest.mark.asyncio
async def test_astream_v2_strips_structured_output_format(self) -> None:
async def test_astream_events_v3_strips_structured_output_format(self) -> None:
model = _RecordingStreamModel()
bound = model.bind(structured_output_format={"schema": {"type": "object"}})
stream = await bound.astream_v2("test")
stream = await bound.astream_events("test", version="v3")
_ = await stream
assert (
"ls_structured_output_format"
@@ -845,7 +846,7 @@ class TestAsyncStreamAclose:
async def test_aclose_cancels_producer_task(self) -> None:
gate = asyncio.Event()
model = _GatedStreamModel(gate=gate)
stream = await model.astream_v2("test")
stream = await model.astream_events("test", version="v3")
# Pull the first delta so the producer enters the gated section.
aiter_ = stream.text.__aiter__()
@@ -863,7 +864,7 @@ class TestAsyncStreamAclose:
async def test_aclose_is_idempotent(self) -> None:
gate = asyncio.Event()
model = _GatedStreamModel(gate=gate)
stream = await model.astream_v2("test")
stream = await model.astream_events("test", version="v3")
aiter_ = stream.text.__aiter__()
await aiter_.__anext__()
@@ -874,7 +875,7 @@ class TestAsyncStreamAclose:
async def test_async_context_manager_closes_stream(self) -> None:
gate = asyncio.Event()
model = _GatedStreamModel(gate=gate)
stream = await model.astream_v2("test")
stream = await model.astream_events("test", version="v3")
async with stream as s:
assert s is stream
@@ -897,7 +898,7 @@ class TestAsyncStreamAclose:
"""
teardown_gate = asyncio.Event()
model = _SlowTeardownModel(teardown_gate=teardown_gate)
stream = await model.astream_v2("test")
stream = await model.astream_events("test", version="v3")
# Prime the producer so it enters `_astream`'s forever-blocking
# await.
@@ -947,7 +948,7 @@ class TestAsyncStreamAclose:
async def test_aclose_before_producer_starts_resolves_projections(self) -> None:
"""Early-cancel path: `_produce` never runs.
If a consumer calls `astream_v2()` and immediately `aclose()`
If a consumer calls `astream_events(version="v3")` and immediately `aclose()`
(or `async with` exits before the loop schedules `_produce`),
`task.cancel()` marks the task cancelled without ever invoking
its body so neither `stream.fail` nor `on_llm_error` fires.
@@ -963,8 +964,8 @@ class TestAsyncStreamAclose:
handler = RecordingHandler()
gate = asyncio.Event()
model = _GatedStreamModel(gate=gate)
stream = await model.astream_v2("test", config={"callbacks": [handler]})
# No yield to the event loop between `astream_v2` returning and
stream = await model.astream_events("test", config={"callbacks": [handler]}, version="v3")
# No yield to the event loop between `astream_events(version="v3")` returning and
# `aclose()` — the producer task has been created but its body
# has not executed.
await stream.aclose()
@@ -1003,7 +1004,7 @@ class TestAsyncStreamAclose:
handler = RecordingHandler()
gate = asyncio.Event()
model = _GatedStreamModel(gate=gate)
stream = await model.astream_v2("test", config={"callbacks": [handler]})
stream = await model.astream_events("test", config={"callbacks": [handler]}, version="v3")
aiter_ = stream.text.__aiter__()
await aiter_.__anext__()
@@ -1043,7 +1044,7 @@ class TestAsyncStreamAclose:
handler = SlowEndHandler()
model = FakeListChatModel(responses=["ok"])
stream = await model.astream_v2("test", config={"callbacks": [handler]})
stream = await model.astream_events("test", config={"callbacks": [handler]}, version="v3")
# Wait until the stream has assembled the message and the
# slow on_llm_end handler has started running.
@@ -1163,7 +1164,7 @@ class TestCacheHitV2Replay:
class _ProviderMetadataStreamModel(BaseChatModel):
"""Fake model that advertises `output_version="responses/v1"` in metadata.
Verifies `stream_v2` pins the assembled message's `output_version` to
Verifies `stream_events(version="v3")` pins the assembled message's `output_version` to
`"v1"` the shape it actually produces regardless of what the
provider's chunk metadata claims.
"""
@@ -1199,11 +1200,11 @@ class _ProviderMetadataStreamModel(BaseChatModel):
class TestOutputVersionPinning:
"""`stream_v2().output` always serializes as v1 content blocks."""
"""`stream_events(version="v3").output` always serializes as v1 content blocks."""
def test_output_version_pinned_to_v1(self) -> None:
model = _ProviderMetadataStreamModel()
stream = model.stream_v2("hi")
stream = model.stream_events("hi", version="v3")
msg = stream.output
# Assembled message must claim `"v1"` even though the provider
# chunk metadata advertised `"responses/v1"`.
@@ -1325,7 +1326,7 @@ class TestBedrockConverseToolCallArgs:
def test_bedrock_tool_call_assembles_without_error(self) -> None:
model = _BedrockConverseToolCallModel()
stream = model.stream_v2("What's the weather in Boston?")
stream = model.stream_events("What's the weather in Boston?", version="v3")
# Drive the stream to completion — the raise would have surfaced here.
events = list(stream)

View File

@@ -1,7 +1,7 @@
"""V1 parity tests: stream_v2() output must match model.stream() output.
"""V1 parity tests: `stream_events(version="v3")` output must match `model.stream()` output.
These are the acceptance criteria for streaming v2 — if any test fails,
v2 has a regression vs v1.
These are the acceptance criteria for the v3 streaming API — if any test fails,
v3 has a regression vs v1.
"""
from __future__ import annotations
@@ -128,8 +128,8 @@ def _collect_v1_message(model: BaseChatModel, input_text: str) -> AIMessage:
def _collect_v2_message(model: BaseChatModel, input_text: str) -> AIMessage:
"""Run model.stream_v2() and get .output."""
stream = model.stream_v2(input_text)
"""Run `model.stream_events(version="v3")` and get `.output`."""
stream = model.stream_events(input_text, version="v3")
return stream.output
@@ -156,13 +156,13 @@ class TestV1ParityBasic:
def test_empty_response(self) -> None:
"""A truly empty stream is an error, matching `stream()` parity.
`stream_v2` distinguishes "producer emitted events but no terminal
`message-finish`" (which is synthesized, for native-event providers
`stream_events(version="v3")` distinguishes "producer emitted events but no
terminal `message-finish`" (which is synthesized, for native-event providers
that omit it) from "producer emitted nothing at all" (which fails
with `ValueError`, same as `stream()`).
"""
model = FakeListChatModel(responses=[""])
stream = model.stream_v2("test")
stream = model.stream_events("test", version="v3")
with pytest.raises(ValueError, match="No generation chunks"):
_ = stream.output
@@ -179,7 +179,7 @@ class TestV1ParityBasic:
def test_text_deltas_reconstruct_content(self) -> None:
model = FakeListChatModel(responses=["Hello!"])
stream = model.stream_v2("test")
stream = model.stream_events("test", version="v3")
deltas = list(stream.text)
content = stream.output.content
@@ -233,7 +233,7 @@ class TestV1ParityToolCalls:
def test_tool_calls_via_projection(self) -> None:
model = self._make_model()
stream = model.stream_v2("weather?")
stream = model.stream_events("weather?", version="v3")
finalized = stream.tool_calls.get()
assert len(finalized) == 1
assert finalized[0]["name"] == "get_weather"
@@ -276,7 +276,7 @@ class TestV1ParityUsage:
assert v1.usage_metadata["total_tokens"] == v2.usage_metadata["total_tokens"]
def test_usage_projection_matches(self) -> None:
stream = self._make_model().stream_v2("hello")
stream = self._make_model().stream_events("hello", version="v3")
# Drain so usage is available
for _ in stream.text:
pass
@@ -352,7 +352,7 @@ class TestV1ParityReasoning:
assert types_in_order == ["reasoning", "text"]
def test_reasoning_projection(self) -> None:
stream = self._make_model().stream_v2("think")
stream = self._make_model().stream_events("think", version="v3")
full_reasoning = str(stream.reasoning)
assert full_reasoning == "Let me think. Done."
@@ -366,7 +366,7 @@ class TestV1ParityError:
]
model = _ScriptedChunkModel(scripted_chunks=chunks, raise_after=True)
stream = model.stream_v2("boom")
stream = model.stream_events("boom", version="v3")
# Drain first; error may surface here or at .output access.
try:
list(stream.text)
@@ -382,7 +382,7 @@ class TestV1ParityError:
]
model = _ScriptedChunkModel(scripted_chunks=chunks, raise_after=True)
stream = await model.astream_v2("boom")
stream = await model.astream_events("boom", version="v3")
try:
async for _ in stream.text:
pass