Skip to content

Commit 1b870bb

Browse files
committed
add LlamaIndex zero-code OTLP example
1 parent 43bc581 commit 1b870bb

3 files changed

Lines changed: 87 additions & 0 deletions

File tree

examples/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ agentevals accepts OTLP/HTTP on port 4318 (`http/protobuf` and `http/json`) and
3030
| [zero-code-examples/strands/](./zero-code-examples/strands/) | Strands | OpenAI |
3131
| [zero-code-examples/adk/](./zero-code-examples/adk/) | Google ADK | Gemini |
3232
| [zero-code-examples/pydantic-ai/](./zero-code-examples/pydantic-ai/) | Pydantic AI | OpenAI |
33+
| [zero-code-examples/llama-index/](./zero-code-examples/llama-index/) | LlamaIndex | OpenAI |
3334

3435
This approach works with any framework that has OTel instrumentation: LangChain, Strands, Google ADK, etc. If your framework already emits OTel spans, you only need to add `OTLPSpanExporter` (and `OTLPLogExporter` if it uses GenAI log-based content delivery).
3536

@@ -105,6 +106,7 @@ Detection checks for `gen_ai.request.model` / `gen_ai.input.messages` (GenAI sem
105106
| [zero-code-examples/strands/](./zero-code-examples/strands/) | Strands | OpenAI | GenAI semconv (events*) | Standard OTLP export |
106107
| [zero-code-examples/adk/](./zero-code-examples/adk/) | Google ADK | Gemini | ADK built-in | Standard OTLP export |
107108
| [zero-code-examples/pydantic-ai/](./zero-code-examples/pydantic-ai/) | Pydantic AI | OpenAI | GenAI semconv (span attrs) | Standard OTLP export |
109+
| [zero-code-examples/llama-index/](./zero-code-examples/llama-index/) | LlamaIndex | OpenAI | GenAI semconv (logs) | Standard OTLP export |
108110
| [langchain_agent](./langchain_agent/) | LangChain | OpenAI | GenAI semconv (logs) | SDK WebSocket |
109111
| [strands_agent](./strands_agent/) | Strands | OpenAI | GenAI semconv (events*) | SDK WebSocket |
110112
| [dice_agent](./dice_agent/) | Google ADK | Gemini | ADK built-in | SDK WebSocket |
@@ -226,6 +228,7 @@ python examples/zero-code-examples/ollama/run.py
226228
python examples/zero-code-examples/strands/run.py
227229
python examples/zero-code-examples/adk/run.py
228230
python examples/zero-code-examples/pydantic-ai/run.py
231+
python examples/zero-code-examples/llama-index/run.py
229232

230233
# SDK examples:
231234
python examples/sdk_example/context_manager_example.py
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
llama-index-core>=0.14.0
2+
llama-index-llms-openai-like>=0.3.0
3+
4+
opentelemetry-sdk>=1.36.0
5+
opentelemetry-exporter-otlp-proto-http>=1.36.0
6+
opentelemetry-instrumentation-openai-v2
7+
python-dotenv>=1.0.0
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
"""LlamaIndex zero-code OTLP example."""
2+
import asyncio
3+
import os
4+
import random
5+
6+
from dotenv import load_dotenv
7+
from llama_index.core.agent.workflow import FunctionAgent
8+
from llama_index.core.tools import FunctionTool
9+
from llama_index.llms.openai_like import OpenAILike
10+
from opentelemetry import trace
11+
from opentelemetry._logs import set_logger_provider
12+
from opentelemetry.exporter.otlp.proto.http._log_exporter import OTLPLogExporter
13+
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
14+
from opentelemetry.instrumentation.openai_v2 import OpenAIInstrumentor
15+
from opentelemetry.sdk._logs import LoggerProvider
16+
from opentelemetry.sdk._logs.export import BatchLogRecordProcessor
17+
from opentelemetry.sdk.resources import Resource
18+
from opentelemetry.sdk.trace import TracerProvider
19+
from opentelemetry.sdk.trace.export import BatchSpanProcessor
20+
21+
load_dotenv(override=True)
22+
23+
24+
def roll_die(sides: int) -> int:
25+
return random.randint(1, sides)
26+
27+
28+
def check_prime(n: int) -> bool:
29+
return n >= 2 and all(n % i for i in range(2, int(n**0.5) + 1))
30+
31+
32+
async def main():
33+
if not os.getenv("OPENAI_API_KEY"):
34+
print("OPENAI_API_KEY not set.")
35+
return
36+
37+
os.environ["OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT"] = "true"
38+
os.environ.setdefault("OTEL_RESOURCE_ATTRIBUTES",
39+
"agentevals.eval_set_id=llama_index_eval,agentevals.session_name=llama-index-zero-code")
40+
41+
resource = Resource.create()
42+
tp = TracerProvider(resource=resource)
43+
tp.add_span_processor(BatchSpanProcessor(OTLPSpanExporter(), schedule_delay_millis=1000))
44+
trace.set_tracer_provider(tp)
45+
lp = LoggerProvider(resource=resource)
46+
lp.add_log_record_processor(BatchLogRecordProcessor(OTLPLogExporter(), schedule_delay_millis=1000))
47+
set_logger_provider(lp)
48+
49+
OpenAIInstrumentor().instrument()
50+
51+
llm = OpenAILike(
52+
model=os.environ.get("OPENAI_MODEL", "gpt-4o-mini"),
53+
api_base=os.environ.get("OPENAI_BASE_URL", "https://api.openai.com/v1"),
54+
is_chat_model=True, is_function_calling_model=True,
55+
)
56+
agent = FunctionAgent(
57+
tools=[FunctionTool.from_defaults(fn=roll_die), FunctionTool.from_defaults(fn=check_prime)],
58+
llm=llm,
59+
system_prompt="Use roll_die for dice rolls. Use check_prime to check if a number is prime.",
60+
)
61+
62+
queries = [
63+
"Hi! Can you help me?",
64+
"Roll a 20-sided die.",
65+
"Roll a 6-sided die and check if the result is prime.",
66+
]
67+
for i, q in enumerate(queries, 1):
68+
print(f"\n[{i}/{len(queries)}] {q}")
69+
result = await agent.run(q)
70+
print(f" {result.response.content}")
71+
72+
tp.force_flush()
73+
lp.force_flush()
74+
75+
76+
if __name__ == "__main__":
77+
asyncio.run(main())

0 commit comments

Comments
 (0)