|
3 | 3 |
|
4 | 4 | import pytest |
5 | 5 |
|
| 6 | +import langfuse.openai as lf_openai_module |
6 | 7 | from langfuse._client.attributes import LangfuseOtelSpanAttributes |
7 | 8 | from langfuse.openai import openai as lf_openai |
8 | 9 |
|
@@ -39,6 +40,17 @@ async def _stream(self, items): |
39 | 40 | yield item |
40 | 41 |
|
41 | 42 |
|
| 43 | +class DummyGeneration: |
| 44 | + def __init__(self) -> None: |
| 45 | + self.end_calls = 0 |
| 46 | + |
| 47 | + def update(self, **kwargs): |
| 48 | + return self |
| 49 | + |
| 50 | + def end(self) -> None: |
| 51 | + self.end_calls += 1 |
| 52 | + |
| 53 | + |
42 | 54 | def _make_chat_stream_chunks(): |
43 | 55 | usage = SimpleNamespace(prompt_tokens=3, completion_tokens=1, total_tokens=4) |
44 | 56 |
|
@@ -76,6 +88,24 @@ def _make_chat_stream_chunks(): |
76 | 88 | ] |
77 | 89 |
|
78 | 90 |
|
| 91 | +def _make_single_chunk_stream(): |
| 92 | + return SimpleNamespace( |
| 93 | + model="gpt-4o-mini", |
| 94 | + choices=[ |
| 95 | + SimpleNamespace( |
| 96 | + delta=SimpleNamespace( |
| 97 | + role="assistant", |
| 98 | + content="2", |
| 99 | + function_call=None, |
| 100 | + tool_calls=None, |
| 101 | + ), |
| 102 | + finish_reason="stop", |
| 103 | + ) |
| 104 | + ], |
| 105 | + usage=None, |
| 106 | + ) |
| 107 | + |
| 108 | + |
79 | 109 | def test_chat_completion_exports_generation_span( |
80 | 110 | langfuse_memory_client, get_span, json_attr |
81 | 111 | ): |
@@ -439,6 +469,50 @@ async def test_openai_async_stream_supports_anext( |
439 | 469 | } |
440 | 470 |
|
441 | 471 |
|
| 472 | +def test_fallback_sync_stream_finalizes_once(): |
| 473 | + resource = SimpleNamespace(object="Completions", type="chat") |
| 474 | + generation = DummyGeneration() |
| 475 | + |
| 476 | + def fallback_stream(): |
| 477 | + yield _make_single_chunk_stream() |
| 478 | + |
| 479 | + wrapper = lf_openai_module.LangfuseResponseGeneratorSync( |
| 480 | + resource=resource, |
| 481 | + response=fallback_stream(), |
| 482 | + generation=generation, |
| 483 | + ) |
| 484 | + |
| 485 | + list(wrapper) |
| 486 | + |
| 487 | + with pytest.raises(StopIteration): |
| 488 | + next(wrapper) |
| 489 | + |
| 490 | + assert generation.end_calls == 1 |
| 491 | + |
| 492 | + |
| 493 | +@pytest.mark.asyncio |
| 494 | +async def test_fallback_async_stream_finalizes_once(): |
| 495 | + resource = SimpleNamespace(object="Completions", type="chat") |
| 496 | + generation = DummyGeneration() |
| 497 | + |
| 498 | + async def fallback_stream(): |
| 499 | + yield _make_single_chunk_stream() |
| 500 | + |
| 501 | + wrapper = lf_openai_module.LangfuseResponseGeneratorAsync( |
| 502 | + resource=resource, |
| 503 | + response=fallback_stream(), |
| 504 | + generation=generation, |
| 505 | + ) |
| 506 | + |
| 507 | + async for _ in wrapper: |
| 508 | + pass |
| 509 | + |
| 510 | + with pytest.raises(StopAsyncIteration): |
| 511 | + await wrapper.__anext__() |
| 512 | + |
| 513 | + assert generation.end_calls == 1 |
| 514 | + |
| 515 | + |
442 | 516 | def test_embedding_exports_dimensions_and_count( |
443 | 517 | langfuse_memory_client, get_span, json_attr |
444 | 518 | ): |
|
0 commit comments