Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion langfuse/openai.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import os
import types
from collections import defaultdict
from collections.abc import Iterator
from dataclasses import dataclass
from datetime import datetime
from inspect import isawaitable, isclass
Expand Down Expand Up @@ -311,7 +312,12 @@ def __init__(
self.args["trace_id"] = trace_id
self.args["parent_observation_id"] = parent_observation_id

self.kwargs = kwargs
# Materialize one-shot iterators (e.g. a generator of messages) so that
# reading them for tracing does not leave OpenAI with an empty argument.
self.kwargs = {
key: list(value) if isinstance(value, Iterator) else value
for key, value in kwargs.items()
}

def get_langfuse_args(self) -> Any:
return {**self.args, **self.kwargs}
Expand Down
37 changes: 37 additions & 0 deletions tests/unit/test_openai.py
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,43 @@ def test_chat_completion_exports_generation_span(
}


def test_chat_completion_does_not_consume_messages_iterator(
langfuse_memory_client, get_span, json_attr
):
openai_client = lf_openai.OpenAI(api_key="test")
response = ChatCompletion.model_validate(
{
"id": "chatcmpl-1",
"object": "chat.completion",
"created": 1,
"model": "gpt-4o-mini",
"choices": [
{
"index": 0,
"message": {"role": "assistant", "content": "hi"},
"finish_reason": "stop",
}
],
}
)
messages = [{"role": "user", "content": "hello"}]

with patch.object(
openai_client.chat.completions, "_post", return_value=response
) as post:
openai_client.chat.completions.create(
name="unit-openai-iterator",
model="gpt-4o-mini",
messages=(message for message in messages),
)

assert post.call_args.kwargs["body"]["messages"] == messages

langfuse_memory_client.flush()
span = get_span("unit-openai-iterator")
assert json_attr(span, LangfuseOtelSpanAttributes.OBSERVATION_INPUT) == messages


def test_chat_completion_with_none_choices_does_not_crash(
langfuse_memory_client, get_span, json_attr
):
Expand Down
Loading