Skip to content
Closed
Show file tree
Hide file tree
Changes from 3 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

Minimally, update the [.env](.env) 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 the falkland islands?",
}
]

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,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