Skip to content

Commit b7fdd6a

Browse files
committed
Convert observability samples to notebooks
1 parent 15f4bd7 commit b7fdd6a

File tree

7 files changed

+645
-544
lines changed

7 files changed

+645
-544
lines changed
Lines changed: 276 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,276 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "markdown",
5+
"metadata": {},
6+
"source": [
7+
"# Weekend Planner with OpenAI Agents Telemetry\n",
8+
"\n",
9+
"Capture GenAI-compliant spans while orchestrating an OpenAI Agents workflow. This notebook mirrors the sample script but keeps everything inline so you can tweak the code and immediately inspect telemetry."
10+
]
11+
},
12+
{
13+
"cell_type": "markdown",
14+
"metadata": {},
15+
"source": [
16+
"## Requirements\n",
17+
"Install the supporting packages before running the cells below.\n",
18+
"\n",
19+
"```bash\n",
20+
"pip install openai openai-agents azure-identity rich python-dotenv\n",
21+
"pip install opentelemetry-instrumentation-openai-agents-v2\n",
22+
"# Optional Azure Monitor exporter\n",
23+
"pip install azure-monitor-opentelemetry-exporter\n",
24+
"```\n",
25+
"\n",
26+
"Set environment variables for your model provider (`API_HOST=github` by default) and, if needed, `APPLICATION_INSIGHTS_CONNECTION_STRING`."
27+
]
28+
},
29+
{
30+
"cell_type": "code",
31+
"execution_count": null,
32+
"metadata": {},
33+
"outputs": [],
34+
"source": [
35+
"from __future__ import annotations\n",
36+
"\n",
37+
"import asyncio\n",
38+
"import logging\n",
39+
"import os\n",
40+
"import random\n",
41+
"from dataclasses import dataclass\n",
42+
"from datetime import datetime\n",
43+
"from typing import Callable\n",
44+
"from urllib.parse import urlparse\n",
45+
"\n",
46+
"import azure.identity\n",
47+
"import openai\n",
48+
"from agents import Agent, OpenAIChatCompletionsModel, Runner, function_tool, set_tracing_disabled\n",
49+
"from dotenv import load_dotenv\n",
50+
"from rich.logging import RichHandler\n",
51+
"\n",
52+
"from opentelemetry import trace\n",
53+
"from opentelemetry.instrumentation.openai_agents import OpenAIAgentsInstrumentor\n",
54+
"from opentelemetry.sdk.resources import Resource\n",
55+
"from opentelemetry.sdk.trace import TracerProvider\n",
56+
"from opentelemetry.sdk.trace.export import BatchSpanProcessor, ConsoleSpanExporter\n",
57+
"\n",
58+
"try:\n",
59+
" from azure.monitor.opentelemetry.exporter import AzureMonitorTraceExporter\n",
60+
"except ImportError: # pragma: no cover - optional dependency\n",
61+
" AzureMonitorTraceExporter = None\n",
62+
"\n",
63+
"load_dotenv(override=True)\n",
64+
"\n",
65+
"logging.basicConfig(level=logging.WARNING, format=\"%(message)s\", datefmt=\"[%X]\", handlers=[RichHandler()])\n",
66+
"LOGGER = logging.getLogger(\"weekend_planner\")\n",
67+
"LOGGER.setLevel(logging.INFO)"
68+
]
69+
},
70+
{
71+
"cell_type": "code",
72+
"execution_count": null,
73+
"metadata": {},
74+
"outputs": [],
75+
"source": [
76+
"@dataclass\n",
77+
"class _ApiConfig:\n",
78+
" \"\"\"Helper describing how to create the OpenAI client.\"\"\"\n",
79+
"\n",
80+
" build_client: Callable[[], object]\n",
81+
" model_name: str\n",
82+
" base_url: str\n",
83+
" provider: str\n",
84+
"\n",
85+
"\n",
86+
"def _set_capture_env(provider: str, base_url: str) -> None:\n",
87+
" \"\"\"Enable GenAI capture toggles required by the instrumentation layer.\"\"\"\n",
88+
"\n",
89+
" capture_defaults = {\n",
90+
" \"OTEL_INSTRUMENTATION_OPENAI_AGENTS_CAPTURE_CONTENT\": \"true\",\n",
91+
" \"OTEL_INSTRUMENTATION_OPENAI_AGENTS_CAPTURE_METRICS\": \"true\",\n",
92+
" \"OTEL_GENAI_CAPTURE_MESSAGES\": \"true\",\n",
93+
" \"OTEL_GENAI_CAPTURE_SYSTEM_INSTRUCTIONS\": \"true\",\n",
94+
" \"OTEL_GENAI_CAPTURE_TOOL_DEFINITIONS\": \"true\",\n",
95+
" \"OTEL_GENAI_EMIT_OPERATION_DETAILS\": \"true\",\n",
96+
" \"OTEL_GENAI_AGENT_NAME\": os.getenv(\"OTEL_GENAI_AGENT_NAME\", \"Weekend Planner Agent\"),\n",
97+
" \"OTEL_GENAI_AGENT_DESCRIPTION\": os.getenv(\"OTEL_GENAI_AGENT_DESCRIPTION\", \"Assistant that plans weekend activities using weather and events data\"),\n",
98+
" \"OTEL_GENAI_AGENT_ID\": os.getenv(\"OTEL_GENAI_AGENT_ID\", \"weekend-planner\"),\n",
99+
" \"OTEL_GENAI_PROVIDER_NAME\": provider,\n",
100+
" }\n",
101+
" for key, value in capture_defaults.items():\n",
102+
" os.environ.setdefault(key, value)\n",
103+
"\n",
104+
" parsed = urlparse(base_url)\n",
105+
" if parsed.hostname:\n",
106+
" os.environ.setdefault(\"OTEL_GENAI_SERVER_ADDRESS\", parsed.hostname)\n",
107+
" if parsed.port:\n",
108+
" os.environ.setdefault(\"OTEL_GENAI_SERVER_PORT\", str(parsed.port))\n",
109+
"\n",
110+
"\n",
111+
"def _resolve_api_config() -> _ApiConfig:\n",
112+
" \"\"\"Return the client configuration for the requested host.\"\"\"\n",
113+
"\n",
114+
" host = os.getenv(\"API_HOST\", \"github\").lower()\n",
115+
"\n",
116+
" if host == \"github\":\n",
117+
" base_url = os.getenv(\"GITHUB_OPENAI_BASE_URL\", \"https://models.inference.ai.azure.com\").rstrip(\"/\")\n",
118+
" model_name = os.getenv(\"GITHUB_MODEL\", \"gpt-4o\")\n",
119+
" api_key = os.environ[\"GITHUB_TOKEN\"]\n",
120+
"\n",
121+
" def _build_client() -> openai.AsyncOpenAI:\n",
122+
" return openai.AsyncOpenAI(base_url=base_url, api_key=api_key)\n",
123+
"\n",
124+
" return _ApiConfig(build_client=_build_client, model_name=model_name, base_url=base_url, provider=\"azure.ai.inference\")\n",
125+
"\n",
126+
" if host == \"azure\":\n",
127+
" endpoint = os.environ[\"AZURE_OPENAI_ENDPOINT\"].rstrip(\"/\")\n",
128+
" api_version = os.environ[\"AZURE_OPENAI_VERSION\"]\n",
129+
" deployment = os.environ[\"AZURE_OPENAI_CHAT_DEPLOYMENT\"]\n",
130+
"\n",
131+
" credential = azure.identity.DefaultAzureCredential()\n",
132+
" token_provider = azure.identity.get_bearer_token_provider(credential, \"https://cognitiveservices.azure.com/.default\")\n",
133+
"\n\n",
134+
" def _build_client() -> openai.AsyncAzureOpenAI:\n",
135+
" return openai.AsyncAzureOpenAI(api_version=api_version, azure_endpoint=endpoint, azure_ad_token_provider=token_provider)\n",
136+
"\n",
137+
" return _ApiConfig(build_client=_build_client, model_name=deployment, base_url=endpoint, provider=\"azure.ai.openai\")\n",
138+
"\n",
139+
" raise ValueError(\"Unsupported API_HOST. Expected 'github' or 'azure'.\")\n",
140+
"\n",
141+
"\n",
142+
"def _configure_tracer() -> None:\n",
143+
" \"\"\"Configure tracer provider and exporter.\"\"\"\n",
144+
"\n",
145+
" resource = Resource.create({\"service.name\": \"weekend-planner-service\", \"service.namespace\": \"ignite25\", \"service.version\": os.getenv(\"SERVICE_VERSION\", \"1.0.0\")})\n",
146+
" provider = TracerProvider(resource=resource)\n",
147+
" connection_string = os.getenv(\"APPLICATION_INSIGHTS_CONNECTION_STRING\")\n",
148+
"\n",
149+
" if connection_string and AzureMonitorTraceExporter is not None:\n",
150+
" exporter = AzureMonitorTraceExporter.from_connection_string(connection_string)\n",
151+
" provider.add_span_processor(BatchSpanProcessor(exporter))\n",
152+
" print(\"[otel] Azure Monitor trace exporter configured\")\n",
153+
" else:\n",
154+
" provider.add_span_processor(BatchSpanProcessor(ConsoleSpanExporter()))\n",
155+
" if connection_string and AzureMonitorTraceExporter is None:\n",
156+
" print(\"[otel] Azure Monitor exporter unavailable. Install azure-monitor-opentelemetry-exporter\")\n",
157+
" else:\n",
158+
" print(\"[otel] Console span exporter configured\")\n",
159+
"\n",
160+
" trace.set_tracer_provider(provider)"
161+
]
162+
},
163+
{
164+
"cell_type": "code",
165+
"execution_count": null,
166+
"metadata": {},
167+
"outputs": [],
168+
"source": [
169+
"API_CONFIG = _resolve_api_config()\n",
170+
"_set_capture_env(API_CONFIG.provider, API_CONFIG.base_url)\n",
171+
"_configure_tracer()\n",
172+
"\n",
173+
"OpenAIAgentsInstrumentor().instrument(tracer_provider=trace.get_tracer_provider())\n",
174+
"CLIENT = API_CONFIG.build_client()\n",
175+
"set_tracing_disabled(False)\n",
176+
"print(\"Instrumentation ready for provider:\", API_CONFIG.provider)"
177+
]
178+
},
179+
{
180+
"cell_type": "code",
181+
"execution_count": null,
182+
"metadata": {},
183+
"outputs": [],
184+
"source": [
185+
"@function_tool\n",
186+
"def get_weather(city: str) -> dict[str, object]:\n",
187+
" LOGGER.info(\"Getting weather for %s\", city)\n",
188+
" if random.random() < 0.05:\n",
189+
" return {\"city\": city, \"temperature\": 72, \"description\": \"Sunny\"}\n",
190+
" return {\"city\": city, \"temperature\": 60, \"description\": \"Rainy\"}\n",
191+
"\n",
192+
"\n",
193+
"@function_tool\n",
194+
"def get_activities(city: str, date: str) -> list[dict[str, object]]:\n",
195+
" LOGGER.info(\"Getting activities for %s on %s\", city, date)\n",
196+
" return [\n",
197+
" {\"name\": \"Hiking\", \"location\": city},\n",
198+
" {\"name\": \"Beach\", \"location\": city},\n",
199+
" {\"name\": \"Museum\", \"location\": city},\n",
200+
" ]\n",
201+
"\n",
202+
"\n",
203+
"@function_tool\n",
204+
"def get_current_date() -> str:\n",
205+
" \"\"\"Gets the current date and returns as YYYY-MM-DD.\"\"\"\n",
206+
"\n",
207+
" LOGGER.info(\"Getting current date\")\n",
208+
" return datetime.now().strftime(\"%Y-%m-%d\")\n",
209+
"\n",
210+
"\n",
211+
"AGENT = Agent(\n",
212+
" name=\"Weekend Planner\",\n",
213+
" instructions=(\n",
214+
" \"You help users plan their weekends and choose the best activities for the given weather. \"\n",
215+
" \"If an activity would be unpleasant in the weather, do not recommend it. \"\n",
216+
" \"Always include the relevant weekend date in your response.\"\n",
217+
" ),\n",
218+
" tools=[get_weather, get_activities, get_current_date],\n",
219+
" model=OpenAIChatCompletionsModel(model=API_CONFIG.model_name, openai_client=CLIENT),\n",
220+
")\n",
221+
"print(\"Agent ready:\", AGENT.name)"
222+
]
223+
},
224+
{
225+
"cell_type": "markdown",
226+
"metadata": {},
227+
"source": [
228+
"## Run the agent\n",
229+
"Execute the final cell to orchestrate a single planning session. Spans will be emitted to the configured exporter (console or Azure Monitor)."
230+
]
231+
},
232+
{
233+
"cell_type": "code",
234+
"execution_count": null,
235+
"metadata": {},
236+
"outputs": [],
237+
"source": [
238+
"async def run_planner():\n",
239+
" tracer = trace.get_tracer(__name__)\n",
240+
" with tracer.start_as_current_span(f\"weekend_planning_session[{API_CONFIG.provider}]\") as span:\n",
241+
" user_request = \"Hi, what can I do this weekend in Seattle?\"\n",
242+
" span.set_attribute(\"user.request\", user_request)\n",
243+
" span.set_attribute(\"gen_ai.provider.name\", API_CONFIG.provider)\n",
244+
" span.set_attribute(\"gen_ai.request.model\", API_CONFIG.model_name)\n",
245+
" span.set_attribute(\"agent.name\", AGENT.name)\n",
246+
" span.set_attribute(\"target.city\", \"Seattle\")\n",
247+
" try:\n",
248+
" result = await Runner.run(AGENT, input=user_request)\n",
249+
" output = result.final_output or \"\"\n",
250+
" span.set_attribute(\"agent.response\", output[:500])\n",
251+
" span.set_attribute(\"request.success\", True)\n",
252+
" print(output)\n",
253+
" except Exception as exc:\n",
254+
" span.record_exception(exc)\n",
255+
" span.set_attribute(\"request.success\", False)\n",
256+
" raise\n",
257+
"\n",
258+
"await run_planner()\n",
259+
"trace.get_tracer_provider().shutdown()"
260+
]
261+
}
262+
],
263+
"metadata": {
264+
"kernelspec": {
265+
"display_name": "Python 3",
266+
"language": "python",
267+
"name": "python3"
268+
},
269+
"language_info": {
270+
"name": "python",
271+
"pygments_lexer": "ipython3"
272+
}
273+
},
274+
"nbformat": 4,
275+
"nbformat_minor": 5
276+
}

0 commit comments

Comments
 (0)