Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,10 @@ def _build_from_streaming_response(
aggregated_usage_metadata = defaultdict(int)
model_version = None
for chunk in response:
try:
span.add_event("llm.content.completion.chunk")
except Exception:
pass
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The try/except block around span.add_event swallows all exceptions. Consider logging the exception (even at a debug level) so that unexpected failures in adding events aren’t silently ignored.

Suggested change
pass
logger.debug("Exception in span.add_event: ", exc_info=True)

# Important: do all processing in a separate sync function, that is
# wrapped in @dont_throw. If we did it here, the @dont_throw on top of
# this function would not be able to catch the errors, as they are
Expand Down Expand Up @@ -430,6 +434,10 @@ async def _abuild_from_streaming_response(
aggregated_usage_metadata = defaultdict(int)
model_version = None
async for chunk in response:
try:
span.add_event("llm.content.completion.chunk")
except Exception:
pass
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly, the async version wraps span.add_event in a try/except without logging. It would be beneficial to log errors for visibility, even if instrumentation should continue.

Suggested change
pass
logger.exception('Failed to add event to span')

# Important: do all processing in a separate sync function, that is
# wrapped in @dont_throw. If we did it here, the @dont_throw on top of
# this function would not be able to catch the errors, as they are
Expand Down
6 changes: 3 additions & 3 deletions src/lmnr/sdk/laminar.py
Original file line number Diff line number Diff line change
Expand Up @@ -671,12 +671,12 @@ def set_span_attributes(
instrumentation.
Example:
```python
with L.start_as_current_span(
with Laminar.start_as_current_span(
name="my_span_name", input=input["messages"], span_type="LLM"
):
response = await my_custom_call_to_openai(input)
L.set_span_output(response["choices"][0]["message"]["content"])
L.set_span_attributes({
Laminar.set_span_output(response["choices"][0]["message"]["content"])
Laminar.set_span_attributes({
Attributes.PROVIDER: 'openai',
Attributes.REQUEST_MODEL: input["model"],
Attributes.RESPONSE_MODEL: response["model"],
Expand Down
4 changes: 4 additions & 0 deletions tests/test_google_genai.py
Original file line number Diff line number Diff line change
Expand Up @@ -847,8 +847,10 @@ def test_google_genai_streaming(span_exporter: InMemorySpanExporter):
],
)
final_response = ""
chunk_count = 0
for chunk in stream:
final_response += chunk.text or ""
chunk_count += 1

spans = span_exporter.get_finished_spans()
assert len(spans) == 1
Expand Down Expand Up @@ -885,6 +887,8 @@ def test_google_genai_streaming(span_exporter: InMemorySpanExporter):
assert span.attributes["gen_ai.usage.input_tokens"] == 7
assert span.attributes["gen_ai.usage.output_tokens"] == 166
assert span.attributes["llm.usage.total_tokens"] == 175 # 173 + 2 (thinking tokens)
assert len(span.events) == chunk_count
assert all(event.name == "llm.content.completion.chunk" for event in span.events)


@pytest.mark.vcr
Expand Down