-
Notifications
You must be signed in to change notification settings - Fork 753
feat(openai-agents): initial instrumentation; collect OpenAI agent traces and metrics #2966
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
adharshctr
wants to merge
69
commits into
traceloop:main
Choose a base branch
from
adharshctr:openai_agent_tracing
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 15 commits
Commits
Show all changes
69 commits
Select commit
Hold shift + click to select a range
6427318
Initialized the openai agent instrumentor
adharshctr 6c5d8ac
Added the init and version file
adharshctr 16fd549
Naming update
adharshctr e1846f1
Added instrumentation for openai agents
adharshctr a54e1c9
Added the lock file
adharshctr 7b6deb0
Naming change
adharshctr aea78cc
Change naming
adharshctr 4794b48
Naming change
adharshctr f045353
Added a sample app openai agent using litellm
adharshctr 99fc730
Added the documentation
adharshctr 25886d2
Added tests
adharshctr da48804
Upgraded the versions of openai agenst
adharshctr 7213f73
Add litellm dependency
adharshctr 91e0c4b
Added tests
adharshctr 83787d9
Added tests
adharshctr dd58935
Done suggested change
adharshctr 94b756f
Done suggested change
adharshctr a61e95f
Done suggested change
adharshctr 0c4ebca
Done the suggested change
adharshctr 45d5159
Done linting
adharshctr cbfbf2a
Refactor the code
adharshctr a8558d2
Added the tests
adharshctr b8d9c82
Added tests
adharshctr 347dca8
Added the vcrpy dependency
adharshctr 33e4b41
Updated the poetry
adharshctr bcf4ff4
Updated the dependencies
adharshctr 20680b2
Used the agentic semantic conventions
adharshctr fd9035e
Used the agentic semantic conventions
adharshctr 6d52258
Done code clean up
adharshctr 73b274b
Updated the suggestion
adharshctr 853fb1f
Updated the suggestion
adharshctr 357d612
Updated the suggestion
adharshctr 0ddeaf1
Updated the suggestion
adharshctr 0ef5203
Recorded the tests
adharshctr dc4ffe7
Revert "Recorded the tests"
adharshctr fecb65c
recorded the tests
adharshctr c39998b
Added Histograms for token usage and duration
adharshctr 8ee706b
Added the cassette yaml
adharshctr ab61c64
recorded the test
adharshctr 3f2b637
recorded the test
adharshctr 99f1050
recorded the test
adharshctr 3bfa955
recorded the test
adharshctr 4b4f954
recorded the test
adharshctr 936f003
recorded the test
adharshctr de10094
Removed api key
adharshctr ce0b86f
Added tests with WatsonX
adharshctr 9d5ad24
Added tests with WatsonX
adharshctr 508f13d
Recorded the tests
adharshctr 5727e52
Recorded the tests
adharshctr 042d1da
Recorded the tests
adharshctr 16e3929
Recorded the tests
adharshctr 6d66754
Recorded the tests
adharshctr 33281ff
recorded the tests
adharshctr bcf51c9
updated the python version
adharshctr d79d133
Rerecord the tests
adharshctr 85645c5
Rerecord the tests
adharshctr a2d944c
Rerecord the tests
adharshctr 5b81d6c
Replaced the test agent with Simple openai agent
adharshctr f487eaf
Done linting
adharshctr b39c453
Recoeded the tests with fix
adharshctr 1f64ccf
Recoeded the tests with fix
adharshctr 20a125e
Recoeded the tests with fix
adharshctr 95f0dc8
Recorded the tests
adharshctr 8614d0a
Updated the instrument version from 0.0.19
adharshctr 3e7f061
Updated the module
adharshctr 5a8f2c7
Updated the poetry
adharshctr 0c5c2dd
Updated the tests
adharshctr 13e6f9a
Updated the tests
adharshctr 8c754e0
Merge branch 'main' into openai_agent_tracing
nirga File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
11 changes: 11 additions & 0 deletions
11
packages/opentelemetry-instrumentation-openai_agents/.flake8
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
[flake8] | ||
exclude = | ||
.git, | ||
__pycache__, | ||
build, | ||
dist, | ||
.tox, | ||
venv, | ||
.venv, | ||
.pytest_cache | ||
max-line-length = 120 |
1 change: 1 addition & 0 deletions
1
packages/opentelemetry-instrumentation-openai_agents/.python-version
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
3.10 |
33 changes: 33 additions & 0 deletions
33
packages/opentelemetry-instrumentation-openai_agents/README.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
# OpenTelemetry OpenAI Agents Instrumentation | ||
|
||
<a href="https://pypi.org/project/opentelemetry-instrumentation-openai_agents/"> | ||
<img src="https://badge.fury.io/py/opentelemetry-instrumentation-openai_agents.svg"> | ||
</a> | ||
|
||
This library enables tracing of agentic workflows implemented using the [OpenAI Agents framework](https://github.com/openai/openai-agents-python), allowing visibility into agent reasoning, tool usage, and decision-making steps. | ||
|
||
## Installation | ||
|
||
```bash | ||
pip install opentelemetry-instrumentation-openai_agents | ||
``` | ||
|
||
## Example usage | ||
|
||
```python | ||
from opentelemetry.instrumentation.openai_agents import OpenAIAgentsInstrumentor | ||
|
||
OpenAIAgentsInstrumentor().instrument() | ||
``` | ||
|
||
## Privacy | ||
|
||
**By default, this instrumentation logs prompts, completions, and embeddings to span attributes**. This gives you a clear visibility into how your LLM application is working, and can make it easy to debug and evaluate the quality of the outputs. | ||
|
||
However, you may want to disable this logging for privacy reasons, as they may contain highly sensitive data from your users. You may also simply want to reduce the size of your traces. | ||
|
||
To disable logging, set the `TRACELOOP_TRACE_CONTENT` environment variable to `false`. | ||
|
||
```bash | ||
TRACELOOP_TRACE_CONTENT=false | ||
``` |
8 changes: 8 additions & 0 deletions
8
...try-instrumentation-openai_agents/opentelemetry/instrumentation/openai_agents/__init__.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
"""OpenTelemetry OpenAI Agents instrumentation""" | ||
|
||
from opentelemetry.instrumentation.openai_agents.version import __version__ | ||
from opentelemetry.instrumentation.openai_agents.instrumentation import ( | ||
OpenAIAgentsInstrumentor, | ||
) | ||
|
||
__all__ = ["OpenAIAgentsInstrumentor", "__version__"] |
87 changes: 87 additions & 0 deletions
87
...trumentation-openai_agents/opentelemetry/instrumentation/openai_agents/instrumentation.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
from typing import Collection | ||
|
||
from wrapt import wrap_function_wrapper | ||
from opentelemetry.trace import SpanKind, get_tracer, Tracer | ||
from opentelemetry.trace.status import Status, StatusCode | ||
from opentelemetry.instrumentation.utils import unwrap | ||
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor | ||
from opentelemetry.instrumentation.openai_agents.version import __version__ | ||
from opentelemetry.semconv_ai import SpanAttributes | ||
from .patch import ( | ||
extract_agent_details, | ||
set_model_settings_span_attributes, | ||
extract_run_config_details, | ||
set_prompt_attributes, | ||
set_response_content_span_attribute, | ||
set_token_usage_span_attributes, | ||
) | ||
|
||
|
||
_instruments = ("openai_agents >= 0.0.2",) | ||
|
||
|
||
class OpenAIAgentsInstrumentor(BaseInstrumentor): | ||
"""An instrumentor for OpenAI Agents SDK.""" | ||
|
||
def instrumentation_dependencies(self) -> Collection[str]: | ||
return _instruments | ||
|
||
def _instrument(self, **kwargs): | ||
tracer_provider = kwargs.get("tracer_provider") | ||
tracer = get_tracer(__name__, __version__, tracer_provider) | ||
|
||
wrap_function_wrapper( | ||
"agents.run", "Runner._get_new_response", _wrap_agent_run(tracer) | ||
) | ||
|
||
def _uninstrument(self, **kwargs): | ||
unwrap("agents.run.Runner", "_get_new_response") | ||
|
||
|
||
def with_tracer_wrapper(func): | ||
|
||
def _with_tracer(tracer): | ||
def wrapper(wrapped, instance, args, kwargs): | ||
return func(tracer, wrapped, instance, args, kwargs) | ||
|
||
return wrapper | ||
|
||
return _with_tracer | ||
|
||
|
||
@with_tracer_wrapper | ||
async def _wrap_agent_run(tracer: Tracer, wrapped, instance, args, kwargs): | ||
agent = args[0] | ||
agent_name = getattr(agent, "name", "agent") | ||
model_name = getattr(getattr(agent, "model", None), "model", | ||
"unknown_model") | ||
|
||
with tracer.start_as_current_span( | ||
f"openai_agents.{agent_name}", | ||
kind=SpanKind.INTERNAL, | ||
attributes={ | ||
SpanAttributes.LLM_SYSTEM: "openai", | ||
SpanAttributes.LLM_REQUEST_MODEL: model_name, | ||
}, | ||
) as span: | ||
try: | ||
extract_agent_details(agent, span) | ||
set_model_settings_span_attributes(agent, span) | ||
|
||
if len(args) > 7: | ||
adharshctr marked this conversation as resolved.
Show resolved
Hide resolved
|
||
extract_run_config_details(args[7], span) | ||
|
||
if len(args) > 2: | ||
set_prompt_attributes(span, args[2]) | ||
|
||
response = await wrapped(*args, **kwargs) | ||
|
||
set_response_content_span_attribute(response, span) | ||
set_token_usage_span_attributes(response, span) | ||
|
||
span.set_status(Status(StatusCode.OK)) | ||
return response | ||
|
||
except Exception as e: | ||
span.set_status(Status(StatusCode.ERROR, str(e))) | ||
raise |
119 changes: 119 additions & 0 deletions
119
...emetry-instrumentation-openai_agents/opentelemetry/instrumentation/openai_agents/patch.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
from opentelemetry.semconv_ai import SpanAttributes | ||
|
||
|
||
def extract_agent_details(test_agent, span): | ||
if test_agent is None: | ||
return | ||
agent = getattr(test_agent, "agent", test_agent) | ||
if agent is None: | ||
return | ||
|
||
attributes = {} | ||
agent_dict = vars(agent) | ||
|
||
for key, value in agent_dict.items(): | ||
if value is not None and isinstance(value, (str, int, float, bool)): | ||
attributes[f"openai.agent.{key}"] = value | ||
# Optional: handle known short lists | ||
elif isinstance(value, list) and len(value) != 0: | ||
attributes[f"openai.agent.{key}_count"] = len(value) | ||
|
||
if attributes: | ||
span.set_attributes(attributes) | ||
|
||
|
||
def set_model_settings_span_attributes(agent, span): | ||
|
||
if not hasattr(agent, "model_settings") or agent.model_settings is None: | ||
return | ||
|
||
model_settings = agent.model_settings | ||
settings_dict = vars(model_settings) | ||
|
||
key_to_span_attr = { | ||
"max_tokens": SpanAttributes.LLM_REQUEST_MAX_TOKENS, | ||
"temperature": SpanAttributes.LLM_REQUEST_TEMPERATURE, | ||
"top_p": SpanAttributes.LLM_REQUEST_TOP_P, | ||
} | ||
|
||
for key, value in settings_dict.items(): | ||
if value is not None: | ||
span_attr = key_to_span_attr.get(key, f"openai.agent.model.{key}") | ||
span.set_attribute(span_attr, value) | ||
|
||
|
||
def extract_run_config_details(run_config, span): | ||
if run_config is None: | ||
return | ||
|
||
config_dict = vars(run_config) | ||
attributes = {} | ||
|
||
for key, value in config_dict.items(): | ||
|
||
if value is not None and isinstance(value, (str, int, float, bool)): | ||
attributes[f"openai.agent.{key}"] = value | ||
elif isinstance(value, list) and len(value) != 0: | ||
attributes[f"openai.agent.{key}_count"] = len(value) | ||
|
||
if attributes: | ||
span.set_attributes(attributes) | ||
|
||
|
||
def set_prompt_attributes(span, message_history): | ||
if not message_history: | ||
return | ||
|
||
for msg in message_history: | ||
adharshctr marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if isinstance(msg, dict): | ||
|
||
role = msg.get("role", "user") | ||
content = msg.get("content", "") | ||
|
||
span.set_attribute(f"{SpanAttributes.LLM_PROMPTS}.role", role) | ||
span.set_attribute(f"{SpanAttributes.LLM_PROMPTS}.content", content) | ||
|
||
|
||
def set_response_content_span_attribute(response, span): | ||
|
||
if hasattr(response, "output") and isinstance(response.output, list): | ||
for output_message in response.output: | ||
adharshctr marked this conversation as resolved.
Show resolved
Hide resolved
|
||
# Extract role and type from output_message | ||
role = getattr(output_message, "role", None) | ||
msg_type = getattr(output_message, "type", None) | ||
|
||
if role: | ||
span.set_attribute( | ||
f"{SpanAttributes.LLM_COMPLETIONS}.role", role) | ||
if msg_type: | ||
span.set_attribute( | ||
f"{SpanAttributes.LLM_COMPLETIONS}.type", msg_type) | ||
|
||
if hasattr(output_message, "content") and isinstance( | ||
output_message.content, list | ||
): | ||
for content_item in output_message.content: | ||
if hasattr(content_item, "text"): | ||
span.set_attribute( | ||
f"{SpanAttributes.LLM_COMPLETIONS}.content", | ||
content_item.text, | ||
) | ||
|
||
|
||
def set_token_usage_span_attributes(response, span): | ||
if hasattr(response, "usage"): | ||
usage = response.usage | ||
input_tokens = getattr(usage, "input_tokens", None) | ||
output_tokens = getattr(usage, "output_tokens", None) | ||
total_tokens = getattr(usage, "total_tokens", None) | ||
|
||
if input_tokens is not None: | ||
span.set_attribute( | ||
SpanAttributes.LLM_USAGE_PROMPT_TOKENS, input_tokens) | ||
if output_tokens is not None: | ||
span.set_attribute( | ||
SpanAttributes.LLM_USAGE_COMPLETION_TOKENS, output_tokens | ||
) | ||
if total_tokens is not None: | ||
span.set_attribute( | ||
SpanAttributes.LLM_USAGE_TOTAL_TOKENS, total_tokens) |
1 change: 1 addition & 0 deletions
1
...etry-instrumentation-openai_agents/opentelemetry/instrumentation/openai_agents/version.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
__version__ = "0.40.7" |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.