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 @@ -2,13 +2,14 @@

An OpenTelemetry instrumentation for the `openai` client library.

This instrumentation currently only supports instrumenting the Chat completions APIs.
This instrumentation currently supports instrumenting the chat completions and the embeddings APIs.

We currently support the following features:
- `sync` and `async` chat completions
- Streaming support
- Functions calling with tools
- Streaming support for chat completions
- Functions calling with tools for chat completions
- Client side metrics
- Embeddings API calls
- Following 1.28.0 Gen AI Semantic Conventions

## Installation
Expand Down Expand Up @@ -60,7 +61,7 @@ log events instead of span events.
### Elastic specific semantic conventions

- New `embeddings` value for `gen_ai.operation.name`
- New `gen_ai.request.encoding_format` attribute with openai specific values `[float, base64]`
- New `gen_ai.request.encoding_formats` attribute with openai specific values `[[float], [base64]]`

## Development

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@
EVENT_GEN_AI_SYSTEM_MESSAGE = "gen_ai.system.message"
EVENT_GEN_AI_TOOL_MESSAGE = "gen_ai.tool.message"

# elastic specific attributes
GEN_AI_REQUEST_ENCODING_FORMAT = "gen_ai.request.encoding_format"
# not yet released attributes
GEN_AI_REQUEST_ENCODING_FORMATS = "gen_ai.request.encoding_formats"

# As this is only used for a type annotation, only import from openai module
# when running type checker like pyright since we otherwise don't want to import
Expand Down Expand Up @@ -204,7 +204,7 @@ def _get_embeddings_span_attributes_from_wrapper(instance, kwargs) -> Attributes
span_attributes.update(_attributes_from_client(client))

if (encoding_format := kwargs.get("encoding_format")) is not None:
span_attributes[GEN_AI_REQUEST_ENCODING_FORMAT] = encoding_format
span_attributes[GEN_AI_REQUEST_ENCODING_FORMATS] = [encoding_format]

return span_attributes

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

import openai
import pytest
from opentelemetry.instrumentation.openai.helpers import GEN_AI_REQUEST_ENCODING_FORMAT
from opentelemetry.instrumentation.openai.helpers import GEN_AI_REQUEST_ENCODING_FORMATS
from opentelemetry.trace import SpanKind, StatusCode
from opentelemetry.semconv._incubating.attributes.gen_ai_attributes import (
GEN_AI_OPERATION_NAME,
Expand Down Expand Up @@ -117,7 +117,7 @@ def test_all_the_client_options(provider_str, model, input_tokens, duration, tra
GEN_AI_REQUEST_MODEL: model,
GEN_AI_SYSTEM: "openai",
GEN_AI_RESPONSE_MODEL: model,
GEN_AI_REQUEST_ENCODING_FORMAT: "float",
GEN_AI_REQUEST_ENCODING_FORMATS: ("float",),
Copy link
Member

Choose a reason for hiding this comment

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

Possibly showing my ignorance here. The code sets value of this attribute to a list, but compares it to a tuple here. Why does the == comparison return True?

>>> {"foo":1, "bar":["float"]} == {"foo":1, "bar":("float",)}
False

Copy link
Member Author

@xrmx xrmx Nov 27, 2024

Choose a reason for hiding this comment

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

Lists ends up as tuples for some reason in tests, haven't digged much on where this happens.

GEN_AI_USAGE_INPUT_TOKENS: input_tokens,
SERVER_ADDRESS: provider.server_address,
SERVER_PORT: provider.server_port,
Expand Down Expand Up @@ -159,7 +159,7 @@ def test_all_the_client_options_integration(provider_str, model, trace_exporter,
GEN_AI_REQUEST_MODEL: model,
GEN_AI_SYSTEM: "openai",
GEN_AI_RESPONSE_MODEL: model,
GEN_AI_REQUEST_ENCODING_FORMAT: "float",
GEN_AI_REQUEST_ENCODING_FORMATS: ("float",),
GEN_AI_USAGE_INPUT_TOKENS: response.usage.prompt_tokens,
SERVER_ADDRESS: provider.server_address,
SERVER_PORT: provider.server_port,
Expand Down Expand Up @@ -312,7 +312,7 @@ async def test_async_all_the_client_options(
GEN_AI_REQUEST_MODEL: model,
GEN_AI_SYSTEM: "openai",
GEN_AI_RESPONSE_MODEL: model,
GEN_AI_REQUEST_ENCODING_FORMAT: "float",
GEN_AI_REQUEST_ENCODING_FORMATS: ("float",),
GEN_AI_USAGE_INPUT_TOKENS: input_tokens,
SERVER_ADDRESS: provider.server_address,
SERVER_PORT: provider.server_port,
Expand Down Expand Up @@ -355,7 +355,7 @@ async def test_async_all_the_client_options_integration(provider_str, model, tra
GEN_AI_REQUEST_MODEL: model,
GEN_AI_SYSTEM: "openai",
GEN_AI_RESPONSE_MODEL: model,
GEN_AI_REQUEST_ENCODING_FORMAT: "float",
GEN_AI_REQUEST_ENCODING_FORMATS: ("float",),
GEN_AI_USAGE_INPUT_TOKENS: response.usage.prompt_tokens,
SERVER_ADDRESS: provider.server_address,
SERVER_PORT: provider.server_port,
Expand Down