Add OpenTelemetry instrumentation for LiteLLM#88
Add OpenTelemetry instrumentation for LiteLLM#88whoIam0987 wants to merge 3 commits intoalibaba:mainfrom
Conversation
c26059b to
d8affed
Compare
There was a problem hiding this comment.
Pull request overview
This pull request adds OpenTelemetry instrumentation for the LiteLLM library, which provides a unified interface to 100+ LLM providers. The instrumentation captures telemetry data for LLM operations including completions, embeddings, streaming, tool calls, and retry mechanisms.
Changes:
- Added new instrumentation package for LiteLLM with support for sync/async completion and embedding APIs
- Implemented streaming response wrappers with proper span lifecycle management
- Added comprehensive test suite covering various LiteLLM features including tool calls, retries, and error handling
Reviewed changes
Copilot reviewed 19 out of 19 changed files in this pull request and generated 19 comments.
Show a summary per file
| File | Description |
|---|---|
__init__.py |
Main instrumentor class that wraps LiteLLM functions and manages telemetry handlers |
_wrapper.py |
Completion wrappers for sync/async calls with streaming support |
_embedding_wrapper.py |
Embedding operation wrappers for sync/async calls |
_stream_wrapper.py |
Stream response wrappers handling chunk accumulation and finalization |
_utils.py |
Utility functions for message conversion, provider parsing, and invocation creation |
version.py, package.py |
Package metadata and dependency declarations |
pyproject.toml |
Package configuration with build system and dependencies |
README.rst |
Documentation for installation, configuration, and usage |
test_*.py |
Comprehensive test suite covering completions, embeddings, streaming, tools, retries, and errors |
test-requirements.txt |
Test dependency specifications |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| # Create invocation object |
There was a problem hiding this comment.
Duplicate comment. The comment "Create invocation object" appears twice consecutively on lines 63 and 65. Remove one of these duplicate lines.
| # Create invocation object |
|
|
||
| # Create invocation object |
There was a problem hiding this comment.
Duplicate comment. The comment "Create invocation object" appears twice consecutively on lines 164 and 166. Remove one of these duplicate lines.
| # Create invocation object |
|
|
||
| # For streaming, we need special handling |
There was a problem hiding this comment.
Duplicate comment. The comment "For streaming, we need special handling" appears twice consecutively on lines 85 and 87. Remove one of these duplicate lines.
| # For streaming, we need special handling |
| stream_wrapper = AsyncStreamWrapper( | ||
| stream=response, | ||
| span=invocation.span, # For TTFT tracking | ||
| callback=lambda span, | ||
| last_chunk, | ||
| error: self._handle_stream_end_with_handler( | ||
| invocation, last_chunk, error, stream_wrapper | ||
| ), | ||
| ) |
There was a problem hiding this comment.
Potential circular reference in lambda callback. The lambda function references stream_wrapper (line 366) before it's assigned (line 360). While Python closures can handle this due to lazy evaluation, this creates a circular reference that could potentially cause issues with garbage collection. Consider restructuring to avoid the circular reference, perhaps by creating the callback after the AsyncStreamWrapper is instantiated or using a different callback pattern.
There was a problem hiding this comment.
Set the callback after instantiating the stream_wrapper.
| * ``ENABLE_LITELLM_INSTRUMENTOR``: Enable/disable instrumentation (default: true) | ||
| * ``ARMS_LITELLM_INSTRUMENTATION_ENABLED``: Alternative enable/disable flag (default: true) |
There was a problem hiding this comment.
The documentation claims that the ENABLE_LITELLM_INSTRUMENTOR environment variable can be used to enable/disable instrumentation, but this variable is never referenced in the actual code. Only ARMS_LITELLM_INSTRUMENTATION_ENABLED is actually implemented. Either implement support for this environment variable or remove it from the documentation to avoid confusion.
| * ``ENABLE_LITELLM_INSTRUMENTOR``: Enable/disable instrumentation (default: true) | |
| * ``ARMS_LITELLM_INSTRUMENTATION_ENABLED``: Alternative enable/disable flag (default: true) | |
| * ``ARMS_LITELLM_INSTRUMENTATION_ENABLED``: Enable/disable instrumentation (default: true) |
There was a problem hiding this comment.
All of ARMS_LITELLM_INSTRUMENTATION_ENABLED were replaced with ENABLE_LITELLM_INSTRUMENTOR
| suppress_token = context.attach( | ||
| context.set_value(SUPPRESS_LLM_SDK_KEY, True) | ||
| ) | ||
| except Exception: |
There was a problem hiding this comment.
'except' clause does nothing but pass and there is no explanatory comment.
| except Exception: | ||
| pass |
There was a problem hiding this comment.
'except' clause does nothing but pass and there is no explanatory comment.
| except Exception: | |
| pass | |
| except Exception as decode_error: | |
| # Ignore JSON parsing errors and fall back to the raw string, | |
| # but log at debug level for diagnosability. | |
| logger.debug( | |
| "Failed to JSON-decode tool call arguments %r: %s", | |
| arguments, | |
| decode_error, | |
| ) |
| except Exception: | ||
| pass |
There was a problem hiding this comment.
'except' clause does nothing but pass and there is no explanatory comment.
| except Exception: | |
| pass | |
| except Exception as handler_error: | |
| # Swallow exceptions from telemetry failure reporting, but log them for diagnostics. | |
| logger.debug( | |
| "Error while reporting LLM failure in _handle_stream_end_with_handler: %s", | |
| handler_error, | |
| ) |
| suppress_token = context.attach( | ||
| context.set_value(SUPPRESS_LLM_SDK_KEY, True) | ||
| ) | ||
| except Exception: |
There was a problem hiding this comment.
'except' clause does nothing but pass and there is no explanatory comment.
| context.set_value(SUPPRESS_LLM_SDK_KEY, True) | ||
| ) | ||
| except Exception: | ||
| pass |
There was a problem hiding this comment.
'except' clause does nothing but pass and there is no explanatory comment.
| pass | |
| # Failed to attach suppression context; proceed without suppression. | |
| logger.exception( | |
| "Failed to attach suppression context for LiteLLM instrumentation" | |
| ) |
d8affed to
002d956
Compare
Cirilla-zmh
left a comment
There was a problem hiding this comment.
Thanks for the contribution! Please address the remaining comments so we can move this PR forward.
| @@ -0,0 +1,8 @@ | |||
| litellm>=1.0.0 | |||
There was a problem hiding this comment.
Could you please add these tests as github workflows?
Just refer to:
loongsuite-python-agent/CONTRIBUTING-loongsuite-zh.md
Lines 290 to 291 in 043b0a9
loongsuite-python-agent/CONTRIBUTING-loongsuite-zh.md
Lines 225 to 240 in 043b0a9
| "License :: OSI Approved :: Apache Software License", | ||
| "Programming Language :: Python", | ||
| "Programming Language :: Python :: 3", | ||
| "Programming Language :: Python :: 3.8", |
There was a problem hiding this comment.
We didn't support Python 3.8 in loongsuite any more.
| pytest | ||
| pytest-asyncio | ||
| openai | ||
| -e aliyun-semantic-conventions |
There was a problem hiding this comment.
I believe it doesn't work here. Could you have a check again?
| Test handling when max_tokens is exceeded. | ||
| """ | ||
|
|
||
| os.environ["DASHSCOPE_API_KEY"] = os.environ.get( |
There was a problem hiding this comment.
pytest.vcr may help to record LLM-releated requests and responses so that you could replay them next time you run the same tests.
Please check:
Description
Instrument Litellm with genai util.
Fixes # (issue)
Type of change
Please delete options that are not relevant.
How Has This Been Tested?
Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration
Does This PR Require a Core Repo Change?
Checklist:
See contributing.md for styleguide, changelog guidelines, and more.