Skip to content
Closed
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 @@ -23,10 +23,13 @@ pip install elastic-opentelemetry-instrumentation-openai
This instrumentation supports *zero-code* / *autoinstrumentation*:

```
opentelemetry-instrument python use_openai.py
# "examples/chat.py" is a simple script using the openai client library.
Copy link
Contributor

Choose a reason for hiding this comment

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

maybe just say something like this? that way we don't need to drift or repeat instructions about things like ENV vars?

See our examples for a quick start

Copy link
Member Author

@xrmx xrmx Dec 18, 2024

Choose a reason for hiding this comment

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

Sorry, I don't understand 😅, do you mean to replace this block with a reference to the examples or just replace this comment with "see our examples for a quick start"?

Copy link
Contributor

Choose a reason for hiding this comment

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

made a top-level comment here #45 (comment)

I'll propose an alternative PR here and into elastic-otel-python..

cd examples

opentelemetry-instrument python chat.py

# You can record more information about prompts as log events by enabling content capture.
OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true opentelemetry-instrument python use_openai.py
OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true opentelemetry-instrument python chat.py
```

Or manual instrumentation:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# OpenAI Zero-Code Instrumentation Examples

This is an example of how to instrument OpenAI calls with zero code changes,
using `opentelemetry-instrument` included in the Elastic Distribution of
OpenTelemetry Python ([EDOT Python][edot-python]).

When OpenAI examples run, they export traces, metrics and logs to an OTLP
compatible endpoint. Traces and metrics include details such as the model used
and the duration of the LLM request. In the case of chat, Logs capture the
request and the generated response. The combination of these provide a
comprehensive view of the performance and behavior of your OpenAI usage.

## Install

First, set up a Python virtual environment like this:
```bash
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
```

Next, install [EDOT Python][edot-python] and dotenv which is a portable way to
load environment variables.
```bash
pip install "python-dotenv[cli]" elastic-opentelemetry
```

Finally, run `edot-bootstrap` which analyzes the code to add relevant
instrumentation, to record traces, metrics and logs.
```bash
edot-bootstrap --action=install
```

## Configure

Copy [default.env](default.env) to `.env` and update the file with your `OPENAI_API_KEY`.

An OTLP compatible endpoint should be listening for traces, metrics and logs on
`http://localhost:4317`. If not, update `OTEL_EXPORTER_OTLP_ENDPOINT` as well.

## Run

There are two examples, and they run the same way:

### Chat

[chat.py](chat.py) asks the LLM a geography question and prints the response.

Run it like this:
```bash
dotenv run -- opentelemetry-instrument python chat.py
```

You should see something like "Atlantic Ocean" unless your LLM hallucinates!

### Embeddings


[embeddings.py](embeddings.py) creates in-memory VectorDB embeddings about
Elastic products. Then, it searches for one similar to a question.

Run it like this:
```bash
dotenv run -- opentelemetry-instrument python embeddings.py
```

You should see something like "Connectors" unless your LLM hallucinates!

---

[edot-python]: https://github.com/elastic/elastic-otel-python/blob/main/docs/get-started.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import os

import openai

CHAT_MODEL = os.environ.get("CHAT_MODEL", "gpt-4o-mini")


def main():
client = openai.Client()

messages = [
{
"role": "user",
"content": "Answer in up to 3 words: Which ocean contains Bouvet Island?",
}
]

chat_completion = client.chat.completions.create(model=CHAT_MODEL, messages=messages)
print(chat_completion.choices[0].message.content)


if __name__ == "__main__":
main()
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Update this with your real OpenAI API key
OPENAI_API_KEY=sk-YOUR_API_KEY

# Uncomment to use Ollama instead of OpenAI
# OPENAI_BASE_URL=http://localhost:11434/v1
# OPENAI_API_KEY=unused
# CHAT_MODEL=qwen2.5:0.5b
# EMBEDDINGS_MODEL=all-minilm:33m

# Uncomment and change to your OTLP endpoint
# OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317
# OTEL_EXPORTER_OTLP_PROTOCOL=grpc

OTEL_SERVICE_NAME=opentelemetry-python-openai
Copy link
Member Author

Choose a reason for hiding this comment

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

@codefromthecrypt So this should be openai-example right?

Copy link
Contributor

Choose a reason for hiding this comment

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

yep


# Change to 'false' to hide prompt and completion content
OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true
# Change to affect behavior of which resources are detected
OTEL_EXPERIMENTAL_RESOURCE_DETECTORS=process_runtime,os,otel,telemetry_distro
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import os

import numpy as np
import openai

EMBEDDINGS_MODEL = os.environ.get("EMBEDDINGS_MODEL", "text-embedding-3-small")


def main():
client = openai.Client()

products = [
"Search: Ingest your data, and explore Elastic's machine learning and retrieval augmented generation (RAG) capabilities."
"Observability: Unify your logs, metrics, traces, and profiling at scale in a single platform.",
"Security: Protect, investigate, and respond to cyber threats with AI-driven security analytics."
"Elasticsearch: Distributed, RESTful search and analytics.",
"Kibana: Visualize your data. Navigate the Stack.",
"Beats: Collect, parse, and ship in a lightweight fashion.",
"Connectors: Connect popular databases, file systems, collaboration tools, and more.",
"Logstash: Ingest, transform, enrich, and output.",
]

# Generate embeddings for each product. Keep them in an array instead of a vector DB.
product_embeddings = []
for product in products:
product_embeddings.append(create_embedding(client, product))

query_embedding = create_embedding(client, "What can help me connect to OpenAI?")

# Calculate cosine similarity between the query and document embeddings
similarities = []
for product_embedding in product_embeddings:
similarity = np.dot(query_embedding, product_embedding) / (
np.linalg.norm(query_embedding) * np.linalg.norm(product_embedding)
)
similarities.append(similarity)

# Get the index of the most similar document
most_similar_index = np.argmax(similarities)

print(products[most_similar_index])


def create_embedding(client, text):
return client.embeddings.create(input=[text], model=EMBEDDINGS_MODEL, encoding_format="float").data[0].embedding


if __name__ == "__main__":
main()
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
openai~=1.57.2
numpy~=2.2.0
2 changes: 1 addition & 1 deletion scripts/license_headers_check.sh
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

if [ $# -eq 0 ]
then
FILES=$(find . \( -name "*.py" -o -name "*.c" -o -name "*.sh" \) -size +1c -not -path "./dist/*" -not -path "./build/*" -not -path "./venv*/*")
FILES=$(find . \( -name "*.py" -o -name "*.c" -o -name "*.sh" \) -size +1c -not -path "./dist/*" -not -path "./build/*" -not -path "./venv*/*" -not -path "*/examples/*")
else
FILES=$@
fi
Expand Down
Loading