From 2aeb06d90c76e81768e58ac39a73d61e5b06ca16 Mon Sep 17 00:00:00 2001 From: mmercuri Date: Sun, 10 May 2026 16:37:29 -0700 Subject: [PATCH 1/4] docs(samples): add READMEs for agno, openai_agents, google_adk, ms_agent_framework, pydantic_ai --- samples/instrument/agno/README.md | 71 +++++++++++++++++ samples/instrument/google_adk/README.md | 77 +++++++++++++++++++ .../instrument/ms_agent_framework/README.md | 76 ++++++++++++++++++ samples/instrument/openai_agents/README.md | 72 +++++++++++++++++ samples/instrument/pydantic_ai/README.md | 76 ++++++++++++++++++ 5 files changed, 372 insertions(+) create mode 100644 samples/instrument/agno/README.md create mode 100644 samples/instrument/google_adk/README.md create mode 100644 samples/instrument/ms_agent_framework/README.md create mode 100644 samples/instrument/openai_agents/README.md create mode 100644 samples/instrument/pydantic_ai/README.md diff --git a/samples/instrument/agno/README.md b/samples/instrument/agno/README.md new file mode 100644 index 00000000..fdee4f08 --- /dev/null +++ b/samples/instrument/agno/README.md @@ -0,0 +1,71 @@ +# Agno instrumentation sample + +End-to-end demo of `AgnoAdapter` — wraps a one-shot Agno `Agent` +backed by `OpenAIChat("gpt-4o-mini")`, runs a single `agent.run()` +call, and ships telemetry to atlas-app via `HttpEventSink`. + +## Prerequisites + +```bash +pip install 'layerlens[agno,providers-openai]' +``` + +Required environment: + +- `OPENAI_API_KEY` — used by `OpenAIChat`. If unset the sample exits + with code `2`. +- `LAYERLENS_STRATIX_API_KEY` — your LayerLens API key (optional). +- `LAYERLENS_STRATIX_BASE_URL` — atlas-app base URL (optional). + +## Run + +```bash +uv run python -m samples.instrument.agno.main +``` + +## What this demonstrates + +| Component | What it proves (source: `main.py`) | +|---|---| +| `AgnoAdapter(capture_config=CaptureConfig.standard())` | Adapter constructed with the standard capture profile. | +| `adapter.add_sink(HttpEventSink(...))` | Events batched to `/telemetry/spans` (`max_batch=10`, `flush_interval_s=1.0`). | +| `adapter.connect()` / `adapter.disconnect()` | Full lifecycle: instrument before invocation, restore on exit. | +| `adapter.instrument_agent(agent)` | Wraps `Agent.run` so `agent.input` + `model.invoke` + `agent.output` events fire on each call (per `main.py` module docstring). | +| Response print | The sample prints the `response.content` string returned by `Agent.run("What is 2 + 2?")`. | + +## Expected output + +When `OPENAI_API_KEY` is unset: + +```text +OPENAI_API_KEY is not set; cannot run sample. +``` + +Exit code: `2`. + +When `agno` is not installed: + +```text +agno not installed. Install with: + pip install 'layerlens[agno,providers-openai]' +``` + +Exit code: `2`. + +When the call succeeds the sample prints the model response and a +shipping confirmation: + +```text +Response: +Telemetry shipped. Check the LayerLens dashboard adapter health page. +``` + +## Multi-tenancy note + +This sample does not pass `org_id` to the adapter constructor. The +`AgnoAdapter` constructor does not yet accept `org_id` — production +multi-tenant wiring lands with the PR #118 adapter-side contract +(currently DRAFT). In the interim, set `org_id` per-event through +`CaptureConfig` overrides or via `HttpEventSink` headers on the +caller side. Once PR #118 merges, pass `org_id=""` to +`AgnoAdapter(...)` directly. diff --git a/samples/instrument/google_adk/README.md b/samples/instrument/google_adk/README.md new file mode 100644 index 00000000..b5ce7f5b --- /dev/null +++ b/samples/instrument/google_adk/README.md @@ -0,0 +1,77 @@ +# Google ADK instrumentation sample + +End-to-end demo of `GoogleADKAdapter` — builds a one-shot `LlmAgent` +backed by `gemini-2.0-flash`, attaches LayerLens callbacks, and runs +a single turn through the ADK `InMemoryRunner`. + +## Prerequisites + +```bash +pip install 'layerlens[google-adk]' +``` + +Required environment (one of): + +- `GOOGLE_API_KEY` — used by the Gemini model against Google AI + Studio. **OR** +- `GOOGLE_GENAI_USE_VERTEXAI=true` — route through Vertex AI; supply + Application Default Credentials. + +If neither is set the sample exits with code `2`. + +Optional: + +- `LAYERLENS_STRATIX_API_KEY` — your LayerLens API key. +- `LAYERLENS_STRATIX_BASE_URL` — atlas-app base URL. + +## Run + +```bash +uv run python -m samples.instrument.google_adk.main +``` + +## What this demonstrates + +| Component | What it proves (source: `main.py`) | +|---|---| +| `GoogleADKAdapter(capture_config=CaptureConfig.standard())` | Standard capture profile. | +| `adapter.add_sink(HttpEventSink(...))` | Batched HTTP transport. | +| `adapter.connect()` / `adapter.disconnect()` | Full lifecycle. | +| `adapter.instrument_agent(agent)` | Attaches LayerLens callbacks to the `LlmAgent`; each callback emits a LayerLens event (per `main.py` module docstring). | +| `InMemoryRunner(agent=agent, app_name="layerlens-sample")` + `run_async` | Async iteration over the ADK event stream; text parts are concatenated and printed. | +| Pre-created session via `runner.session_service.create_session(...)` | Demonstrates the required ADK pattern for giving `run_async` a session to write into. | + +## Expected output + +When neither `GOOGLE_API_KEY` nor `GOOGLE_GENAI_USE_VERTEXAI=true` is +set: + +```text +Neither GOOGLE_API_KEY nor GOOGLE_GENAI_USE_VERTEXAI is set; cannot run sample. +``` + +Exit code: `2`. + +When `google-adk` is not installed: + +```text +google-adk not installed. Install with: + pip install 'layerlens[google-adk]' +``` + +Exit code: `2`. + +When the call succeeds: + +```text +Response: +Telemetry shipped. Check the LayerLens dashboard adapter health page. +``` + +## Multi-tenancy note + +This sample does not pass `org_id` to `GoogleADKAdapter`. The +constructor does not yet accept `org_id` — production multi-tenant +wiring lands with the PR #118 adapter-side contract (currently +DRAFT). Once PR #118 merges, pass `org_id` to the adapter so every +emitted event carries it. diff --git a/samples/instrument/ms_agent_framework/README.md b/samples/instrument/ms_agent_framework/README.md new file mode 100644 index 00000000..e8981a25 --- /dev/null +++ b/samples/instrument/ms_agent_framework/README.md @@ -0,0 +1,76 @@ +# Microsoft Agent Framework instrumentation sample + +End-to-end demo of `MSAgentAdapter` — builds a one-shot +`ChatCompletionAgent` backed by `OpenAIChatCompletion("gpt-4o-mini")`, +wraps it via `MSAgentAdapter.instrument_chat`, and runs a single +`agent.invoke` async call. + +> **Note on dependencies:** the Microsoft Agent Framework currently +> ships as part of `semantic-kernel`. The `ms-agent-framework` extra +> in `pyproject.toml` resolves to `semantic-kernel>=1.0,<2.0` for +> that reason. The import path the sample uses is +> `semantic_kernel.agents.ChatCompletionAgent`. + +## Prerequisites + +```bash +pip install 'layerlens[ms-agent-framework,providers-openai]' +``` + +Required environment: + +- `OPENAI_API_KEY` — used by `OpenAIChatCompletion`. If unset the + sample exits with code `2`. +- `LAYERLENS_STRATIX_API_KEY` — your LayerLens API key (optional). +- `LAYERLENS_STRATIX_BASE_URL` — atlas-app base URL (optional). + +## Run + +```bash +uv run python -m samples.instrument.ms_agent_framework.main +``` + +## What this demonstrates + +| Component | What it proves (source: `main.py`) | +|---|---| +| `MSAgentAdapter(capture_config=CaptureConfig.standard())` | Standard capture profile. | +| `adapter.add_sink(HttpEventSink(...))` | Batched HTTP transport. | +| `adapter.connect()` / `adapter.disconnect()` | Full lifecycle. | +| `adapter.instrument_chat(agent)` | Wraps the `ChatCompletionAgent`; module docstring states each invocation emits `agent.input` + `model.invoke` + `agent.output`. | +| Async streaming via `async for response in agent.invoke(...)` | Iterates the agent's response stream and collects `response.content` strings. | +| `asyncio.run(_run(agent))` | Demonstrates the sync-driver-around-async-agent pattern. | + +## Expected output + +When `OPENAI_API_KEY` is unset: + +```text +OPENAI_API_KEY is not set; cannot run sample. +``` + +Exit code: `2`. + +When `semantic-kernel` agents are not installed: + +```text +semantic-kernel agents not installed. Install with: + pip install 'layerlens[ms-agent-framework,providers-openai]' +``` + +Exit code: `2`. + +When the call succeeds: + +```text +Response: +Telemetry shipped. Check the LayerLens dashboard adapter health page. +``` + +## Multi-tenancy note + +This sample does not pass `org_id` to `MSAgentAdapter`. The +constructor does not yet accept `org_id` — production multi-tenant +wiring lands with the PR #118 adapter-side contract (currently +DRAFT). Once PR #118 merges, pass `org_id` to the adapter so every +emitted event carries it. diff --git a/samples/instrument/openai_agents/README.md b/samples/instrument/openai_agents/README.md new file mode 100644 index 00000000..3fcc23d0 --- /dev/null +++ b/samples/instrument/openai_agents/README.md @@ -0,0 +1,72 @@ +# OpenAI Agents SDK instrumentation sample + +End-to-end demo of `OpenAIAgentsAdapter` — registers a LayerLens trace +processor with the OpenAI Agents SDK, runs `Runner.run_sync` against +a one-shot `Agent`, and ships every SDK span as a LayerLens event. + +## Prerequisites + +```bash +pip install 'layerlens[openai-agents]' openai-agents +``` + +> The `openai-agents` extra in `pyproject.toml` pulls `openai>=1.30,<2` +> but the `openai-agents` SDK package itself is not in the extra and +> must be installed explicitly. + +Required environment: + +- `OPENAI_API_KEY` — used by the underlying OpenAI client. If unset + the sample exits with code `2`. +- `LAYERLENS_STRATIX_API_KEY` — your LayerLens API key (optional). +- `LAYERLENS_STRATIX_BASE_URL` — atlas-app base URL (optional). + +## Run + +```bash +uv run python -m samples.instrument.openai_agents.main +``` + +## What this demonstrates + +| Component | What it proves (source: `main.py`) | +|---|---| +| `OpenAIAgentsAdapter(capture_config=CaptureConfig.standard())` | Adapter constructed with the standard capture profile. | +| `adapter.add_sink(HttpEventSink(...))` | Events batched to `/telemetry/spans` (`max_batch=10`, `flush_interval_s=1.0`). | +| `adapter.connect()` / `adapter.disconnect()` | Full lifecycle. | +| `adapter.instrument_runner(None)` | Registers the LayerLens trace processor with the SDK at the global level — every SDK span (agent, model, tool, handoff) emits a LayerLens event (per `main.py` module docstring). | +| `Runner.run_sync(agent, "What is 2 + 2?")` | One synchronous turn against a `gpt-4o-mini` `Agent`; `result.final_output` is printed. | + +## Expected output + +When `OPENAI_API_KEY` is unset: + +```text +OPENAI_API_KEY is not set; cannot run sample. +``` + +Exit code: `2`. + +When `openai-agents` is not installed: + +```text +openai-agents not installed. Install with: + pip install 'layerlens[openai-agents]' openai-agents +``` + +Exit code: `2`. + +When the call succeeds: + +```text +Response: +Telemetry shipped. Check the LayerLens dashboard adapter health page. +``` + +## Multi-tenancy note + +This sample does not pass `org_id` to `OpenAIAgentsAdapter`. The +constructor does not yet accept `org_id` — production multi-tenant +wiring lands with the PR #118 adapter-side contract (currently +DRAFT). Once PR #118 merges, pass `org_id` to the adapter so every +emitted event carries it. diff --git a/samples/instrument/pydantic_ai/README.md b/samples/instrument/pydantic_ai/README.md new file mode 100644 index 00000000..2a2bbd4d --- /dev/null +++ b/samples/instrument/pydantic_ai/README.md @@ -0,0 +1,76 @@ +# PydanticAI instrumentation sample + +End-to-end demo of `PydanticAIAdapter` — wraps a one-shot +`Agent("openai:gpt-4o-mini")`, runs `agent.run_sync`, and ships +telemetry to atlas-app via `HttpEventSink`. + +## Prerequisites + +```bash +pip install 'layerlens[pydantic-ai,providers-openai]' +``` + +The `pydantic-ai` extra installs `pydantic-ai>=0.0.13,<1.0` +(Python 3.10+). + +Required environment: + +- `OPENAI_API_KEY` — used by the `"openai:gpt-4o-mini"` model spec. + If unset the sample exits with code `2`. +- `LAYERLENS_STRATIX_API_KEY` — your LayerLens API key (optional). +- `LAYERLENS_STRATIX_BASE_URL` — atlas-app base URL (optional). + +## Run + +```bash +uv run python -m samples.instrument.pydantic_ai.main +``` + +## What this demonstrates + +| Component | What it proves (source: `main.py`) | +|---|---| +| `PydanticAIAdapter(capture_config=CaptureConfig.standard())` | Standard capture profile. | +| `adapter.add_sink(HttpEventSink(...))` | Batched HTTP transport. | +| `adapter.connect()` / `adapter.disconnect()` | Full lifecycle. | +| `adapter.instrument_agent(agent)` | Wraps the PydanticAI `Agent`; module docstring states each run emits `agent.input` + `model.invoke` + `agent.output`. | +| `agent.run_sync("What is 2 + 2?")` | One synchronous run; `result.data` is printed. | +| `result.usage()` | When non-`None`, the sample prints request/response/total token counts — proves usage propagation through the wrapper. | + +## Expected output + +When `OPENAI_API_KEY` is unset: + +```text +OPENAI_API_KEY is not set; cannot run sample. +``` + +Exit code: `2`. + +When `pydantic-ai` is not installed: + +```text +pydantic-ai not installed. Install with: + pip install 'layerlens[pydantic-ai,providers-openai]' +``` + +Exit code: `2`. + +When the call succeeds: + +```text +Response: +Tokens — request: , response: , total: +Telemetry shipped. Check the LayerLens dashboard adapter health page. +``` + +(The token line is conditional on `result.usage()` returning a +non-`None` value.) + +## Multi-tenancy note + +This sample does not pass `org_id` to `PydanticAIAdapter`. The +constructor does not yet accept `org_id` — production multi-tenant +wiring lands with the PR #118 adapter-side contract (currently +DRAFT). Once PR #118 merges, pass `org_id` to the adapter so every +emitted event carries it. From 39ec5336b424deb0fbdb33cdb5d0f465039d97a6 Mon Sep 17 00:00:00 2001 From: mmercuri Date: Sun, 10 May 2026 16:38:18 -0700 Subject: [PATCH 2/4] docs(samples): add READMEs for bedrock_agents and strands AWS adapters --- samples/instrument/bedrock_agents/README.md | 81 +++++++++++++++++++++ samples/instrument/strands/README.md | 77 ++++++++++++++++++++ 2 files changed, 158 insertions(+) create mode 100644 samples/instrument/bedrock_agents/README.md create mode 100644 samples/instrument/strands/README.md diff --git a/samples/instrument/bedrock_agents/README.md b/samples/instrument/bedrock_agents/README.md new file mode 100644 index 00000000..c1616081 --- /dev/null +++ b/samples/instrument/bedrock_agents/README.md @@ -0,0 +1,81 @@ +# AWS Bedrock Agents instrumentation sample + +End-to-end demo of `BedrockAgentsAdapter` — wraps a +`bedrock-agent-runtime` boto3 client, runs a single `invoke_agent` +call, drains the streamed response, and ships telemetry events to +atlas-app via `HttpEventSink`. + +> **Live-only.** This sample requires a real Bedrock Agent ID; there +> is no mock mode. If `BEDROCK_AGENT_ID` is unset the sample exits +> cleanly with code `2`. + +## Prerequisites + +```bash +pip install 'layerlens[bedrock-agents]' +``` + +The `bedrock-agents` extra installs `boto3>=1.34`. + +Required environment: + +- `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` (or any standard + boto3 credential source — IAM role, profile, etc.). +- `AWS_REGION` — region your agent lives in (defaults to + `us-east-1`). +- `BEDROCK_AGENT_ID` — your Bedrock Agent ID (e.g. `ABCDEFGHIJ`). +- `BEDROCK_AGENT_ALIAS_ID` — alias to invoke (defaults to + `TSTALIASID`). +- `LAYERLENS_STRATIX_API_KEY` — your LayerLens API key (optional). +- `LAYERLENS_STRATIX_BASE_URL` — atlas-app base URL (optional). + +## Run + +```bash +uv run python -m samples.instrument.bedrock_agents.main +``` + +## What this demonstrates + +| Component | What it proves (source: `main.py`) | +|---|---| +| `BedrockAgentsAdapter(capture_config=CaptureConfig.standard())` | Standard capture profile. | +| `adapter.add_sink(HttpEventSink(...))` | Batched HTTP transport. | +| `adapter.connect()` / `adapter.disconnect()` | Full lifecycle. | +| `adapter.instrument_client(client)` | Registers event hooks on the boto3 `bedrock-agent-runtime` client; the module docstring states this emits `agent.input` + `model.invoke` + `tool.call` + `agent.output` events. | +| `client.invoke_agent(agentId=..., agentAliasId=..., sessionId=uuid4(), inputText="What is 2 + 2?")` | Live invocation against your configured agent. | +| Streamed response drain (`for event in response["completion"]: ...`) | Trace events fire as each chunk is iterated; concatenated bytes are decoded and printed. | + +## Expected output + +When `BEDROCK_AGENT_ID` is unset: + +```text +BEDROCK_AGENT_ID is not set; cannot run sample. +``` + +Exit code: `2`. + +When `boto3` is not installed: + +```text +boto3 not installed. Install with: + pip install 'layerlens[bedrock-agents]' +``` + +Exit code: `2`. + +When the call succeeds: + +```text +Response: +Telemetry shipped. Check the LayerLens dashboard adapter health page. +``` + +## Multi-tenancy note + +This sample does not pass `org_id` to `BedrockAgentsAdapter`. The +constructor does not yet accept `org_id` — production multi-tenant +wiring lands with the PR #118 adapter-side contract (currently +DRAFT). Once PR #118 merges, pass `org_id` to the adapter so every +emitted event carries it. diff --git a/samples/instrument/strands/README.md b/samples/instrument/strands/README.md new file mode 100644 index 00000000..9ecd3123 --- /dev/null +++ b/samples/instrument/strands/README.md @@ -0,0 +1,77 @@ +# AWS Strands instrumentation sample + +End-to-end demo of `StrandsAdapter` — builds a one-shot Strands +`Agent` backed by a Bedrock model, wraps it via +`StrandsAdapter.instrument_agent`, and runs a single call. + +## Prerequisites + +```bash +pip install 'layerlens[strands]' +``` + +The `strands` extra installs `strands-agents>=0.1,<1.0` (Python 3.10+). + +Required environment: + +- `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` **or** `AWS_PROFILE` + (or any standard boto3 credential source). If neither + `AWS_ACCESS_KEY_ID` nor `AWS_PROFILE` is set the sample exits with + code `2`. +- `AWS_REGION` — region where your Bedrock model access is enabled + (Strands defaults to `us-west-2`). +- `BEDROCK_MODEL_ID` — Bedrock model ID for Strands to use; defaults + to `us.anthropic.claude-3-5-sonnet-20241022-v2:0` if unset. +- `LAYERLENS_STRATIX_API_KEY` — your LayerLens API key (optional). +- `LAYERLENS_STRATIX_BASE_URL` — atlas-app base URL (optional). + +## Run + +```bash +uv run python -m samples.instrument.strands.main +``` + +## What this demonstrates + +| Component | What it proves (source: `main.py`) | +|---|---| +| `StrandsAdapter(capture_config=CaptureConfig.standard())` | Standard capture profile. | +| `adapter.add_sink(HttpEventSink(...))` | Batched HTTP transport. | +| `adapter.connect()` / `adapter.disconnect()` | Full lifecycle. | +| `adapter.instrument_agent(agent)` | Wraps the Strands `Agent`; the module docstring states each call emits `agent.input` + `model.invoke` + `agent.output`. | +| `agent("What is 2 + 2?")` | One synchronous call against the configured Bedrock model. | +| `BEDROCK_MODEL_ID` env override | Demonstrates pulling the model id from environment with a sensible Claude 3.5 Sonnet default. | + +## Expected output + +When AWS credentials are not configured: + +```text +AWS credentials are not set (need AWS_ACCESS_KEY_ID or AWS_PROFILE); cannot run sample. +``` + +Exit code: `2`. + +When `strands-agents` is not installed: + +```text +strands-agents not installed. Install with: + pip install 'layerlens[strands]' +``` + +Exit code: `2`. + +When the call succeeds: + +```text +Response: +Telemetry shipped. Check the LayerLens dashboard adapter health page. +``` + +## Multi-tenancy note + +This sample does not pass `org_id` to `StrandsAdapter`. The +constructor does not yet accept `org_id` — production multi-tenant +wiring lands with the PR #118 adapter-side contract (currently +DRAFT). Once PR #118 merges, pass `org_id` to the adapter so every +emitted event carries it. From c97919e07213125f895eea3b347e5161a4b7e417 Mon Sep 17 00:00:00 2001 From: mmercuri Date: Sun, 10 May 2026 16:39:16 -0700 Subject: [PATCH 3/4] docs(samples): add READMEs for llama_index and semantic_kernel orchestration samples --- samples/instrument/llama_index/README.md | 72 +++++++++++++++++++ samples/instrument/semantic_kernel/README.md | 74 ++++++++++++++++++++ 2 files changed, 146 insertions(+) create mode 100644 samples/instrument/llama_index/README.md create mode 100644 samples/instrument/semantic_kernel/README.md diff --git a/samples/instrument/llama_index/README.md b/samples/instrument/llama_index/README.md new file mode 100644 index 00000000..4a7f1b3e --- /dev/null +++ b/samples/instrument/llama_index/README.md @@ -0,0 +1,72 @@ +# LlamaIndex instrumentation sample + +End-to-end demo of `LlamaIndexAdapter` — registers the LayerLens +event handler with the global LlamaIndex `Dispatcher`, runs a single +LLM `chat` call, and ships events to atlas-app via `HttpEventSink`. + +## Prerequisites + +```bash +pip install 'layerlens[llama-index,providers-openai]' llama-index-llms-openai +``` + +> The `llama-index` extra pulls the `llama-index` core meta-package +> but the `llama-index-llms-openai` integration package is separate +> and must be installed explicitly (per `main.py` install hint). + +Required environment: + +- `OPENAI_API_KEY` — used by `llama_index.llms.openai.OpenAI`. If + unset the sample exits with code `2`. +- `LAYERLENS_STRATIX_API_KEY` — your LayerLens API key (optional). +- `LAYERLENS_STRATIX_BASE_URL` — atlas-app base URL (optional). + +## Run + +```bash +uv run python -m samples.instrument.llama_index.main +``` + +## What this demonstrates + +| Component | What it proves (source: `main.py`) | +|---|---| +| `LlamaIndexAdapter(capture_config=CaptureConfig.standard())` | Standard capture profile. | +| `adapter.add_sink(HttpEventSink(...))` | Batched HTTP transport (`max_batch=10`, `flush_interval_s=1.0`). | +| `adapter.connect()` / `adapter.disconnect()` | Full lifecycle. | +| `adapter.instrument_workflow(None)` | Registers the LayerLens event handler at the global Dispatcher level — emits `model.invoke` (and `tool.call` / `agent.*` if produced) per the module docstring. | +| `llm.chat(...)` with `gpt-4o-mini`, `max_tokens=20` | One synchronous chat turn; the assistant message content is printed. | + +## Expected output + +When `OPENAI_API_KEY` is unset: + +```text +OPENAI_API_KEY is not set; cannot run sample. +``` + +Exit code: `2`. + +When `llama-index` is not installed: + +```text +llama-index not installed. Install with: + pip install 'layerlens[llama-index,providers-openai]' llama-index-llms-openai +``` + +Exit code: `2`. + +When the call succeeds: + +```text +Response: +Telemetry shipped. Check the LayerLens dashboard adapter health page. +``` + +## Multi-tenancy note + +This sample does not pass `org_id` to `LlamaIndexAdapter`. The +constructor does not yet accept `org_id` — production multi-tenant +wiring lands with the PR #118 adapter-side contract (currently +DRAFT). Once PR #118 merges, pass `org_id` to the adapter so every +emitted event carries it. diff --git a/samples/instrument/semantic_kernel/README.md b/samples/instrument/semantic_kernel/README.md new file mode 100644 index 00000000..98434de5 --- /dev/null +++ b/samples/instrument/semantic_kernel/README.md @@ -0,0 +1,74 @@ +# Semantic Kernel instrumentation sample + +End-to-end demo of `SemanticKernelAdapter` — builds a `Kernel` with +an `OpenAIChatCompletion("gpt-4o-mini")` service, registers +LayerLens filters via `SemanticKernelAdapter.instrument_kernel`, and +runs a single `invoke_prompt` call. + +## Prerequisites + +```bash +pip install 'layerlens[semantic-kernel,providers-openai]' +``` + +The `semantic-kernel` extra installs `semantic-kernel>=1.0,<2.0` +(Python 3.10+). + +Required environment: + +- `OPENAI_API_KEY` — used by `OpenAIChatCompletion`. If unset the + sample exits with code `2`. +- `LAYERLENS_STRATIX_API_KEY` — your LayerLens API key (optional). +- `LAYERLENS_STRATIX_BASE_URL` — atlas-app base URL (optional). + +## Run + +```bash +uv run python -m samples.instrument.semantic_kernel.main +``` + +## What this demonstrates + +| Component | What it proves (source: `main.py`) | +|---|---| +| `SemanticKernelAdapter(capture_config=CaptureConfig.standard())` | Standard capture profile. | +| `adapter.add_sink(HttpEventSink(...))` | Batched HTTP transport (`max_batch=10`, `flush_interval_s=1.0`). | +| `adapter.connect()` / `adapter.disconnect()` | Full lifecycle. | +| `adapter.instrument_kernel(kernel)` | Registers LayerLens filters on the kernel; module docstring states filter callbacks emit `agent.input` / `agent.output` / `model.invoke` events. | +| `kernel.invoke_prompt(prompt=..., arguments=KernelArguments())` | One async prompt invocation through the kernel. | +| `asyncio.run(_run(kernel))` | Sync-driver-around-async-kernel pattern. | +| Lazy import of `KernelArguments` inside `_run` | Keeps the module importable even when `semantic-kernel` is absent. | + +## Expected output + +When `OPENAI_API_KEY` is unset: + +```text +OPENAI_API_KEY is not set; cannot run sample. +``` + +Exit code: `2`. + +When `semantic-kernel` is not installed: + +```text +semantic-kernel not installed. Install with: + pip install 'layerlens[semantic-kernel,providers-openai]' +``` + +Exit code: `2`. + +When the call succeeds: + +```text +Response: +Telemetry shipped. Check the LayerLens dashboard adapter health page. +``` + +## Multi-tenancy note + +This sample does not pass `org_id` to `SemanticKernelAdapter`. The +constructor does not yet accept `org_id` — production multi-tenant +wiring lands with the PR #118 adapter-side contract (currently +DRAFT). Once PR #118 merges, pass `org_id` to the adapter so every +emitted event carries it. From bd8b6a351a4354ae06823981c0f6bbcfc7a746b0 Mon Sep 17 00:00:00 2001 From: mmercuri Date: Sun, 10 May 2026 16:40:01 -0700 Subject: [PATCH 4/4] docs(samples): add READMEs for embedding and benchmark_import data-path samples --- samples/instrument/benchmark_import/README.md | 71 +++++++++++++++++ samples/instrument/embedding/README.md | 79 +++++++++++++++++++ 2 files changed, 150 insertions(+) create mode 100644 samples/instrument/benchmark_import/README.md create mode 100644 samples/instrument/embedding/README.md diff --git a/samples/instrument/benchmark_import/README.md b/samples/instrument/benchmark_import/README.md new file mode 100644 index 00000000..52b751e1 --- /dev/null +++ b/samples/instrument/benchmark_import/README.md @@ -0,0 +1,71 @@ +# Benchmark import sample + +End-to-end demo of `BenchmarkImportAdapter` — writes a 3-row CSV to +a temp dir, calls `BenchmarkImportAdapter.import_csv` against it +with a schema mapping and tags, and prints the resulting +`ImportResult`. + +> `BenchmarkImportAdapter` is a **data importer**, not a runtime +> trace adapter. It does not require any LLM credentials, does not +> instantiate an `HttpEventSink`, and does not emit runtime +> `agent.*` events. This is consistent with the adapter's design +> (per audit row for `benchmark_import`). + +## Prerequisites + +```bash +pip install 'layerlens[benchmark-import]' +``` + +The `benchmark-import` extra is intentionally empty — this sample +is replay-based and ships no additional runtime deps beyond +`layerlens` core. + +No environment variables are required. + +## Run + +```bash +uv run python -m samples.instrument.benchmark_import.main +``` + +## What this demonstrates + +| Component | What it proves (source: `main.py`) | +|---|---| +| `BenchmarkImportAdapter()` | Adapter constructs with no args — no sink, no capture config, no transport. | +| Temp CSV via `_write_sample_csv` | Three QA rows (math, geo, science) written via `csv.DictWriter`. | +| `adapter.import_csv(path=..., schema_mapping={"question": "prompt", "answer": "expected_output", "category": "category"}, tags=["sample", "qa"])` | Demonstrates the schema-mapping contract and tag attachment. | +| `result.success` / `result.errors` | Sample bails with exit code `1` and prints errors if the import fails. | +| `result.benchmark_id`, `result.records_imported`, `result.duration_ms`, `result.metadata.tags` | Sample prints every public field of the `ImportResult`. | + +## Expected output + +On success: + +```text +Benchmark id: +Records imported: 3 +Duration: ms +Tags: sample, qa +``` + +Exit code: `0`. + +On failure: + +```text +Import failed: [, , ...] +``` + +Exit code: `1`. + +## Multi-tenancy note + +This sample does not pass `org_id` to `BenchmarkImportAdapter`. The +constructor does not yet accept `org_id` — production multi-tenant +wiring lands with the PR #118 adapter-side contract (currently +DRAFT). For batch imports, the multi-tenant story is to scope the +`ImportResult.benchmark_id` to an `org_id` on the atlas-app side +when the record is persisted; the importer itself does not need +the org context to construct the dataset. diff --git a/samples/instrument/embedding/README.md b/samples/instrument/embedding/README.md new file mode 100644 index 00000000..e107428b --- /dev/null +++ b/samples/instrument/embedding/README.md @@ -0,0 +1,79 @@ +# Embedding instrumentation sample + +End-to-end demo of `EmbeddingAdapter` — wraps an OpenAI client with +`EmbeddingAdapter.wrap_openai` and runs a single `embeddings.create` +call, emitting one `embedding.create` event per the module docstring. + +## Prerequisites + +```bash +pip install 'layerlens[embedding,providers-openai]' +``` + +> The `embedding` extra is intentionally empty — vector store hooks +> come from whichever store you actually use. This sample only +> exercises the OpenAI embedding path, so it needs the +> `providers-openai` extra for the `openai` SDK. + +Required environment: + +- `OPENAI_API_KEY` — your OpenAI API key. If unset the sample exits + with code `2`. +- `LAYERLENS_STRATIX_API_KEY` — your LayerLens API key (optional). +- `LAYERLENS_STRATIX_BASE_URL` — atlas-app base URL (optional). + +## Run + +```bash +uv run python -m samples.instrument.embedding.main +``` + +## What this demonstrates + +| Component | What it proves (source: `main.py`) | +|---|---| +| `EmbeddingAdapter(capture_config=CaptureConfig.standard())` | Standard capture profile. | +| `adapter.add_sink(HttpEventSink(...))` | Batched HTTP transport. | +| `adapter.connect()` / `adapter.disconnect()` | Full lifecycle. | +| `adapter.wrap_openai(client)` | Wraps the `OpenAI()` client's `embeddings.create` path; module docstring states each call emits one `embedding.create` event. | +| `client.embeddings.create(model="text-embedding-3-small", input=[..., ...])` | Two input strings → two vectors. The sample prints vector count and dimension. | +| Token usage print | When `response.usage` is non-`None`, total tokens are printed. | + +## Expected output + +When `OPENAI_API_KEY` is unset: + +```text +OPENAI_API_KEY is not set; cannot run sample. +``` + +Exit code: `2`. + +When the `openai` SDK is not installed: + +```text +openai not installed. Install with: + pip install 'layerlens[embedding,providers-openai]' +``` + +Exit code: `2`. + +When the call succeeds: + +```text +Embeddings: 2 vectors of dim 1536 +Tokens: +Telemetry shipped. Check the LayerLens dashboard adapter health page. +``` + +(The token line is conditional on `response.usage` being non-`None`. +The dim value is whatever `text-embedding-3-small` returns; 1536 is +the documented OpenAI dimension at the time of writing.) + +## Multi-tenancy note + +This sample does not pass `org_id` to `EmbeddingAdapter`. The +constructor does not yet accept `org_id` — production multi-tenant +wiring lands with the PR #118 adapter-side contract (currently +DRAFT). Once PR #118 merges, pass `org_id` to the adapter so every +emitted event carries it.