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
10 changes: 10 additions & 0 deletions tests/integration/apps/anthropic-app/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
ARG PYTHON_VERSION=3.11
FROM python:${PYTHON_VERSION}-slim

WORKDIR /app
RUN pip install --no-cache-dir flask anthropic
COPY tests/integration/apps/anthropic-app/app.py .

EXPOSE 8080

CMD ["python", "app.py"]
22 changes: 22 additions & 0 deletions tests/integration/apps/anthropic-app/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import os
from flask import Flask
import anthropic

app = Flask(__name__)

MOCK_URL = os.environ.get("ANTHROPIC_BASE_URL", "http://mock-llm-server:5000")
client = anthropic.Anthropic(base_url=MOCK_URL, api_key="fake-key")


@app.route("/test")
def test():
message = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=256,
messages=[{"role": "user", "content": "Say hello"}],
)
return {"status": "ok", "response": message.content[0].text}


if __name__ == "__main__":
app.run(host="0.0.0.0", port=8080)
10 changes: 10 additions & 0 deletions tests/integration/apps/langchain-app/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
ARG PYTHON_VERSION=3.11
FROM python:${PYTHON_VERSION}-slim

WORKDIR /app
RUN pip install --no-cache-dir flask langchain-openai
COPY tests/integration/apps/langchain-app/app.py .

EXPOSE 8080

CMD ["python", "app.py"]
22 changes: 22 additions & 0 deletions tests/integration/apps/langchain-app/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import os
from flask import Flask
from langchain_openai import ChatOpenAI

app = Flask(__name__)

MOCK_URL = os.environ.get("OPENAI_BASE_URL", "http://mock-llm-server:5000/v1")


@app.route("/test")
def test():
llm = ChatOpenAI(
model="gpt-4o-mini",
base_url=MOCK_URL,
api_key="fake-key",
)
response = llm.invoke("Say hello")
return {"status": "ok", "response": response.content}


if __name__ == "__main__":
app.run(host="0.0.0.0", port=8080)
10 changes: 10 additions & 0 deletions tests/integration/apps/mock-llm-server/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
ARG PYTHON_VERSION=3.11
FROM python:${PYTHON_VERSION}-slim

WORKDIR /app
RUN pip install --no-cache-dir flask
COPY tests/integration/apps/mock-llm-server/server.py .

EXPOSE 5000

CMD ["python", "server.py"]
65 changes: 65 additions & 0 deletions tests/integration/apps/mock-llm-server/server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import time
import uuid
from flask import Flask, request, jsonify

app = Flask(__name__)


@app.route("/health", methods=["GET"])
def health():
return "ok"


@app.route("/v1/chat/completions", methods=["POST"])
def openai_chat_completions():
body = request.get_json(silent=True) or {}
model = body.get("model", "gpt-4o-mini")
return jsonify({
"id": f"chatcmpl-{uuid.uuid4().hex[:24]}",
"object": "chat.completion",
"created": int(time.time()),
"model": model,
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "This is a mock response from the fake LLM server.",
},
"finish_reason": "stop",
}
],
"usage": {
"prompt_tokens": 12,
"completion_tokens": 10,
"total_tokens": 22,
},
})


@app.route("/v1/messages", methods=["POST"])
def anthropic_messages():
body = request.get_json(silent=True) or {}
model = body.get("model", "claude-3-5-sonnet-20241022")
return jsonify({
"id": f"msg_{uuid.uuid4().hex[:24]}",
"type": "message",
"role": "assistant",
"content": [
{
"type": "text",
"text": "This is a mock Anthropic response.",
}
],
"model": model,
"stop_reason": "end_turn",
"stop_sequence": None,
"usage": {
"input_tokens": 15,
"output_tokens": 8,
},
})


if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)
10 changes: 10 additions & 0 deletions tests/integration/apps/openai-app/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
ARG PYTHON_VERSION=3.11
FROM python:${PYTHON_VERSION}-slim

WORKDIR /app
RUN pip install --no-cache-dir flask openai
COPY tests/integration/apps/openai-app/app.py .

EXPOSE 8080

CMD ["python", "app.py"]
21 changes: 21 additions & 0 deletions tests/integration/apps/openai-app/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import os
from flask import Flask
from openai import OpenAI

app = Flask(__name__)

MOCK_URL = os.environ.get("OPENAI_BASE_URL", "http://mock-llm-server:5000/v1")
client = OpenAI(base_url=MOCK_URL, api_key="fake-key")


@app.route("/test")
def test():
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": "Say hello"}],
)
return {"status": "ok", "response": response.choices[0].message.content}


if __name__ == "__main__":
app.run(host="0.0.0.0", port=8080)
107 changes: 107 additions & 0 deletions tests/integration/docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -148,3 +148,110 @@ services:
- DISABLE_OPAMP_CLIENT=true
ports:
- "8083:8080"

# ── GenAI apps ────────────────────────────────────────────────────
# Mock LLM server provides fake OpenAI and Anthropic API endpoints.
# The three GenAI apps exercise the corresponding instrumentation
# libraries against this mock.

mock-llm-server:
build:
context: ../../
dockerfile: tests/integration/apps/mock-llm-server/Dockerfile
args:
PYTHON_VERSION: ${PYTHON_VERSION:-3.11}
ports:
- "5050:5000"

openai-app:
build:
context: ../../
dockerfile: tests/integration/apps/openai-app/Dockerfile
args:
PYTHON_VERSION: ${PYTHON_VERSION:-3.11}
depends_on:
agent:
condition: service_completed_successfully
collector:
condition: service_started
mock-llm-server:
condition: service_started
volumes:
- instrumentation:/var/odigos/python:ro
environment:
- PYTHONPATH=/var/odigos/python:/var/odigos/python/opentelemetry/instrumentation/auto_instrumentation
- OTEL_SERVICE_NAME=openai-app
- OTEL_TRACES_EXPORTER=otlp
- OTEL_EXPORTER_OTLP_ENDPOINT=http://collector:4318
- OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
- OTEL_METRICS_EXPORTER=none
- OTEL_LOGS_EXPORTER=none
- OTEL_PYTHON_CONFIGURATOR=odigos-python-configurator
- ODIGOS_DISTRO_NAME=python-community
- DISABLE_OPAMP_CLIENT=true
- OPENAI_BASE_URL=http://mock-llm-server:5000/v1
- OPENAI_API_KEY=fake-key
ports:
- "8084:8080"

anthropic-app:
build:
context: ../../
dockerfile: tests/integration/apps/anthropic-app/Dockerfile
args:
PYTHON_VERSION: ${PYTHON_VERSION:-3.11}
depends_on:
agent:
condition: service_completed_successfully
collector:
condition: service_started
mock-llm-server:
condition: service_started
volumes:
- instrumentation:/var/odigos/python:ro
environment:
- PYTHONPATH=/var/odigos/python:/var/odigos/python/opentelemetry/instrumentation/auto_instrumentation
- OTEL_SERVICE_NAME=anthropic-app
- OTEL_TRACES_EXPORTER=otlp
- OTEL_EXPORTER_OTLP_ENDPOINT=http://collector:4318
- OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
- OTEL_METRICS_EXPORTER=none
- OTEL_LOGS_EXPORTER=none
- OTEL_PYTHON_CONFIGURATOR=odigos-python-configurator
- ODIGOS_DISTRO_NAME=python-community
- DISABLE_OPAMP_CLIENT=true
- ANTHROPIC_BASE_URL=http://mock-llm-server:5000
- ANTHROPIC_API_KEY=fake-key
ports:
- "8085:8080"

langchain-app:
build:
context: ../../
dockerfile: tests/integration/apps/langchain-app/Dockerfile
args:
PYTHON_VERSION: ${PYTHON_VERSION:-3.11}
depends_on:
agent:
condition: service_completed_successfully
collector:
condition: service_started
mock-llm-server:
condition: service_started
volumes:
- instrumentation:/var/odigos/python:ro
environment:
- PYTHONPATH=/var/odigos/python:/var/odigos/python/opentelemetry/instrumentation/auto_instrumentation
- OTEL_SERVICE_NAME=langchain-app
- OTEL_TRACES_EXPORTER=otlp
- OTEL_EXPORTER_OTLP_ENDPOINT=http://collector:4318
- OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
- OTEL_METRICS_EXPORTER=none
- OTEL_LOGS_EXPORTER=none
- OTEL_PYTHON_CONFIGURATOR=odigos-python-configurator
- ODIGOS_DISTRO_NAME=python-community
- DISABLE_OPAMP_CLIENT=true
- OPENAI_BASE_URL=http://mock-llm-server:5000/v1
- OPENAI_API_KEY=fake-key
ports:
- "8086:8080"
3 changes: 3 additions & 0 deletions tests/integration/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ APPS=(
"pythongunicorn:8000:/sub/home"
"django-app:8082:/rolldice"
"sqlalchemy-app:8083:/rolldice"
"openai-app:8084:/test"
"anthropic-app:8085:/test"
"langchain-app:8086:/test"
)

STARTUP_WAIT=30 # seconds to wait for containers to start
Expand Down
12 changes: 12 additions & 0 deletions tests/integration/verify.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,18 @@
"opentelemetry.instrumentation.sqlalchemy",
"opentelemetry.instrumentation.sqlite3",
],
"openai-app": [
"opentelemetry.instrumentation.flask",
"opentelemetry.instrumentation.openai_v2",
],
"anthropic-app": [
"opentelemetry.instrumentation.flask",
"opentelemetry.instrumentation.anthropic",
],
"langchain-app": [
"opentelemetry.instrumentation.flask",
"opentelemetry.instrumentation.langchain",
],
}

# OTLP span kind: 2 = SERVER
Expand Down
Loading