diff --git a/redirects.mjs b/redirects.mjs
index 6edf0151..0a4b629b 100644
--- a/redirects.mjs
+++ b/redirects.mjs
@@ -44,8 +44,18 @@ export const redirects = {
'/enable-service-principal-azure': '/azure-integration/enable-service-principal-azure/',
'/get-help': '/getting-started/get-help/',
'/go-instrumentation-running-on-kubernetes': '/kloudmate-agent/auto-instrumentation/go/',
+ '/guides/llm/anthropic/send-claude-api-traces':
+ '/guides/ai-observability/anthropic/send-claude-api-traces/',
+ '/guides/llm/anthropic/send-claude-code-telemetry':
+ '/guides/ai-observability/anthropic/send-claude-code-telemetry/',
+ '/guides/llm/instrument-python-app-with-openllmetry':
+ '/guides/ai-observability/instrument-python-app-with-openllmetry/',
'/incidents-overview': '/incident-management/incidents/',
'/investigations': '/kloudmate-assistant/investigations/',
+ '/llm-observability': '/ai-observability/',
+ '/llm-observability/ai-trace-observability': '/ai-observability/agent-traces/',
+ '/llm-observability/what-is-openllmetry':
+ '/guides/ai-observability/instrument-python-app-with-openllmetry/',
'/logs-list-panel': '/visualize-data/dashboards/adding-a-panel/logs-list-panel/',
'/logs-management': '/kloudmate-agent/log-monitoring/',
'/managing-dashboards': '/visualize-data/dashboards/managing-dashboards/',
diff --git a/src/content/docs/docs/ai-observability/_meta.json b/src/content/docs/docs/ai-observability/_meta.json
new file mode 100644
index 00000000..6b8b8f04
--- /dev/null
+++ b/src/content/docs/docs/ai-observability/_meta.json
@@ -0,0 +1,5 @@
+{
+ "label": "AI Observability",
+ "order": 12.5,
+ "collapsed": true
+}
diff --git a/src/content/docs/docs/ai-observability/agent-traces.mdx b/src/content/docs/docs/ai-observability/agent-traces.mdx
new file mode 100644
index 00000000..dd37ef39
--- /dev/null
+++ b/src/content/docs/docs/ai-observability/agent-traces.mdx
@@ -0,0 +1,54 @@
+---
+title: "Agent Traces"
+description: "Read a single agent run in trace detail: the AI summary, the conversation behind each model call, token and finish-reason details, and the AI Flow graph."
+sidebar:
+ order: 4
+---
+
+When a trace contains GenAI spans, trace detail adds views for reading the agent run: the prompts it sent, the tools it called, the tokens each call spent, and the step where it went wrong. Open a run from **AI Observability → Traces**, or select any trace with GenAI spans in **Traces**.
+
+## AI summary
+
+The **AI summary** at the top of the trace shows the models the run used, the number of model and tool calls, total tokens (with cached input called out), how the time split between model calls and tool calls, and how many AI spans failed.
+
+
+
+## Conversation
+
+Select a model or agent span to open the **Conversation** tab. It shows the prompt and response as a transcript, with each message labeled by role (system, user, assistant, or tool) in the order the model saw them.
+
+
+
+- **Tool calls** the assistant made appear inline, with the tool name and the arguments the model passed.
+- **Tool results** show what each tool returned to the model.
+- **The finish reason** appears next to the role, so a truncated or filtered response stands out.
+- **The system prompt** is collapsed at the top.
+
+The **Conversation** tab appears only when your instrumentation captures message content. Each [framework guide](../send-genai-traces/#pick-your-framework) shows how to turn it on.
+
+## Gen AI details
+
+The **Gen AI** tab holds the structured details of a model or tool span:
+
+- **Tokens**: total, input (with cached input called out), and output. Cached tokens are part of the input count, not added to it.
+- **Finish reason**. Abnormal reasons such as `length` (the answer was truncated) and `content_filter` are highlighted.
+- **Request parameters** such as temperature, top-p, and max tokens.
+- **Tool definitions** offered to the model on that call, collapsed by default.
+
+For a tool span, the tab shows the tool call's arguments and result as JSON. All raw `gen_ai.*` attributes stay available on the **Attributes** tab.
+
+## AI Flow
+
+Select **AI Flow** (next to **Service List** and **Request Flow**) to see the run's GenAI spans as a graph beside the waterfall, laid out by how the agent's steps connect.
+
+Each node shows the step's kind (LLM, tool, agent, and so on), the span name, and token usage, with a badge when the call failed or finished abnormally. Leaf steps such as tool calls are collapsed into their parent node until you expand it. Select a node to open its **Conversation** and **Gen AI** tabs.
+
+## AI cues in the waterfall
+
+In the waterfall, each GenAI span has an icon for its kind: model call, tool call, agent, or retrieval.
+
+## Related resources
+
+- [Use AI Observability](../ai-observability-interface/)
+- [Send GenAI traces](../send-genai-traces/)
+- [Trace Detail](/apm-and-tracing/trace-detail/)
diff --git a/src/content/docs/docs/ai-observability/ai-observability-interface.mdx b/src/content/docs/docs/ai-observability/ai-observability-interface.mdx
new file mode 100644
index 00000000..2fc43ea9
--- /dev/null
+++ b/src/content/docs/docs/ai-observability/ai-observability-interface.mdx
@@ -0,0 +1,132 @@
+---
+title: "Use AI Observability"
+description: "Read the Overview, Traces, and Tools tabs in AI Observability: requests, tokens, estimated cost, error rate, model and agent breakdowns, runs, and tool calls."
+sidebar:
+ order: 3
+---
+
+Use **Overview** for trends and breakdowns, **Traces** for individual runs, and **Tools** for tool calls. All tabs share one time range, which defaults to the last 24 hours.
+
+## Overview
+
+**Overview** shows how much work your AI apps did in the selected range, what it cost, and how much of it failed. Each tile compares against the previous period of the same length.
+
+
+
+| Tile | What it counts |
+| --- | --- |
+| **AI Requests** | Runs your AI apps handled from start to finish. See [How runs are built](#how-runs-are-built). |
+| **Token Volume** | Input plus output tokens across all LLM calls. |
+| **LLM Calls** | Individual requests to a model. One AI request can make many LLM calls. |
+| **Error Rate** | Failed runs as a share of all runs. A step the agent recovered from, such as a tool that failed once and succeeded on retry, doesn't count as a failure. |
+| **Est. Cost** | The estimated price of every LLM call in the range. See [How cost is estimated](#how-cost-is-estimated). |
+| **LLM Latency p95** | The time 95% of individual LLM calls finish within, from the request to the last token. Long answers take longer, so this tracks output length as well as model speed. |
+
+**N LLM calls cut off** counts model calls that hit their max-token limit, so their answers may be incomplete. **N calls not priced** counts calls whose model or provider has no known price.
+
+When 1,000 or more traces in the range contain failures, the error rate is calculated from the 1,000 with the most failures and shown with a `≥` prefix, meaning the real rate is at least that high.
+
+### Trend charts
+
+- **AI Requests** and **Token Volume** (input and output).
+- **Error Rate**. A failed run counts in the interval it started in.
+- **Est. Cost Over Time**, stacked into input, output, and any other cost such as a per-request fee.
+- **LLM Latency per Call** at p50 and p95.
+- **Cache Hit Rate**, the share of input tokens read from the provider's prompt cache. A falling cache hit rate usually means a prompt prefix changed and the cache stopped matching.
+
+A run that spans several intervals counts in each of them, so the bars on the **AI Requests** chart can add up to more than the tile.
+
+### Models, Providers, and Agents
+
+
+
+- **Models** lists the 10 models with the most calls, with each model's provider, LLM calls, estimated cost, error rate, p95 latency, and input and output tokens. Models that report reasoning tokens also get a **Reasoning** column. The same model served by two providers, for example `gpt-4o-mini` through OpenAI and through OpenRouter, appears as two rows.
+- **Providers** groups LLM calls by provider and API host. The host tells you when calls go through a gateway: a call with provider `openai` and host `openrouter.ai` was sent to OpenRouter with an OpenAI model.
+- **Agents** lists the 10 busiest agents with their requests, estimated cost, error rate, and p95 latency. It appears only when your agent spans carry `gen_ai.agent.name`. An agent's cost shows a dash when its LLM calls don't carry the agent's name.
+
+## Traces
+
+**Traces** lists the runs in the selected range, newest first. Filter the list by model with **All Models**, or select **Errors** to show only runs with a failed span.
+
+
+
+| Column | Shows |
+| --- | --- |
+| **Name** | The run name. See the naming rules below. |
+| **Start time** | When the run started. |
+| **Duration** | How long the run took, with its status if it hasn't finished. See [Run status](#run-status). |
+| **Models** | The models the run called and how many times, for example `claude-opus-5-5 · 8 calls`. |
+| **Steps** | The number of LLM calls and tool calls, for example `8 LLM · 14 tools`. |
+| **Tokens (In / Out)** | Input and output tokens across the run's LLM calls. |
+| **Est. Cost** | The estimated cost of the run's LLM calls. |
+| **Error** | The error type when the run failed. **Cut off** means the run's last LLM call hit its max-token limit, so the final answer may be partial. |
+
+Select a run to open its trace. For a turn, that's the trace with the agent span; otherwise it's the run's busiest trace. [Agent Traces](../agent-traces/) covers the AI views in trace detail.
+
+### How runs are built
+
+A run is one request your AI app handled, which can span more than one trace:
+
+- A trace that contains an agent span (`invoke_agent`) is a **turn**, and starts a run.
+- A trace without an agent span joins the nearest earlier turn with the same `gen_ai.conversation.id`, or the next one if none came before. If the conversation has no turns at all, its traces form one run.
+- A trace without a conversation ID is a run of its own.
+
+Runs are named, in order of preference, after the agent (**Claude Code Turn**, or **Agent Turn** when the agent has no name), the workflow name, the root span of your application, the most-called model (**Chat · gpt-4o-mini**), or the tool (**Tool · lookup_order**).
+
+### Run status
+
+A run that hasn't finished shows its status under **Duration**. For a relative time range, open runs update every 15 seconds.
+
+| Status | Meaning |
+| --- | --- |
+| **Running** | The run had activity in the last 2 minutes. |
+| **Waiting** | The run hasn't finished and has been quiet for 2 to 10 minutes. It's usually waiting on a slow tool or on input from a person. |
+| **Incomplete** | The run's outer span never arrived: the run started before the range, or it has been quiet for more than 10 minutes. It may still be sending, or it stopped without finishing. Totals cover only the part inside the range. |
+
+**Incomplete** isn't an error. If a run you expect to see is incomplete, widen the time range so it covers the start of the run.
+
+## Tools
+
+**Tools** lists the tools your agents called in the range, with each tool's type, call count, error rate, and p95 latency. Filter by type with **All Tool Types**.
+
+
+
+| Type | Meaning |
+| --- | --- |
+| **Local** | A function that runs in your application's own code. |
+| **MCP** | A tool called through an MCP server. The server name appears after the type when the instrumentation sends it, for example **MCP · github**. |
+| **Extension** | A tool that calls an external API directly, without MCP. |
+| **Datastore** | A tool that queries a data store, for example a search or RAG lookup. |
+
+When your MCP tool calls carry a server name, turn on **Group by server** to add a subtotal row for each MCP server and compare their error rates and call counts.
+
+The same tool name can appear on more than one row. For example, a `get_weather` function your app runs locally and a `get_weather` tool on an MCP server are counted separately.
+
+### Tool detail
+
+
+
+Select a tool to see its calls and errors over time, along with:
+
+- **Used By**, the agents that called the tool, from the latest 100 calls.
+- **Error Types**, the failed calls grouped by `error.type`.
+- **Failing Calls** (the latest 10) and **Slowest Calls** (from the latest 100 calls), each with the agent, duration, status, arguments, and result. Arguments and results appear only when your instrumentation captures tool content.
+
+Select a call to open its trace at the tool span.
+
+## How cost is estimated
+
+KloudMate prices each LLM call when its span arrives, from the call's token counts and the provider's public list price. Prices come from the open-source [genai-prices](https://github.com/pydantic/genai-prices) dataset, which prices more than 1,200 models.
+
+- The provider is taken from the API host first, so a call sent through OpenRouter is priced at OpenRouter's rate.
+- The model is the one the provider reports in its response, or the requested model when the response doesn't name one.
+- Cached input tokens are priced at the provider's cache rates, and reasoning tokens are priced as output.
+- If your framework already reports a cost on the span, as Pydantic AI does, KloudMate keeps that value.
+
+Costs are estimates and won't match your invoice exactly. They don't include negotiated discounts, committed-use pricing, or batch discounts. Calls to self-hosted models, private gateways, and models the dataset doesn't list count as **calls not priced**.
+
+## Related resources
+
+- [AI Observability overview](../)
+- [Send GenAI traces](../send-genai-traces/)
+- [Agent Traces](../agent-traces/)
diff --git a/src/content/docs/docs/ai-observability/images/ai-observability-breakdowns.png b/src/content/docs/docs/ai-observability/images/ai-observability-breakdowns.png
new file mode 100644
index 00000000..4fb55efe
Binary files /dev/null and b/src/content/docs/docs/ai-observability/images/ai-observability-breakdowns.png differ
diff --git a/src/content/docs/docs/ai-observability/images/ai-observability-overview.png b/src/content/docs/docs/ai-observability/images/ai-observability-overview.png
new file mode 100644
index 00000000..f01afd0c
Binary files /dev/null and b/src/content/docs/docs/ai-observability/images/ai-observability-overview.png differ
diff --git a/src/content/docs/docs/ai-observability/images/ai-observability-tool-detail.png b/src/content/docs/docs/ai-observability/images/ai-observability-tool-detail.png
new file mode 100644
index 00000000..31801fe1
Binary files /dev/null and b/src/content/docs/docs/ai-observability/images/ai-observability-tool-detail.png differ
diff --git a/src/content/docs/docs/ai-observability/images/ai-observability-tools.png b/src/content/docs/docs/ai-observability/images/ai-observability-tools.png
new file mode 100644
index 00000000..5b1b36dd
Binary files /dev/null and b/src/content/docs/docs/ai-observability/images/ai-observability-tools.png differ
diff --git a/src/content/docs/docs/ai-observability/images/ai-observability-traces.png b/src/content/docs/docs/ai-observability/images/ai-observability-traces.png
new file mode 100644
index 00000000..c96f7d56
Binary files /dev/null and b/src/content/docs/docs/ai-observability/images/ai-observability-traces.png differ
diff --git a/src/content/docs/docs/llm-observability/images/llm-conversation.png b/src/content/docs/docs/ai-observability/images/llm-conversation.png
similarity index 100%
rename from src/content/docs/docs/llm-observability/images/llm-conversation.png
rename to src/content/docs/docs/ai-observability/images/llm-conversation.png
diff --git a/src/content/docs/docs/llm-observability/images/llm-overview.png b/src/content/docs/docs/ai-observability/images/llm-overview.png
similarity index 100%
rename from src/content/docs/docs/llm-observability/images/llm-overview.png
rename to src/content/docs/docs/ai-observability/images/llm-overview.png
diff --git a/src/content/docs/docs/ai-observability/index.mdx b/src/content/docs/docs/ai-observability/index.mdx
new file mode 100644
index 00000000..bb13d61a
--- /dev/null
+++ b/src/content/docs/docs/ai-observability/index.mdx
@@ -0,0 +1,54 @@
+---
+title: "AI Observability"
+description: "Track requests, tokens, estimated cost, errors, and tool calls across every LLM app and agent that sends OpenTelemetry GenAI traces to KloudMate."
+sidebar:
+ label: "Overview"
+ order: 1
+---
+
+AI Observability gives you end-to-end visibility into the LLM applications and agents running in your workspace. A single request to an agent can fan out into several model calls, tool calls, and retries across different providers, and each step adds latency, token cost, and another point of failure. AI Observability traces every request through those steps, so you can track usage and spend by model, provider, and agent, find the tool calls that fail or slow a run down, and open any run to read the prompts, responses, and tool calls behind it.
+
+It's built on the OpenTelemetry GenAI semantic conventions, so any app or agent that exports GenAI traces shows up here, whichever framework or model provider it uses. You don't need a proprietary SDK.
+
+Open it from **AI Observability** in the sidebar:
+
+- **Overview** tracks requests, token volume, estimated cost, error rate, and latency over time, with breakdowns by model, provider, and agent.
+- **Traces** lists each agent run with its models, steps, tokens, and cost. Select a run to open its trace.
+- **Tools** reports how often each tool is called, how often it fails, and how long it takes, for both local functions and MCP tools.
+
+
+
+## What KloudMate does with your traces
+
+Frameworks describe the same thing in different ways. OpenInference reports input tokens as `llm.token_count.prompt`, while the Vercel AI SDK uses `ai.usage.inputTokens`. When your spans arrive, KloudMate maps them onto the [OpenTelemetry GenAI semantic conventions](https://github.com/open-telemetry/semantic-conventions-genai/blob/main/docs/gen-ai/README.md), so a Claude Code turn and a LangGraph run are counted the same way:
+
+- **Attribute names** from each [supported framework](./send-genai-traces/#pick-your-framework), and older GenAI names such as `gen_ai.system`, are mapped to the current names.
+- **Provider names and finish reasons** are made consistent. For example, `end_turn` becomes `stop` and `max_tokens` becomes `length`.
+- **Missing values are filled in** from related spans. A conversation ID set on a parent span is copied to the GenAI spans under it, and an agent span without its own input and output gets them from its first and last model calls.
+- **Internal framework steps aren't counted twice.** For example, an MCP tool call recorded by both the client and the server counts once.
+- **Each model call gets an estimated cost.** See [How cost is estimated](./ai-observability-interface/#how-cost-is-estimated).
+
+Renamed attributes keep their values, and values that already follow the conventions aren't changed.
+
+## Key concepts
+
+| Term | Meaning |
+| --- | --- |
+| Run | One request your AI app handled from start to finish. The **AI Requests** count and the rows on the **Traces** tab are runs. See [How runs are built](./ai-observability-interface/#how-runs-are-built). |
+| Turn | A run that starts with an agent span (`invoke_agent`), named after the agent, for example **Claude Code Turn**. |
+| LLM call | A single request to a model. One run can make many LLM calls. |
+| Tool call | A span whose operation is `execute_tool`, labeled **Local**, **MCP**, **Extension**, or **Datastore**. |
+| Conversation | The traces that share a `gen_ai.conversation.id`. |
+| Est. Cost | The estimated price of the model calls in USD. See [How cost is estimated](./ai-observability-interface/#how-cost-is-estimated). |
+
+## Get started
+
+1. Create an API key in **Settings → Ingest Keys**.
+2. Instrument your app or agent and point its exporter at KloudMate. [Send GenAI traces](./send-genai-traces/) links to a setup guide for each supported framework.
+3. Run your app, then open **AI Observability**.
+
+## Related resources
+
+- [Use AI Observability](./ai-observability-interface/)
+- [Agent Traces](./agent-traces/)
+- [Send GenAI traces](./send-genai-traces/)
diff --git a/src/content/docs/docs/ai-observability/send-genai-traces.mdx b/src/content/docs/docs/ai-observability/send-genai-traces.mdx
new file mode 100644
index 00000000..0dc0e384
--- /dev/null
+++ b/src/content/docs/docs/ai-observability/send-genai-traces.mdx
@@ -0,0 +1,104 @@
+---
+title: "Send GenAI Traces"
+description: "Send OpenTelemetry GenAI traces from any LLM app or agent framework to KloudMate: the endpoint, the attributes AI Observability reads, and a setup guide for each supported framework."
+sidebar:
+ order: 2
+---
+
+AI Observability works with any app or agent that exports OpenTelemetry traces with GenAI spans. You don't install a KloudMate SDK. Turn on the OpenTelemetry support your framework already has, or add an instrumentation library, and point the exporter at KloudMate.
+
+## Pick your framework
+
+| Framework | Instrumentation | Guide |
+| --- | --- | --- |
+| Claude Code and Cowork | Built in | [Claude Code and Cowork](/guides/ai-observability/anthropic/send-claude-code-telemetry/) |
+| Claude API and SDK | Built in | [Claude API and SDK](/guides/ai-observability/anthropic/send-claude-api-traces/) |
+| OpenAI SDK (Python, Node.js) | OpenTelemetry instrumentation | [OpenAI SDK](/guides/ai-observability/openai/openai-sdk/) |
+| OpenAI Agents SDK | OpenInference | [OpenAI Agents SDK](/guides/ai-observability/openai/agents-sdk/) |
+| OpenAI Codex | Built in | [OpenAI Codex](/guides/ai-observability/openai/codex/) |
+| CrewAI | OpenLLMetry | [CrewAI](/guides/ai-observability/crewai/) |
+| Google ADK | Built in | [Google ADK](/guides/ai-observability/google-adk/) |
+| LangChain and LangGraph | OpenInference | [LangChain and LangGraph](/guides/ai-observability/langchain-langgraph/) |
+| LlamaIndex | OpenInference | [LlamaIndex](/guides/ai-observability/llamaindex/) |
+| Mastra | Built in | [Mastra](/guides/ai-observability/mastra/) |
+| Pydantic AI | Built in | [Pydantic AI](/guides/ai-observability/pydantic-ai/) |
+| Vercel AI SDK | Built in | [Vercel AI SDK](/guides/ai-observability/vercel-ai-sdk/) |
+
+Other libraries work too, as long as they emit spans that follow the [OpenTelemetry GenAI semantic conventions](https://github.com/open-telemetry/semantic-conventions-genai/blob/main/docs/gen-ai/README.md). That includes the OpenTelemetry instrumentations for other model providers and the other frameworks that [OpenLLMetry](https://www.traceloop.com/docs/openllmetry/tracing/supported) and [OpenInference](https://github.com/Arize-ai/openinference) instrument.
+
+## Point the exporter at KloudMate
+
+Most OpenTelemetry SDKs read the standard exporter variables:
+
+```bash
+export OTEL_SERVICE_NAME="support-agent"
+export OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"
+export OTEL_EXPORTER_OTLP_ENDPOINT="https://otel.kloudmate.com:4318"
+export OTEL_EXPORTER_OTLP_HEADERS="Authorization=YOUR_KLOUDMATE_API_KEY"
+```
+
+- Create the API key in **Settings → Ingest Keys**. KloudMate expects the raw key in the `Authorization` header, with no `Bearer` prefix.
+- Don't add `/v1/traces` to `OTEL_EXPORTER_OTLP_ENDPOINT`, because the exporter appends it. When a framework asks for a traces URL instead, use `https://otel.kloudmate.com:4318/v1/traces`.
+- If the [KloudMate agent](../../kloudmate-agent/) runs on the same machine, set the endpoint to `http://localhost:4318` and leave out the header.
+
+AI Observability reads **traces** only. Some instrumentations write prompts and responses to log events by default. Each framework guide shows how to put them on spans instead.
+
+## What KloudMate reads from a span
+
+If you write spans by hand, set these attributes. Older names, such as `gen_ai.system`, are renamed to the current ones at ingest.
+
+| Attribute | Used for |
+| --- | --- |
+| `gen_ai.operation.name` | Required on every GenAI span. `chat`, `generate_content`, `text_completion`, and `embeddings` are LLM calls. `invoke_agent` starts a turn. `execute_tool` is a tool call. |
+| `gen_ai.request.model`, `gen_ai.provider.name` | The **Models** and **Providers** tables, and the price lookup. |
+| `gen_ai.usage.input_tokens`, `gen_ai.usage.output_tokens` | Token volume and cost. Cached input and reasoning tokens are counted as part of these totals. |
+| `gen_ai.usage.cache_read.input_tokens` | **Cache Hit Rate**, and the cache price in the cost estimate. |
+| `gen_ai.response.finish_reasons` | **Cut off** on runs and the cut-off count on **Overview**. |
+| `server.address` | The **Host** column, and the price lookup for calls through a gateway such as OpenRouter. |
+| `gen_ai.agent.name` | The run name and the **Agents** table. Set it on the model calls too, or the agent's cost can't be added up. |
+| `gen_ai.conversation.id` | Groups the traces of one conversation. |
+| `gen_ai.tool.name`, `gen_ai.tool.type` | The **Tools** tab. For MCP tools, set `mcp.method.name` to `tools/call` and `mcp.server.name` to the server name. |
+| `gen_ai.input.messages`, `gen_ai.output.messages`, `gen_ai.tool.call.arguments`, `gen_ai.tool.call.result` | The conversation view and tool arguments and results. |
+| `error.type` and the span status | Error rate and the **Error** column. |
+
+:::note
+Prompts, responses, and tool arguments can contain personal or sensitive data. Every metric on the Overview, Traces, and Tools tabs works without them, so capture content only if you want to read conversations in KloudMate.
+:::
+
+## Group a conversation
+
+When a framework doesn't send a conversation ID, each trace shows as its own run. Set `gen_ai.conversation.id` on a span that wraps the AI work, and KloudMate copies it to the GenAI spans underneath:
+
+```python
+from opentelemetry import trace
+
+tracer = trace.get_tracer("support-agent")
+
+with tracer.start_as_current_span("handle_message") as span:
+ span.set_attribute("gen_ai.conversation.id", chat_id)
+ run_agent(message)
+```
+
+Use an ID that stays the same for the whole conversation, such as your chat or thread ID. Don't use a user ID, because one user can have many conversations.
+
+KloudMate copies the ID only to spans that arrive in the same export as the wrapping span. If a request runs longer than the exporter's batch delay, 5 seconds by default, set `OTEL_BSP_SCHEDULE_DELAY` (in milliseconds) above the request's duration.
+
+## Check that data arrives
+
+Run your app once, then open **AI Observability → Traces**. If the run doesn't appear:
+
+- Check that the exporter sends traces over HTTP to port `4318`, and that `OTEL_EXPORTER_OTLP_PROTOCOL` is `http/protobuf`.
+- Check that the `Authorization` header holds a valid key with no `Bearer` prefix.
+- Search for your service in **Traces** in the main sidebar. If the spans are there but AI Observability is empty, they don't carry `gen_ai.operation.name`, so the instrumentation isn't emitting GenAI spans.
+
+## Known limitations
+
+- **Token counts are used as the framework reports them.** Some instrumentations leave out cached or cache-write tokens. Missing cache reads make the cache hit rate read low and the cost read high. Missing cache writes make the cost read low.
+- **Two instrumentations on the same call count it twice.** For example, LangChain instrumentation plus OpenAI SDK instrumentation records each model call twice. Instrument one layer only.
+- **Batch API calls are priced at the standard rate.** No framework marks a call as a batch call.
+
+## Related resources
+
+- [Use AI Observability](../ai-observability-interface/)
+- [Agent Traces](../agent-traces/)
+- [OpenTelemetry GenAI semantic conventions](https://github.com/open-telemetry/semantic-conventions-genai)
diff --git a/src/content/docs/docs/apm-and-tracing/index.mdx b/src/content/docs/docs/apm-and-tracing/index.mdx
index 9b68ff0a..94f494b7 100644
--- a/src/content/docs/docs/apm-and-tracing/index.mdx
+++ b/src/content/docs/docs/apm-and-tracing/index.mdx
@@ -31,6 +31,6 @@ Application Metrics and Custom Metrics go through the same OpenTelemetry SDK flo
## Related Paths
-- [LLM Observability](../llm-observability/)
+- [AI Observability](../ai-observability/)
- [Visualize Data](../visualize-data/)
- [Logs](../logs/)
diff --git a/src/content/docs/docs/apm-and-tracing/trace-detail.mdx b/src/content/docs/docs/apm-and-tracing/trace-detail.mdx
index f2e5a725..fe5e52f1 100644
--- a/src/content/docs/docs/apm-and-tracing/trace-detail.mdx
+++ b/src/content/docs/docs/apm-and-tracing/trace-detail.mdx
@@ -15,7 +15,7 @@ Until you select a span, the left panel shows the trace summary:
- **Per-service time**: each service in the trace with its share of the request time, so you can see where the request spent most of its time.
- **Errors**: any errored spans, with a link to jump straight to the span.
-When the trace contains GenAI spans, an **AI summary** appears here too: models, model and tool call counts, token usage, and the model-versus-tool time split. See [Agentic Workflow Observability](/llm-observability/ai-trace-observability/).
+When the trace contains GenAI spans, an **AI summary** appears here too: models, model and tool call counts, token usage, and the model-versus-tool time split. See [Agent Traces](/ai-observability/agent-traces/).
## Timeline
@@ -34,7 +34,7 @@ Selecting a span replaces the summary with the span's detail, organized into tab
- **Request** and **Response** for HTTP spans: method, route, status, headers, and body.
- **Database** for database spans: the statement and database attributes.
- **Message** for messaging spans.
-- **Conversation** and **Gen AI** for LLM spans: the prompt and response transcript, token usage, finish reason, and tool calls. See [Agentic Workflow Observability](/llm-observability/ai-trace-observability/).
+- **Conversation** and **Gen AI** for LLM spans: the prompt and response transcript, token usage, finish reason, and tool calls. See [Agent Traces](/ai-observability/agent-traces/).
- **Info** for service, host, Kubernetes, and deployment metadata.
- **Events** and **Links** when the span has them.
- **Attributes** for the full attribute set, grouped by semantic-convention prefix.
@@ -46,7 +46,7 @@ The toggle at the top right switches how the right panel presents the trace:
- **Service List** lists the spans in a table (operation, type, and duration) for a flat, scannable view of everything in the trace.
- **Request Flow** draws the trace as a service graph, so you can see how services called each other and where errors propagated.
-- **AI Flow** appears for traces with GenAI spans. It lays out the model and tool steps as a workflow graph. See [Agentic Workflow Observability](/llm-observability/ai-trace-observability/).
+- **AI Flow** appears for traces with GenAI spans. It lays out the model and tool steps as a workflow graph. See [Agent Traces](/ai-observability/agent-traces/).
Selecting a span in any view, or in the timeline, keeps the detail panel in sync.
@@ -61,4 +61,4 @@ Two shortcuts sit in the trace header:
- [Trace Explorer](/apm-and-tracing/trace-explorer/)
- [APM Views](/apm-and-tracing/apm-views/)
-- [Agentic Workflow Observability](/llm-observability/ai-trace-observability/)
+- [Agent Traces](/ai-observability/agent-traces/)
diff --git a/src/content/docs/docs/llm-observability/_meta.json b/src/content/docs/docs/llm-observability/_meta.json
deleted file mode 100644
index 13412742..00000000
--- a/src/content/docs/docs/llm-observability/_meta.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "label": "LLM Observability",
- "order": 13,
- "collapsed": true
-}
diff --git a/src/content/docs/docs/llm-observability/ai-trace-observability.mdx b/src/content/docs/docs/llm-observability/ai-trace-observability.mdx
deleted file mode 100644
index 1940fa1e..00000000
--- a/src/content/docs/docs/llm-observability/ai-trace-observability.mdx
+++ /dev/null
@@ -1,76 +0,0 @@
----
-title: "Agentic Workflow Observability"
-description: "See prompts, tool calls, token usage, and finish reasons for any agent run in KloudMate: the AI summary, conversation view, and AI Flow graph."
-sidebar:
- order: 2
----
-
-KloudMate reads any agent run as a trace you can step through: the prompts it sent, the tools it called, the tokens each call spent, and the point where it went wrong. When the agent emits OpenTelemetry GenAI spans, these views appear on top of the normal trace detail, with nothing extra to switch on.
-
-To get there, open **Traces**, find a trace from your agent, and select it. The sections below walk through what you'll see.
-
-## What KloudMate can observe
-
-These views work for any workload that emits OpenTelemetry GenAI spans:
-
-- **Agentic applications.** An AI feature inside your product, such as a support agent that looks up an order and issues a refund. The screenshots here come from one of them, a service named `ecommerce-support-agent`.
-- **Standalone AI agents.** An agent service or worker that plans, calls tools, and acts on its own.
-- **Coding agents.** Developer agents that read code, run commands, and call models. A growing number emit OpenTelemetry telemetry, so when they produce GenAI spans, those runs land here alongside the rest.
-
-All of them export the same OpenTelemetry GenAI spans, so KloudMate reads them the same way. You don't run a separate pipeline for agent telemetry: the model and tool spans sit in the same traces as your HTTP, database, and queue spans.
-
-## AI summary
-
-At the top of an agent trace, the **AI summary** gives you the shape of the run at a glance: the models it used, how many model and tool calls ran, total tokens (with cached input called out), the time split between model and tool calls, and how many AI spans errored.
-
-
-
-It answers the first questions you tend to have about a run. Which model ran? How many tokens did it spend? Did the time go into the model or the tools?
-
-## Conversation view
-
-Select a model span to open the **Conversation** tab, where KloudMate renders the prompt and response as a transcript instead of raw JSON. Each turn is labeled by role (system, user, assistant, or tool) and shown in order.
-
-
-
-The transcript captures the parts that matter when an answer looks wrong:
-
-- **Assistant tool calls** appear inline with the tool name and the arguments the model passed.
-- **Tool results** show what each tool returned.
-- **The finish reason** sits next to the role, so a truncated or filtered response stands out.
-- **Copy** any turn with the button in its header.
-
-When a model does something you didn't expect, this is the fastest way to see what it was told and what it said back.
-
-## Token, cache, and finish-reason details
-
-The **Gen AI** tab holds the structured details for a model or tool span:
-
-- **Tokens** on a single line: total, input (with cached input called out), and output. Cached tokens are part of the input count rather than added on top, so the number matches what you were billed for.
-- **Finish reason** chips. A normal `stop` reads as neutral; an abnormal reason like `length` (truncated) or `content_filter` is highlighted so you don't miss it.
-- **Request parameters** the call used, such as temperature, top-p, and max tokens.
-- **Tool definitions** offered to the model on that call, collapsed by default.
-
-For tool spans, the Gen AI tab shows the **tool call**, its arguments and result, as structured JSON. The full set of raw `gen_ai.*` attributes stays available under the **Attributes** tab.
-
-## AI Flow
-
-Switch the view mode to **AI Flow** (top right of the trace) to read the run as a workflow graph instead of a time-ordered waterfall. AI Flow shows only the GenAI spans, laid out by how the agent's steps connect.
-
-Each node shows the step's kind (LLM, tool, agent, and so on), the model, token usage including cached tokens, and a badge when a call finished abnormally or errored. Click a node to open its span detail, the same Conversation and Gen AI tabs described above. For a long, linear agent chain, this reads more easily than the waterfall.
-
-## AI cues in the waterfall
-
-The standard timeline stays AI-aware. GenAI spans get distinct icons by kind, so model calls, tool calls, agents, and retrieval steps each read differently. Model spans also show the model name inline on the row. That makes the model and tool steps easy to pick out among a trace's other spans.
-
-## Where the data comes from
-
-These views build on standard OpenTelemetry GenAI attributes, so they work with any compatible instrumentation. The conversation view specifically needs the instrumentation to capture prompt and response content.
-
-If you're building your own agent, the fastest path is OpenLLMetry, which auto-instruments common frameworks and providers. To produce traces like the ones above, see [Instrument a Python LLM App with OpenLLMetry](/guides/llm/instrument-python-app-with-openllmetry/). If your agent already exports OpenTelemetry GenAI spans, point it at KloudMate and the same views apply.
-
-## Related resources
-
-- [Instrument a Python LLM App with OpenLLMetry](/guides/llm/instrument-python-app-with-openllmetry/)
-- [Introduction to OpenLLMetry](../what-is-openllmetry/)
-- [Trace Detail](/apm-and-tracing/trace-detail/), the trace views that agentic workflow observability builds on
diff --git a/src/content/docs/docs/llm-observability/images/llm-observability-with-kloudmate-and-openllmetry-1.png b/src/content/docs/docs/llm-observability/images/llm-observability-with-kloudmate-and-openllmetry-1.png
deleted file mode 100644
index 81bd3efa..00000000
Binary files a/src/content/docs/docs/llm-observability/images/llm-observability-with-kloudmate-and-openllmetry-1.png and /dev/null differ
diff --git a/src/content/docs/docs/llm-observability/images/llm-observability-with-kloudmate-and-openllmetry-2.png b/src/content/docs/docs/llm-observability/images/llm-observability-with-kloudmate-and-openllmetry-2.png
deleted file mode 100644
index 13e84dd2..00000000
Binary files a/src/content/docs/docs/llm-observability/images/llm-observability-with-kloudmate-and-openllmetry-2.png and /dev/null differ
diff --git a/src/content/docs/docs/llm-observability/images/openllmetry_-opentelemetry-based-observability-for-llms-1.png b/src/content/docs/docs/llm-observability/images/openllmetry_-opentelemetry-based-observability-for-llms-1.png
deleted file mode 100644
index d852b80c..00000000
Binary files a/src/content/docs/docs/llm-observability/images/openllmetry_-opentelemetry-based-observability-for-llms-1.png and /dev/null differ
diff --git a/src/content/docs/docs/llm-observability/index.mdx b/src/content/docs/docs/llm-observability/index.mdx
deleted file mode 100644
index 4fe13c27..00000000
--- a/src/content/docs/docs/llm-observability/index.mdx
+++ /dev/null
@@ -1,33 +0,0 @@
----
-title: "LLM Observability"
-description: "Trace, debug, and understand LLM and agent applications in KloudMate: prompts, tool calls, token usage, and latency."
-sidebar:
- label: "Overview"
- order: 1
----
-
-A single answer from an LLM agent can hide several model calls, a handful of tool calls, and a retry or two. When that answer is wrong, slow, or expensive, a flat log line won't tell you why. You need to see the whole chain.
-
-KloudMate traces LLM and agent applications as part of its OpenTelemetry-based tracing. Each run becomes one distributed trace, so you can read the model's reasoning, every tool call, the tokens each call spent, and where the time went, alongside the rest of your application's traces, in the same place.
-
-## What you can do
-
-- **Read the full conversation** behind a span (system, user, assistant, and tool turns) without reconstructing it from raw attributes.
-- **See token usage** per call and per trace, including cached input tokens, so you can find the calls driving cost.
-- **Follow the agent's tools**: which tools it called, with what arguments, and what they returned.
-- **Catch failures and truncations**: tool errors and abnormal finish reasons (`length`, `content_filter`) are surfaced, not buried.
-- **Read the run as a graph** with AI Flow, which lays out the model and tool steps as a workflow.
-- **Attribute traces to a user or session** when your instrumentation tags them, so you can follow one customer or one conversation.
-
-[Agentic Workflow Observability](./ai-trace-observability/) covers each of these in detail.
-
-## Getting data in
-
-KloudMate reads standard OpenTelemetry GenAI spans, so any OpenTelemetry-compatible instrumentation works. The fastest path is [OpenLLMetry](./what-is-openllmetry/), which auto-instruments common frameworks and providers.
-
-To instrument an app from scratch, follow [Instrument a Python LLM App with OpenLLMetry](/guides/llm/instrument-python-app-with-openllmetry/).
-
-## In this section
-
-- [Agentic Workflow Observability](./ai-trace-observability/), what KloudMate shows you on an agent trace
-- [Introduction to OpenLLMetry](./what-is-openllmetry/), how the data is captured
diff --git a/src/content/docs/docs/llm-observability/what-is-openllmetry.mdx b/src/content/docs/docs/llm-observability/what-is-openllmetry.mdx
deleted file mode 100644
index 5d0db81d..00000000
--- a/src/content/docs/docs/llm-observability/what-is-openllmetry.mdx
+++ /dev/null
@@ -1,36 +0,0 @@
----
-title: "Introduction to OpenLLMetry"
-description: "How OpenLLMetry extends OpenTelemetry to capture traces from LLM and agent applications."
-sidebar:
- order: 3
----
-
-LLM applications are built from model calls, tool calls, and framework glue, and their behavior shifts with every prompt. To debug and tune them, you need the same tracing you'd use for any distributed system, adapted to capture model-specific details like prompts, token usage, and tool calls.
-
-[OpenTelemetry](https://opentelemetry.io/) is the open standard for that tracing. OpenLLMetry extends it to the LLM layer.
-
-## What is OpenLLMetry?
-
-[OpenLLMetry](https://www.traceloop.com/docs/openllmetry/introduction) is an open-source SDK from Traceloop that adds LLM instrumentation to OpenTelemetry. It's Apache-2.0 licensed and built on the same protocols OpenTelemetry uses, so the traces it produces flow to any OpenTelemetry backend, including KloudMate.
-
-Because it builds on OpenTelemetry, you don't run a separate pipeline for AI telemetry. Model and tool spans sit in the same traces as your HTTP, database, and queue spans.
-
-## How it works
-
-OpenLLMetry ships instrumentations for the common pieces of an LLM stack:
-
-- **Providers** such as OpenAI, Anthropic, and Cohere.
-- **Frameworks** such as LangChain and LlamaIndex.
-- **Vector databases** such as Pinecone and Chroma.
-
-When you initialize the SDK, it patches these libraries so each model call, tool call, and chain step emits a span, with attributes for the prompt, response, model, and token usage. Your application logic stays the same; you add the SDK and point it at your backend.
-
-
-
-The SDK exports over OTLP, the OpenTelemetry wire protocol, so sending data to KloudMate comes down to setting the endpoint and your API key.
-
-## Related resources
-
-- [Instrument a Python LLM App with OpenLLMetry](/guides/llm/instrument-python-app-with-openllmetry/)
-- [Agentic Workflow Observability](../ai-trace-observability/)
-- [What Is OpenTelemetry?](../../opentelemetry/what-is-opentelemetry/)
diff --git a/src/content/docs/guides/ai-observability/_meta.json b/src/content/docs/guides/ai-observability/_meta.json
new file mode 100644
index 00000000..0fc2e8e0
--- /dev/null
+++ b/src/content/docs/guides/ai-observability/_meta.json
@@ -0,0 +1,4 @@
+{
+ "label": "AI Observability",
+ "order": 4
+}
diff --git a/src/content/docs/guides/llm/anthropic/_meta.json b/src/content/docs/guides/ai-observability/anthropic/_meta.json
similarity index 68%
rename from src/content/docs/guides/llm/anthropic/_meta.json
rename to src/content/docs/guides/ai-observability/anthropic/_meta.json
index 3188ced1..9035453b 100644
--- a/src/content/docs/guides/llm/anthropic/_meta.json
+++ b/src/content/docs/guides/ai-observability/anthropic/_meta.json
@@ -1,4 +1,4 @@
{
"label": "Anthropic",
- "order": 2
+ "order": 1
}
diff --git a/src/content/docs/guides/llm/anthropic/send-claude-api-traces.mdx b/src/content/docs/guides/ai-observability/anthropic/send-claude-api-traces.mdx
similarity index 89%
rename from src/content/docs/guides/llm/anthropic/send-claude-api-traces.mdx
rename to src/content/docs/guides/ai-observability/anthropic/send-claude-api-traces.mdx
index 1df64de5..eeb2effb 100644
--- a/src/content/docs/guides/llm/anthropic/send-claude-api-traces.mdx
+++ b/src/content/docs/guides/ai-observability/anthropic/send-claude-api-traces.mdx
@@ -10,7 +10,7 @@ import { Tabs, TabItem } from '@astrojs/starlight/components';
The Claude API and the Claude SDK can emit an OpenTelemetry span for each Messages API request, model run, tool use, and Managed Agents turn. Send these spans to KloudMate to debug failed or slow requests, track token usage and latency, and see each Claude call inside your application's own traces.
-You can send traces from two places, and you can use both at once:
+Use API tracing, SDK tracing, or both:
- **API tracing** sends spans from the Claude API itself. You add a destination in the Claude Console, and every request in the workspaces it covers is traced, from any client, with no code change. It's the only way to see each model run inside a request, server tools such as web search, and Managed Agents turns.
- **SDK tracing** sends spans from the Claude SDK in your application, through your own OpenTelemetry exporter. It also traces the client tools that the SDK's tool runner runs, and it nests each Claude call under your application's spans.
@@ -194,19 +194,9 @@ For API tracing, set it on the destination. For SDK tracing, set `ANTHROPIC_OPEN
With **Full content**, prompts and responses are stored in KloudMate under your workspace's retention settings, so check that this meets your organization's requirements before you turn it on. Each span holds up to 0.75 MB of content by default, and longer content is shortened and marked with `anthropic.content.truncated`.
-## Read the spans
+## Read the traces
-| Span | Emitted by | What it covers |
-| --- | --- | --- |
-| `anthropic.messages.create` | SDK (`CLIENT`) and API (`SERVER`) | One Messages API request |
-| `anthropic.model_request` | API | One model run inside a request |
-| `anthropic.tool_use`, `anthropic.mcp_tool_use` | API | A server tool or an MCP connector tool that the API runs |
-| `anthropic.custom_tool_use` | SDK | A client tool that the SDK's tool runner runs |
-| `anthropic.session.turn` | API | One Managed Agents turn |
-
-Token usage is in `gen_ai.usage.input_tokens` and `gen_ai.usage.output_tokens`, and `gen_ai.usage.input_tokens` already includes prompt cache reads and writes. A request's usage appears on more than one span, so when you chart tokens, sum only the `anthropic.messages.create` spans from one source. If you use both API and SDK tracing, sum the `SERVER` spans.
-
-For every span and attribute, see the [Claude OpenTelemetry span reference](https://platform.claude.com/docs/en/api/opentelemetry/spans). For what KloudMate shows on an LLM trace, see [Agentic Workflow Observability](/llm-observability/ai-trace-observability/).
+Each Claude call appears in [AI Observability](/ai-observability/) with its model, tokens, and estimated cost. If you use both API and SDK tracing, each request is recorded once by each, so AI Observability counts its tokens and cost twice. Send only one source to KloudMate, or filter by service. For every span and attribute, see the [Claude OpenTelemetry span reference](https://platform.claude.com/docs/en/api/opentelemetry/spans).
## Troubleshooting
diff --git a/src/content/docs/guides/llm/anthropic/send-claude-code-telemetry.mdx b/src/content/docs/guides/ai-observability/anthropic/send-claude-code-telemetry.mdx
similarity index 76%
rename from src/content/docs/guides/llm/anthropic/send-claude-code-telemetry.mdx
rename to src/content/docs/guides/ai-observability/anthropic/send-claude-code-telemetry.mdx
index 32339a64..fe23aee6 100644
--- a/src/content/docs/guides/llm/anthropic/send-claude-code-telemetry.mdx
+++ b/src/content/docs/guides/ai-observability/anthropic/send-claude-code-telemetry.mdx
@@ -44,10 +44,10 @@ Replace `YOUR_KLOUDMATE_API_KEY` with your KloudMate API key. KloudMate expects
If the [KloudMate agent](../../../../kloudmate-agent/installation/) runs on the same machine, set `OTEL_EXPORTER_OTLP_ENDPOINT` to `http://localhost:4318` and leave out `OTEL_EXPORTER_OTLP_HEADERS`. The agent forwards the data with your workspace's API key.
-Claude Code reads the settings when a session starts, so restart Claude Code, or start a new session in Claude Desktop. Within a minute, the `claude_code.session.count` metric and the `claude_code.user_prompt` events appear in KloudMate under the service `claude-code`, or `claude-code-desktop` for a **Code** tab session.
+Claude Code reads the settings when a session starts, so restart Claude Code, or start a new session in Claude Desktop. Within a minute, data appears in KloudMate under the service `claude-code`, or `claude-code-desktop` for a **Code** tab session.
:::tip
-You can also `export` the same variables in your shell before you run `claude`. Claude Desktop reads only a few variables from your shell profile, so use `settings.json` for the **Code** tab.
+You can also `export` the same variables in your shell before you run `claude`. For the Claude Desktop **Code** tab, put the variables in `~/.claude/settings.json` or a saved local environment. A value already set in the environment Desktop starts with takes precedence over `settings.json`.
:::
### Turn on traces
@@ -59,7 +59,7 @@ Traces are in beta. Add these variables to the same `env` block to export a trac
"OTEL_TRACES_EXPORTER": "otlp"
```
-Each trace starts at a `claude_code.interaction` span for one prompt. Under it, `claude_code.llm_request` spans carry the model, token counts, time to first token, and the `gen_ai.*` attributes, and `claude_code.tool` spans show each tool call, including the time spent waiting for your approval.
+Each prompt then appears in [AI Observability](/ai-observability/) as a **Claude Code Turn**, with its model calls, tool calls, tokens, and estimated cost. To see the prompt text in the turn, also set `OTEL_LOG_USER_PROMPTS=1`. See [Choose what content is exported](#choose-what-content-is-exported).
## Roll out to your team
@@ -87,7 +87,7 @@ As an Owner of a Claude for Teams or Claude for Enterprise organization, you can
3\. Save. Claude Code picks up the settings the next time each developer starts it. Because the settings set an export endpoint, Claude Code asks each developer to approve them once, in their next interactive terminal session.
-Managed settings take precedence over each developer's own settings, so developers can't point the export somewhere else. They reach sessions that sign in with the organization's Team or Enterprise account. A session that uses an API key from the Claude Console, Amazon Bedrock, Google Cloud, or Microsoft Foundry doesn't fetch them.
+Managed settings take precedence over each developer's own settings, so developers can't point the export somewhere else. They reach sessions that sign in with the organization's Team or Enterprise account. Sessions that authenticate with a Claude Console API key, or that use Amazon Bedrock, Google Cloud, or Microsoft Foundry, don't receive them.
For those sessions, or if you manage devices with MDM, deploy the same JSON as a `managed-settings.json` file instead:
@@ -113,7 +113,7 @@ For those sessions, or if you manage devices with MDM, deploy the same JSON as a
## Send Cowork events
-Cowork exports events, not metrics or traces, and it has its own settings. It's available on the Team and Enterprise plans, and needs Claude Desktop 1.1.4173 or later.
+Cowork exports events, not metrics or traces, and it has its own settings. It's available on the Team and Enterprise plans, and needs Claude Desktop 1.1.4173 or later for local sessions, or 1.22209.3 or later for cloud sessions. See [Monitor Claude Cowork activity with OpenTelemetry](https://support.claude.com/en/articles/14477985-monitor-claude-cowork-activity-with-opentelemetry).
1\. On claude.ai, go to **Organization settings → Cowork**.
@@ -127,7 +127,7 @@ Cowork exports events, not metrics or traces, and it has its own settings. It's
3\. Save. Cowork reads the settings when a session starts, so the events arrive from the next Cowork session each user starts.
-In KloudMate, Cowork events are logs with the service `cowork`. They include `user_prompt`, `assistant_response`, `api_request` with the model, tokens, and estimated cost, `tool_result`, `tool_decision`, and `api_error`. A shared `prompt.id` links all the events from one prompt.
+Cowork events arrive in KloudMate **Logs** under the service `cowork`.
## Choose what content is exported
@@ -136,24 +136,18 @@ By default, Claude Code and Cowork export metadata only: counts, durations, mode
| Variable | Adds |
| --- | --- |
| `OTEL_LOG_USER_PROMPTS=1` | Prompt text, and model response text unless you set `OTEL_LOG_ASSISTANT_RESPONSES=0` |
-| `OTEL_LOG_TOOL_DETAILS=1` | Tool arguments, such as Bash commands and file paths, and MCP server and tool names |
-| `OTEL_LOG_TOOL_CONTENT=1` | Tool output, on trace spans |
+| `OTEL_LOG_TOOL_DETAILS=1` | Tool arguments, such as Bash commands and file paths, and MCP server and tool names, on events |
+| `OTEL_LOG_TOOL_CONTENT=1` | Tool output, as an event on trace spans. Requires traces. |
-For Cowork, content is set on each device with the `otlpContentCapture` key of the [Claude Desktop managed configuration](https://claude.com/docs/cowork/monitoring#content-capture).
+Claude's answers, and tool arguments and results on trace spans, need Claude Code's detailed beta tracing, which is configured separately. See [Claude Code monitoring](https://code.claude.com/docs/en/monitoring-usage).
+
+For Cowork, content is set on each device with the `otlpContentCapture` key of the [Claude Desktop managed configuration](https://claude.com/docs/cowork/monitoring#content-capture). To make sure every device sends metadata only, set it to `[]`.
Even with metadata only, events carry `user.email` when developers sign in with a Claude account, and Cowork events carry the paths of the folders a user connects. Content you turn on is stored in KloudMate under your workspace's retention settings.
## What you get in KloudMate
-| Signal | Examples |
-| --- | --- |
-| Metrics | `claude_code.session.count`, `claude_code.token.usage` by `type` and `model`, `claude_code.cost.usage` in USD, `claude_code.lines_of_code.count`, `claude_code.commit.count`, `claude_code.pull_request.count`, `claude_code.active_time.total` |
-| Events, as logs | `claude_code.user_prompt`, `claude_code.api_request`, `claude_code.api_error`, `claude_code.tool_result`, `claude_code.tool_decision` |
-| Traces | `claude_code.interaction`, `claude_code.llm_request`, `claude_code.tool` |
-
-Metrics and events carry `user.account_uuid`, `user.email`, `organization.id`, and `session.id`, so you can break cost and token usage down by developer. Claude Code sends metrics with delta temporality every 60 seconds, which KloudMate reads as-is. Cost values are estimates. For billing, use the Claude Console.
-
-For every metric, event, and attribute, see [Claude Code monitoring](https://code.claude.com/docs/en/monitoring-usage) and [Cowork monitoring](https://claude.com/docs/cowork/monitoring).
+Metrics and events carry the developer's account, so you can break cost and token usage down by developer in **Explore** or a dashboard. Cost values are estimates. For billing, use your billing dashboard. For every metric, event, and attribute, see [Claude Code monitoring](https://code.claude.com/docs/en/monitoring-usage) and [Cowork monitoring](https://claude.com/docs/cowork/monitoring).
## Troubleshooting
@@ -163,4 +157,4 @@ For every metric, event, and attribute, see [Claude Code monitoring](https://cod
| Export errors with status 401 | `OTEL_EXPORTER_OTLP_HEADERS` doesn't hold a valid KloudMate API key, or the value has a `Bearer` prefix. |
| Managed settings don't apply | Run `claude doctor` and read the `Managed settings (remote)` line. Exporter changes apply only when Claude Code restarts. |
| No data from the Claude Desktop **Code** tab | Put the variables in `~/.claude/settings.json` or managed settings, not in your shell profile. Filter KloudMate by the service `claude-code-desktop`, not `claude-code`. |
-| No Cowork events | Start a new Cowork session after you save, and check that Claude Desktop is version 1.1.4173 or later. |
+| No Cowork events | Start a new Cowork session after you save, and check that Claude Desktop is version 1.1.4173 or later, or 1.22209.3 or later for cloud sessions. |
diff --git a/src/content/docs/guides/ai-observability/crewai.mdx b/src/content/docs/guides/ai-observability/crewai.mdx
new file mode 100644
index 00000000..41b32de1
--- /dev/null
+++ b/src/content/docs/guides/ai-observability/crewai.mdx
@@ -0,0 +1,84 @@
+---
+title: "Send CrewAI Traces to KloudMate"
+description: "Trace CrewAI crews and agents with OpenLLMetry and send model calls, tokens, and cost to KloudMate AI Observability."
+sidebar:
+ label: "CrewAI"
+ order: 3
+---
+
+CrewAI doesn't export OpenTelemetry traces to your own backend. OpenLLMetry, from Traceloop, adds them: one `Traceloop.init()` call instruments CrewAI and the model SDKs it uses. Send those spans to KloudMate to see each crew run in [AI Observability](/ai-observability/).
+
+## Prerequisites
+
+- A KloudMate API key, from **Settings → Ingest Keys**.
+- Python 3.11 or later.
+
+## Install the packages
+
+```bash
+pip install crewai traceloop-sdk
+```
+
+## Initialize OpenLLMetry
+
+Call `Traceloop.init()` at startup, before you import CrewAI:
+
+```python
+import os
+
+from traceloop.sdk import Traceloop
+
+Traceloop.init(
+ app_name="research-crew",
+ api_endpoint="https://otel.kloudmate.com:4318",
+ headers={"Authorization": os.environ["KLOUDMATE_API_KEY"]},
+)
+
+from crewai import Agent, Crew, Task # noqa: E402
+```
+
+Use the base URL without `/v1/traces`, because the SDK appends it. KloudMate expects the raw API key in the `Authorization` header, with no `Bearer` prefix. If the [KloudMate agent](/kloudmate-agent/) runs on the same machine, set `api_endpoint` to `http://localhost:4318` and leave out `headers`.
+
+You can set the endpoint and header with environment variables instead. These take priority over the values passed to `init()`:
+
+```bash
+export TRACELOOP_BASE_URL="https://otel.kloudmate.com:4318"
+export TRACELOOP_HEADERS="Authorization=YOUR_KLOUDMATE_API_KEY"
+```
+
+## Turn off CrewAI's own tracing
+
+CrewAI has built-in telemetry and tracing that send data to CrewAI, and on the first run it can stop and ask whether to share the trace. Neither reaches KloudMate. Turn them off so only OpenLLMetry spans are sent and the run doesn't wait for an answer:
+
+```bash
+export CREWAI_DISABLE_TELEMETRY="true"
+export CREWAI_TRACING_ENABLED="false"
+```
+
+## Group a conversation
+
+Set a `session_id` association property before each run. KloudMate uses it as the conversation ID:
+
+```python
+Traceloop.set_association_properties({"session_id": "chat-42", "user_id": "user-17"})
+result = crew.kickoff(inputs={"topic": topic})
+```
+
+## Control what's captured
+
+OpenLLMetry records prompts and responses by default. To leave them out, set `TRACELOOP_TRACE_CONTENT=false`. Token counts, models, and latency are still recorded.
+
+## Known limitations
+
+- CrewAI doesn't create spans for its tools, including tools you define with `@tool`, so those calls appear only inside the model's messages, not on the **Tools** tab. Tool calls through an MCP server are traced.
+- Calls that CrewAI sends to OpenRouter carry a bare model name without the provider, so they can't be priced and count as **calls not priced**.
+
+## Check the data
+
+Run the crew once, then open **AI Observability → Traces**. Each crew run appears with its model calls, tokens, and estimated cost, and each CrewAI agent appears in the **Agents** table on the Overview tab.
+
+## Related resources
+
+- [OpenLLMetry with KloudMate](https://www.traceloop.com/docs/openllmetry/integrations/kloudmate)
+- [OpenLLMetry configuration](https://www.traceloop.com/docs/openllmetry/configuration)
+- [Instrument a Python LLM App with OpenLLMetry](../instrument-python-app-with-openllmetry/)
diff --git a/src/content/docs/guides/ai-observability/google-adk.mdx b/src/content/docs/guides/ai-observability/google-adk.mdx
new file mode 100644
index 00000000..375839b8
--- /dev/null
+++ b/src/content/docs/guides/ai-observability/google-adk.mdx
@@ -0,0 +1,76 @@
+---
+title: "Send Google ADK Traces to KloudMate"
+description: "Export Google Agent Development Kit (ADK) traces to KloudMate to track agent runs, model calls, tool calls, tokens, and cost in AI Observability."
+sidebar:
+ label: "Google ADK"
+ order: 4
+---
+
+The Agent Development Kit (ADK) has OpenTelemetry tracing built in. It creates a span for each agent run, model call, and tool call, following the GenAI semantic conventions. Set the standard exporter variables and ADK sends them to KloudMate, where every run appears in [AI Observability](/ai-observability/).
+
+## Prerequisites
+
+- A KloudMate API key, from **Settings → Ingest Keys**.
+- ADK for Python 2.13 or later.
+
+## Install the packages
+
+Install ADK with the `otel` extra, which adds the OTLP exporter:
+
+```bash
+pip install "google-adk[otel]"
+```
+
+Without the extra, ADK logs a warning and doesn't export anything.
+
+## Point ADK at KloudMate
+
+Set these variables in the environment that runs your agent:
+
+```bash
+export OTEL_SERVICE_NAME="support-agent"
+export OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"
+export OTEL_EXPORTER_OTLP_ENDPOINT="https://otel.kloudmate.com:4318"
+export OTEL_EXPORTER_OTLP_HEADERS="Authorization=YOUR_KLOUDMATE_API_KEY"
+```
+
+KloudMate expects the raw API key in the `Authorization` header, with no `Bearer` prefix. If the [KloudMate agent](/kloudmate-agent/) runs on the same machine, set the endpoint to `http://localhost:4318` and leave out the header. With `OTEL_EXPORTER_OTLP_ENDPOINT` set, ADK also exports its metrics and logs to the same place.
+
+## Start exporting
+
+For `adk web` and `adk api_server`, set the variables and start the server as usual. In your own code, call `maybe_set_otel_providers()` once, before you create a runner:
+
+```python
+from google.adk.telemetry.setup import maybe_set_otel_providers
+
+maybe_set_otel_providers()
+```
+
+## Capture prompts and responses
+
+By default, ADK doesn't put prompts and responses in the GenAI attributes that AI Observability reads. To add them, set:
+
+```bash
+export OTEL_SEMCONV_STABILITY_OPT_IN="gen_ai_latest_experimental"
+export OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT="SPAN_ONLY"
+```
+
+Use `SPAN_ONLY`, not `true`. In ADK, `true` writes content to log events only, which AI Observability doesn't read.
+
+## Group a conversation
+
+ADK uses the session ID as the conversation ID, so every turn in one ADK session is grouped into the same conversation. You don't need to set anything.
+
+## Known limitations
+
+- When you use a non-Gemini model through LiteLLM, the provider comes from the model name prefix, such as `openrouter/` or `anthropic/`. Some LiteLLM providers report reasoning tokens twice, which inflates output tokens and cost.
+
+## Check the data
+
+Run the agent once, then open **AI Observability → Traces**. The run appears as a turn named after the root agent, with its model, steps, tokens, and estimated cost.
+
+## Related resources
+
+- [ADK traces](https://adk.dev/observability/traces/)
+- [ADK logging](https://adk.dev/observability/logging/)
+- [Use AI Observability](/ai-observability/ai-observability-interface/)
diff --git a/src/content/docs/guides/llm/instrument-python-app-with-openllmetry.mdx b/src/content/docs/guides/ai-observability/instrument-python-app-with-openllmetry.mdx
similarity index 70%
rename from src/content/docs/guides/llm/instrument-python-app-with-openllmetry.mdx
rename to src/content/docs/guides/ai-observability/instrument-python-app-with-openllmetry.mdx
index 6c437550..cdfaf868 100644
--- a/src/content/docs/guides/llm/instrument-python-app-with-openllmetry.mdx
+++ b/src/content/docs/guides/ai-observability/instrument-python-app-with-openllmetry.mdx
@@ -2,18 +2,19 @@
title: "Instrument a Python LLM App with OpenLLMetry"
description: "Trace a LangChain agent end to end with OpenLLMetry and send prompts, tool calls, token usage, and latency to KloudMate."
sidebar:
- order: 1
+ label: "OpenLLMetry tutorial (Python)"
+ order: 20
---
This guide walks you through instrumenting a real LLM application (a small LangChain agent that calls tools) and viewing its traces in KloudMate. By the end you'll see each agent run as a single trace: the model's reasoning, every tool call, token usage per call, and where the time went.
-You'll build a customer-support agent for an online store. The agent answers a question by deciding which tools to call (look up an order, check a return policy), then writing a reply. That back-and-forth is exactly the kind of multi-step flow that's hard to debug from logs alone, and easy to read as a trace.
+You'll build a customer-support agent for an online store. The agent answers a question by deciding which tools to call (look up an order, check a return policy), then writing a reply.
-OpenLLMetry does the instrumentation. It's an OpenTelemetry-native SDK from Traceloop that auto-instruments LangChain and the underlying model calls, so you add a few lines of setup and change nothing in the agent itself. For the concepts behind it, see [Introduction to OpenLLMetry](/llm-observability/what-is-openllmetry/).
+OpenLLMetry does the instrumentation. It's an OpenTelemetry-native SDK from Traceloop that auto-instruments LangChain and the underlying model calls, so you add a few lines of setup and change nothing in the agent itself.
## Prerequisites
-- Python 3.9 or later.
+- Python 3.10 or later.
- An OpenAI API key ([where to find it](https://help.openai.com/en/articles/4936850-where-do-i-find-my-openai-api-key)).
- A KloudMate workspace API key, from **Settings → Ingest Keys**.
@@ -59,7 +60,7 @@ KloudMate authenticates with your API key in the `Authorization` header: the raw
## Step 3: Build the agent
-Create `app.py`. The agent has two tools backed by in-memory data so the example runs without a database. In a real app these would be API or database calls, but the tool *shape* is what matters, because that's what shows up in the trace.
+Create `app.py`. The agent has two tools backed by in-memory data so the example runs without a database. In a real app these would be API or database calls.
```python
import os
@@ -73,8 +74,7 @@ Traceloop.init(
disable_batch=True,
)
-from langchain.agents import AgentExecutor, create_tool_calling_agent
-from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
+from langchain.agents import create_agent
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI
@@ -108,24 +108,15 @@ SYSTEM_PROMPT = (
"details or refund amounts. Give the customer a short, helpful answer."
)
-def build_agent() -> AgentExecutor:
+def build_agent():
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
- prompt = ChatPromptTemplate.from_messages(
- [
- ("system", SYSTEM_PROMPT),
- ("human", "{input}"),
- MessagesPlaceholder("agent_scratchpad"),
- ]
- )
- agent = create_tool_calling_agent(llm, TOOLS, prompt)
- return AgentExecutor(agent=agent, tools=TOOLS, verbose=False)
+ return create_agent(llm, tools=TOOLS, system_prompt=SYSTEM_PROMPT, name="support_agent")
if __name__ == "__main__":
agent = build_agent()
- result = agent.invoke(
- {"input": "I'd like to return order #4412. What's the policy and how much would I get back?"}
- )
- print(result["output"])
+ question = "I'd like to return order #4412. What's the policy and how much would I get back?"
+ result = agent.invoke({"messages": [{"role": "user", "content": question}]})
+ print(result["messages"][-1].content)
```
## Step 4: Run it
@@ -134,15 +125,15 @@ if __name__ == "__main__":
python3 app.py
```
-The agent answers the question, and OpenLLMetry exports the trace to KloudMate. Behind that one answer, the agent made two model calls and two tool calls, all captured as a single trace.
+The agent answers the question, and OpenLLMetry exports the trace to KloudMate. Behind that one answer, the agent made several model calls and two tool calls, all captured as a single trace.
## Step 5: View the trace in KloudMate
Open [KloudMate](https://app.kloudmate.com), go to **Traces**, and filter by the service `support-agent`. Open the most recent trace.
-You'll see the full agent run as a waterfall: the `AgentExecutor` at the top, the model calls (`ChatOpenAI.chat`), and the tool calls (`get_order_status`, `get_return_policy`) nested underneath with their durations. Select any model span to read the exact prompt and response, the token usage, and the finish reason.
+You'll see the full agent run as a waterfall: the agent run at the top, with the model calls and the `get_order_status` and `get_return_policy` tool calls nested underneath. Select any model span to read the exact prompt and response, the token usage, and the finish reason.
-For a tour of everything KloudMate surfaces on an agent trace (the conversation transcript, token and cache breakdown, tool calls, and the AI Flow graph), see [Agentic Workflow Observability](/llm-observability/ai-trace-observability/).
+For a tour of everything KloudMate surfaces on an agent trace (the conversation transcript, token and cache breakdown, tool calls, and the AI Flow graph), see [Agent Traces](/ai-observability/agent-traces/). The run also appears in **AI Observability → Traces** as **support_agent Turn**, with its token usage and estimated cost.
## Step 6: Tag traces with a user and session
@@ -152,21 +143,19 @@ Production agents serve many customers across many conversations. Attach a user
Traceloop.set_association_properties(
{"user_id": "cust_1042", "session_id": "sess_8f21"}
)
-result = agent.invoke({"input": "..."})
+result = agent.invoke({"messages": [{"role": "user", "content": question}]})
```
-KloudMate stores these on every span in the run, so you can filter and group traces by `user_id` or `session_id` when you investigate an issue.
+KloudMate stores these on every span in the run, so you can filter and group traces by `user_id` or `session_id` when you investigate an issue. The `session_id` also becomes the conversation ID (`gen_ai.conversation.id`) in AI Observability.
## Step 7: Prepare for production
-A few changes once you move past local testing:
-
- **Batch exports.** Drop `disable_batch=True` so spans export in the background instead of one HTTP request per span.
-- **Decide what to capture.** OpenLLMetry records prompt and completion content by default, which is what makes the conversation view useful. If your prompts carry sensitive data, set `TRACELOOP_TRACE_CONTENT=false` to record metadata (tokens, model, latency) without the message bodies.
+- **Decide what to capture.** OpenLLMetry records prompt and completion content by default. If your prompts carry sensitive data, set `TRACELOOP_TRACE_CONTENT=false` to record metadata (tokens, model, latency) without the message bodies.
- **Keep init first.** Call `Traceloop.init()` before the rest of your app imports run, so every model and tool call is instrumented.
## Related resources
-- [Agentic Workflow Observability](/llm-observability/ai-trace-observability/), what to look for once your traces land in KloudMate
-- [Introduction to OpenLLMetry](/llm-observability/what-is-openllmetry/)
-- [Instrument a Python App](/guides/opentelemetry/python/), general OpenTelemetry instrumentation
+- [Agent Traces](/ai-observability/agent-traces/)
+- [AI Observability](/ai-observability/)
+- [Instrument a Python App](/guides/opentelemetry/python/)
diff --git a/src/content/docs/guides/ai-observability/langchain-langgraph.mdx b/src/content/docs/guides/ai-observability/langchain-langgraph.mdx
new file mode 100644
index 00000000..4a3da23f
--- /dev/null
+++ b/src/content/docs/guides/ai-observability/langchain-langgraph.mdx
@@ -0,0 +1,122 @@
+---
+title: "Send LangChain and LangGraph Traces to KloudMate"
+description: "Trace LangChain and LangGraph agents in Python with OpenInference and send model calls, tool calls, tokens, and cost to KloudMate AI Observability."
+sidebar:
+ label: "LangChain and LangGraph"
+ order: 5
+---
+
+import { Tabs, TabItem } from '@astrojs/starlight/components';
+
+LangChain and LangGraph don't export OpenTelemetry traces on their own. The OpenInference instrumentation for LangChain adds them: it creates a span for each agent run, model call, and tool call, with token counts and messages. Send those spans to KloudMate to see each run in [AI Observability](/ai-observability/). The same instrumentation covers LangGraph, because LangGraph runs on LangChain.
+
+## Prerequisites
+
+- A KloudMate API key, from **Settings → Ingest Keys**.
+- Python 3.11 or later.
+
+## Instrument your app
+
+You can run your app under `opentelemetry-instrument` without changing its code, or set up the instrumentation in code.
+
+
+
+
+Install the instrumentation and the OpenTelemetry launcher:
+
+```bash
+pip install openinference-instrumentation-langchain opentelemetry-distro opentelemetry-exporter-otlp-proto-http
+```
+
+Point the exporter at KloudMate and start your app with `opentelemetry-instrument`, which loads the LangChain instrumentation automatically:
+
+```bash
+export OTEL_SERVICE_NAME="support-agent"
+export OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"
+export OTEL_EXPORTER_OTLP_ENDPOINT="https://otel.kloudmate.com:4318"
+export OTEL_EXPORTER_OTLP_HEADERS="Authorization=YOUR_KLOUDMATE_API_KEY"
+
+opentelemetry-instrument python app.py
+```
+
+
+
+
+Install the instrumentation and the OpenTelemetry SDK:
+
+```bash
+pip install openinference-instrumentation-langchain opentelemetry-sdk opentelemetry-exporter-otlp-proto-http
+```
+
+Set up a tracer provider and instrument LangChain before you build your chain or graph:
+
+```python
+from openinference.instrumentation.langchain import LangChainInstrumentor
+from opentelemetry import trace
+from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
+from opentelemetry.sdk.trace import TracerProvider
+from opentelemetry.sdk.trace.export import BatchSpanProcessor
+
+provider = TracerProvider()
+provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter()))
+trace.set_tracer_provider(provider)
+
+LangChainInstrumentor().instrument(tracer_provider=provider)
+```
+
+`OTLPSpanExporter()` reads the endpoint and header from the same `OTEL_EXPORTER_OTLP_*` variables shown in the other tab. Call `provider.shutdown()` before a script exits, so the last spans are sent.
+
+
+
+
+KloudMate expects the raw API key in the `Authorization` header, with no `Bearer` prefix. If the [KloudMate agent](/kloudmate-agent/) runs on the same machine, set the endpoint to `http://localhost:4318` and leave out the header.
+
+## Name your agent
+
+OpenInference marks a span as an agent only when its name contains "agent". Give your graph a name such as `support_agent` so each run appears as a turn of that agent:
+
+```python
+from langchain.agents import create_agent
+
+graph = create_agent(model, tools, name="support_agent")
+```
+
+A graph left with the default name `LangGraph` has no agent span, so its runs are named after the model instead.
+
+## Group a conversation
+
+Wrap each call in `using_session()` with an ID that stays the same for the whole conversation, such as your chat or thread ID:
+
+```python
+from openinference.instrumentation import using_session
+
+with using_session("chat-42"):
+ graph.invoke({"messages": [("user", question)]})
+```
+
+To attach a user ID as well, use `using_attributes(session_id="chat-42", user_id="user-17")`.
+
+## Control what's captured
+
+OpenInference records prompts, responses, and tool arguments by default. To leave them out, set these variables before your app starts:
+
+```bash
+export OPENINFERENCE_HIDE_INPUTS="true"
+export OPENINFERENCE_HIDE_OUTPUTS="true"
+```
+
+Hidden values are replaced with `__REDACTED__`. Token counts, models, and latency are still recorded.
+
+## Avoid double counting
+
+Instrument only one layer. If you also install the OpenAI or Anthropic SDK instrumentation, each model call is recorded twice, once by LangChain and once by the provider SDK, and AI Observability counts both.
+
+## Check the data
+
+Run the agent once, then open **AI Observability → Traces**. The run appears as **support_agent Turn** with its model, steps, tokens, and estimated cost. OpenInference doesn't report cache writes, so the estimated cost can be slightly lower than your bill when prompt caching is on.
+
+## Related resources
+
+- [OpenInference LangChain instrumentation](https://github.com/Arize-ai/openinference/tree/main/python/instrumentation/openinference-instrumentation-langchain)
+- [OpenInference configuration](https://github.com/Arize-ai/openinference/blob/main/spec/configuration.md)
+- [Instrument a Python LLM App with OpenLLMetry](../instrument-python-app-with-openllmetry/)
diff --git a/src/content/docs/guides/ai-observability/llamaindex.mdx b/src/content/docs/guides/ai-observability/llamaindex.mdx
new file mode 100644
index 00000000..ac318c06
--- /dev/null
+++ b/src/content/docs/guides/ai-observability/llamaindex.mdx
@@ -0,0 +1,104 @@
+---
+title: "Send LlamaIndex Traces to KloudMate"
+description: "Trace LlamaIndex agents and query engines in Python with OpenInference and send model calls, tool calls, tokens, and cost to KloudMate AI Observability."
+sidebar:
+ label: "LlamaIndex"
+ order: 6
+---
+
+import { Tabs, TabItem } from '@astrojs/starlight/components';
+
+The OpenInference instrumentation for LlamaIndex creates a span for each agent run, model call, embedding, and tool call. Send those spans to KloudMate to see each run in [AI Observability](/ai-observability/).
+
+Use OpenInference rather than LlamaIndex's own `llama-index-observability-otel` package. Its spans don't use the GenAI attribute names, so model calls and tokens don't appear in AI Observability.
+
+## Prerequisites
+
+- A KloudMate API key, from **Settings → Ingest Keys**.
+- Python 3.10 or later.
+
+## Instrument your app
+
+
+
+
+Install the instrumentation and the OpenTelemetry launcher:
+
+```bash
+pip install openinference-instrumentation-llama-index opentelemetry-distro opentelemetry-exporter-otlp-proto-http
+```
+
+Point the exporter at KloudMate and start your app with `opentelemetry-instrument`, which loads the LlamaIndex instrumentation automatically:
+
+```bash
+export OTEL_SERVICE_NAME="rag-service"
+export OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"
+export OTEL_EXPORTER_OTLP_ENDPOINT="https://otel.kloudmate.com:4318"
+export OTEL_EXPORTER_OTLP_HEADERS="Authorization=YOUR_KLOUDMATE_API_KEY"
+
+opentelemetry-instrument python app.py
+```
+
+
+
+
+Install the instrumentation and the OpenTelemetry SDK:
+
+```bash
+pip install openinference-instrumentation-llama-index opentelemetry-sdk opentelemetry-exporter-otlp-proto-http
+```
+
+Set up a tracer provider and instrument LlamaIndex at startup:
+
+```python
+from openinference.instrumentation.llama_index import LlamaIndexInstrumentor
+from opentelemetry import trace
+from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
+from opentelemetry.sdk.trace import TracerProvider
+from opentelemetry.sdk.trace.export import BatchSpanProcessor
+
+provider = TracerProvider()
+provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter()))
+trace.set_tracer_provider(provider)
+
+LlamaIndexInstrumentor().instrument(tracer_provider=provider)
+```
+
+`OTLPSpanExporter()` reads the endpoint and header from the same `OTEL_EXPORTER_OTLP_*` variables shown in the other tab. Call `provider.shutdown()` before a script exits, so the last spans are sent.
+
+
+
+
+KloudMate expects the raw API key in the `Authorization` header, with no `Bearer` prefix. If the [KloudMate agent](/kloudmate-agent/) runs on the same machine, set the endpoint to `http://localhost:4318` and leave out the header.
+
+## Group a conversation
+
+Wrap each call in `using_session()` with an ID that stays the same for the whole conversation:
+
+```python
+from openinference.instrumentation import using_session
+
+with using_session("chat-42"):
+ response = await agent.run(question)
+```
+
+## Control what's captured
+
+OpenInference records prompts, responses, and retrieved documents by default. To leave out inputs and outputs, set these variables before your app starts:
+
+```bash
+export OPENINFERENCE_HIDE_INPUTS="true"
+export OPENINFERENCE_HIDE_OUTPUTS="true"
+```
+
+Token counts, models, and latency are still recorded. The [OpenInference configuration](https://github.com/Arize-ai/openinference/blob/main/spec/configuration.md) lists the finer-grained settings, such as `OPENINFERENCE_HIDE_RETRIEVAL_DOCUMENTS`.
+
+## Check the data
+
+Run a query once, then open **AI Observability → Traces**. The run appears with its model calls, tokens, and estimated cost.
+
+## Related resources
+
+- [OpenInference LlamaIndex instrumentation](https://github.com/Arize-ai/openinference/tree/main/python/instrumentation/openinference-instrumentation-llama-index)
+- [LlamaIndex observability](https://developers.llamaindex.ai/python/framework/module_guides/observability/)
+- [Use AI Observability](/ai-observability/ai-observability-interface/)
diff --git a/src/content/docs/guides/ai-observability/mastra.mdx b/src/content/docs/guides/ai-observability/mastra.mdx
new file mode 100644
index 00000000..71a0a71d
--- /dev/null
+++ b/src/content/docs/guides/ai-observability/mastra.mdx
@@ -0,0 +1,90 @@
+---
+title: "Send Mastra Traces to KloudMate"
+description: "Export OpenTelemetry traces from Mastra agents and workflows to KloudMate to track model calls, tool calls, tokens, and cost in AI Observability."
+sidebar:
+ label: "Mastra"
+ order: 7
+---
+
+Mastra traces its own agent runs, model calls, tool calls, and workflows. Add its OpenTelemetry exporter and point it at KloudMate to see every run in [AI Observability](/ai-observability/) with its model, tokens, estimated cost, and errors.
+
+## Prerequisites
+
+- A KloudMate API key, from **Settings → Ingest Keys**.
+- Mastra 1.x (`@mastra/core`) on Node.js 22.13 or later.
+
+## Install the packages
+
+```bash
+npm install @mastra/observability @mastra/otel-exporter @opentelemetry/exporter-trace-otlp-proto@^0.218.0
+```
+
+## Add the exporter
+
+Add an `Observability` config with an `OtelExporter` to your `Mastra` instance:
+
+```typescript
+import { Mastra } from '@mastra/core';
+import { Observability } from '@mastra/observability';
+import { OtelExporter } from '@mastra/otel-exporter';
+
+export const mastra = new Mastra({
+ agents: { supportAgent },
+ observability: new Observability({
+ configs: {
+ otel: {
+ serviceName: 'support-agent',
+ exporters: [
+ new OtelExporter({
+ provider: {
+ custom: {
+ endpoint: 'https://otel.kloudmate.com:4318/v1/traces',
+ protocol: 'http/protobuf',
+ headers: { Authorization: process.env.KLOUDMATE_API_KEY! },
+ },
+ },
+ }),
+ ],
+ },
+ },
+ }),
+});
+```
+
+The Mastra exporter doesn't read the `OTEL_EXPORTER_OTLP_*` variables, so the endpoint and header go in the config. Use the full traces URL, ending in `/v1/traces`. KloudMate expects the raw API key in the `Authorization` header, with no `Bearer` prefix.
+
+If the [KloudMate agent](/kloudmate-agent/) runs on the same machine, set the endpoint to `http://localhost:4318/v1/traces` and leave out `headers`.
+
+In a short-lived script or a serverless function, call `await mastra.observability.flush()` before the process exits, so the last spans are sent.
+
+## Group a conversation
+
+Pass a memory thread when you call the agent. KloudMate uses the thread ID as the conversation ID, so the turns of one conversation are grouped together:
+
+```typescript
+const result = await mastra.getAgent('supportAgent').generate(message, {
+ memory: { thread: 'chat-42', resource: 'user-17' },
+});
+```
+
+`stream()` takes the same `memory` option. Configure a `Memory` on the agent if you also want Mastra to keep the conversation history.
+
+## Control what's captured
+
+Mastra records prompts and responses by default, and its sensitive-data filter redacts fields such as passwords, tokens, and API keys before export. To leave the input or output out of a single call, pass `tracingOptions`:
+
+```typescript
+await agent.generate(message, {
+ tracingOptions: { hideInput: true, hideOutput: true },
+});
+```
+
+## Check the data
+
+Run the agent once, then open **AI Observability → Traces**. The run appears as a turn named after the agent, with its model, steps, tokens, and estimated cost.
+
+## Related resources
+
+- [Mastra OpenTelemetry exporter](https://mastra.ai/integrations/observability/opentelemetry)
+- [Use AI Observability](/ai-observability/ai-observability-interface/)
+- [Send GenAI traces](/ai-observability/send-genai-traces/)
diff --git a/src/content/docs/guides/ai-observability/openai/_meta.json b/src/content/docs/guides/ai-observability/openai/_meta.json
new file mode 100644
index 00000000..c055e6e8
--- /dev/null
+++ b/src/content/docs/guides/ai-observability/openai/_meta.json
@@ -0,0 +1,4 @@
+{
+ "label": "OpenAI",
+ "order": 2
+}
diff --git a/src/content/docs/guides/ai-observability/openai/agents-sdk.mdx b/src/content/docs/guides/ai-observability/openai/agents-sdk.mdx
new file mode 100644
index 00000000..8c1360cf
--- /dev/null
+++ b/src/content/docs/guides/ai-observability/openai/agents-sdk.mdx
@@ -0,0 +1,91 @@
+---
+title: "Send OpenAI Agents SDK Traces to KloudMate"
+description: "Trace OpenAI Agents SDK runs in Python with OpenInference and send agent runs, model calls, tool calls, tokens, and cost to KloudMate AI Observability."
+sidebar:
+ label: "OpenAI Agents SDK"
+ order: 2
+---
+
+The OpenAI Agents SDK traces every run, but by default it sends those traces to the OpenAI dashboard. The OpenInference instrumentation for the Agents SDK turns them into OpenTelemetry spans instead, so you can send them to KloudMate and see each run in [AI Observability](/ai-observability/).
+
+## Prerequisites
+
+- A KloudMate API key, from **Settings → Ingest Keys**.
+- Python 3.10 or later.
+
+## Install the packages
+
+```bash
+pip install openai-agents openinference-instrumentation-openai-agents opentelemetry-sdk opentelemetry-exporter-otlp-proto-http
+```
+
+## Instrument the SDK
+
+Set up a tracer provider and instrument the Agents SDK before you run an agent:
+
+```python
+from openinference.instrumentation.openai_agents import OpenAIAgentsInstrumentor
+from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
+from opentelemetry.sdk.trace import TracerProvider
+from opentelemetry.sdk.trace.export import BatchSpanProcessor
+from opentelemetry.trace import set_tracer_provider
+
+tracer_provider = TracerProvider()
+tracer_provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter()))
+set_tracer_provider(tracer_provider)
+
+OpenAIAgentsInstrumentor().instrument(tracer_provider=tracer_provider)
+```
+
+Call `tracer_provider.shutdown()` before a script exits, so the last spans are sent.
+
+By default, the instrumentation replaces the SDK's own trace export, so traces stop going to the OpenAI dashboard. To keep sending to both, pass `exclusive_processor=False` to `instrument()`.
+
+:::caution
+Don't turn tracing off with `set_tracing_disabled(True)`, `OPENAI_AGENTS_DISABLE_TRACING`, or `RunConfig(tracing_disabled=True)`. Those stop the SDK from producing traces at all, so nothing reaches KloudMate either.
+:::
+
+## Point the exporter at KloudMate
+
+`OTLPSpanExporter()` reads the standard OpenTelemetry variables. Set them in the environment that runs your app:
+
+```bash
+export OTEL_SERVICE_NAME="support-agent"
+export OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"
+export OTEL_EXPORTER_OTLP_ENDPOINT="https://otel.kloudmate.com:4318"
+export OTEL_EXPORTER_OTLP_HEADERS="Authorization=YOUR_KLOUDMATE_API_KEY"
+```
+
+KloudMate expects the raw API key in the `Authorization` header, with no `Bearer` prefix. If the [KloudMate agent](/kloudmate-agent/) runs on the same machine, set the endpoint to `http://localhost:4318` and leave out the header.
+
+## Group a conversation
+
+The Agents SDK's `group_id` isn't exported to OpenTelemetry. Wrap each run in `using_session()` instead:
+
+```python
+from openinference.instrumentation import using_session
+
+with using_session("chat-42"):
+ result = await Runner.run(agent, question)
+```
+
+## Control what's captured
+
+Prompts, responses, and tool arguments are recorded by default. To leave them out, set these variables before your app starts:
+
+```bash
+export OPENINFERENCE_HIDE_INPUTS="true"
+export OPENINFERENCE_HIDE_OUTPUTS="true"
+```
+
+Token counts, models, and latency are still recorded.
+
+## Check the data
+
+Run the agent once, then open **AI Observability → Traces**. The run appears as **Agent workflow Turn**, or under the name you set with `RunConfig(workflow_name=...)`, with its model, steps, tokens, and estimated cost.
+
+## Related resources
+
+- [OpenInference Agents SDK instrumentation](https://pypi.org/project/openinference-instrumentation-openai-agents/)
+- [Agents SDK tracing](https://openai.github.io/openai-agents-python/tracing/)
+- [Use AI Observability](/ai-observability/ai-observability-interface/)
diff --git a/src/content/docs/guides/ai-observability/openai/codex.mdx b/src/content/docs/guides/ai-observability/openai/codex.mdx
new file mode 100644
index 00000000..e97f4b6f
--- /dev/null
+++ b/src/content/docs/guides/ai-observability/openai/codex.mdx
@@ -0,0 +1,50 @@
+---
+title: "Send OpenAI Codex Telemetry to KloudMate"
+description: "Export traces and events from the OpenAI Codex CLI and IDE extension to KloudMate to track turns, model calls, tool calls, tokens, and cost in AI Observability."
+sidebar:
+ label: "OpenAI Codex"
+ order: 3
+---
+
+Codex can export OpenTelemetry traces and log events for every session. Add exporters to your Codex config and point them at KloudMate to see each Codex turn in [AI Observability](/ai-observability/) with its model calls, tool calls, tokens, and estimated cost.
+
+## Prerequisites
+
+- A KloudMate API key, from **Settings → Ingest Keys**.
+- The Codex CLI or IDE extension, configured through `~/.codex/config.toml`.
+
+## Add the exporters
+
+Add an `[otel]` section to `~/.codex/config.toml`. Codex ignores `[otel]` in a project's `.codex/config.toml`, so it must go in the file in your home directory.
+
+```toml
+[otel]
+environment = "prod"
+trace_exporter = { otlp-http = { endpoint = "https://otel.kloudmate.com:4318/v1/traces", protocol = "binary", headers = { "Authorization" = "YOUR_KLOUDMATE_API_KEY" } } }
+exporter = { otlp-http = { endpoint = "https://otel.kloudmate.com:4318/v1/logs", protocol = "binary", headers = { "Authorization" = "YOUR_KLOUDMATE_API_KEY" } } }
+```
+
+- `trace_exporter` sends the traces that AI Observability reads. It's off unless you set it.
+- `exporter` sends Codex's log events, such as API requests and tool decisions, to KloudMate **Logs**. It's optional.
+- Each endpoint takes the full path for its signal (`/v1/traces` or `/v1/logs`), and `protocol` is required.
+
+KloudMate expects the raw API key in the `Authorization` header, with no `Bearer` prefix. Restart Codex after you edit the file.
+
+## Prompts and responses
+
+Codex writes prompts and responses to log events only, not to spans, so the conversation view in AI Observability stays empty for Codex runs. User prompts are redacted even in the log events unless you add `log_user_prompt = true` to the `[otel]` section.
+
+## Known limitations
+
+- Codex doesn't set a status on its spans, so a request that fails without a response isn't counted as an error, and its turn shows as completed.
+- Codex sends its spans in several batches. Latency for the last model call in a turn can read low, and that call can be missing its provider.
+
+## Check the data
+
+Run one prompt in Codex, then open **AI Observability → Traces**. The turn appears as **Codex Turn**, grouped with the rest of its session, with its model, steps, tokens, and estimated cost.
+
+## Related resources
+
+- [Codex advanced configuration](https://learn.chatgpt.com/docs/config-file/config-advanced)
+- [Send Claude Code telemetry](../../anthropic/send-claude-code-telemetry/)
+- [Use AI Observability](/ai-observability/ai-observability-interface/)
diff --git a/src/content/docs/guides/ai-observability/openai/openai-sdk.mdx b/src/content/docs/guides/ai-observability/openai/openai-sdk.mdx
new file mode 100644
index 00000000..755d1f40
--- /dev/null
+++ b/src/content/docs/guides/ai-observability/openai/openai-sdk.mdx
@@ -0,0 +1,111 @@
+---
+title: "Send OpenAI SDK Traces to KloudMate"
+description: "Trace OpenAI SDK calls in Python and Node.js with the OpenTelemetry instrumentation and send model calls, tokens, and cost to KloudMate AI Observability."
+sidebar:
+ label: "OpenAI SDK"
+ order: 1
+---
+
+import { Tabs, TabItem } from '@astrojs/starlight/components';
+
+The OpenTelemetry project maintains an instrumentation for the OpenAI SDK that creates a span for each chat completion and embedding request, with the model, token counts, and, if you turn it on, the messages. Send those spans to KloudMate to see each call in [AI Observability](/ai-observability/) with its estimated cost.
+
+Use this guide when your app calls the OpenAI SDK directly. If you build on an agent framework, follow the guide for that framework instead, so each model call is recorded once.
+
+## Prerequisites
+
+- A KloudMate API key, from **Settings → Ingest Keys**.
+- Python 3.10 or later, or Node.js 22.15 or later.
+
+## Instrument your app
+
+
+
+
+Install the instrumentation and the OpenTelemetry launcher:
+
+```bash
+pip install opentelemetry-distro opentelemetry-exporter-otlp-proto-http opentelemetry-instrumentation-openai-v2
+```
+
+Set the exporter and content variables, then start your app with `opentelemetry-instrument`:
+
+```bash
+export OTEL_SERVICE_NAME="support-agent"
+export OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"
+export OTEL_EXPORTER_OTLP_ENDPOINT="https://otel.kloudmate.com:4318"
+export OTEL_EXPORTER_OTLP_HEADERS="Authorization=YOUR_KLOUDMATE_API_KEY"
+export OTEL_SEMCONV_STABILITY_OPT_IN="gen_ai_latest_experimental"
+export OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT="span_only"
+
+opentelemetry-instrument python app.py
+```
+
+Set `OTEL_EXPORTER_OTLP_PROTOCOL` explicitly. The launcher defaults to gRPC, which doesn't work with the HTTP endpoint on port 4318.
+
+`span_only` puts prompts and responses on the spans, where AI Observability reads them. Without `OTEL_SEMCONV_STABILITY_OPT_IN`, the instrumentation follows an older version of the conventions and writes content to log events instead. To leave content out, omit `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT`.
+
+:::caution
+If the app fails to start with an `ImportError` for `opentelemetry.util.genai.instruments`, the installed `opentelemetry-util-genai` is newer than the instrumentation supports. Install `"opentelemetry-util-genai<1.2"` until a compatible instrumentation release is out.
+:::
+
+
+
+
+Install the OpenTelemetry SDK and the OpenAI instrumentation:
+
+```bash
+npm install @opentelemetry/sdk-node @opentelemetry/exporter-trace-otlp-proto @opentelemetry/instrumentation-openai
+```
+
+Create `telemetry.ts`, which starts the SDK before your app imports `openai`:
+
+```typescript
+import { OpenAIInstrumentation } from '@opentelemetry/instrumentation-openai';
+import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto';
+import { NodeSDK } from '@opentelemetry/sdk-node';
+
+const sdk = new NodeSDK({
+ traceExporter: new OTLPTraceExporter(),
+ instrumentations: [new OpenAIInstrumentation()],
+});
+sdk.start();
+```
+
+Load it ahead of your app, for example `node --import ./telemetry.js app.js`, and set the same `OTEL_SERVICE_NAME` and `OTEL_EXPORTER_OTLP_*` variables shown in the Python tab. The Node.js instrumentation also covers the Responses API. It writes message content to log events rather than spans, so the conversation view stays empty for these calls.
+
+
+
+
+KloudMate expects the raw API key in the `Authorization` header, with no `Bearer` prefix. If the [KloudMate agent](/kloudmate-agent/) runs on the same machine, set the endpoint to `http://localhost:4318` and leave out the header.
+
+## Group a conversation
+
+The instrumentation doesn't know which conversation a call belongs to, so each call shows as its own run. To group the calls of one conversation, wrap them in a span that sets `gen_ai.conversation.id`:
+
+```python
+from opentelemetry import trace
+
+tracer = trace.get_tracer("support-agent")
+
+with tracer.start_as_current_span("handle_message") as span:
+ span.set_attribute("gen_ai.conversation.id", chat_id)
+ reply = client.chat.completions.create(model="gpt-4o-mini", messages=messages)
+```
+
+KloudMate copies the ID only to spans that arrive in the same export as the wrapping span. If a request runs longer than the exporter's batch delay, 5 seconds by default, set `OTEL_BSP_SCHEDULE_DELAY` (in milliseconds) above the request's duration.
+
+## Known limitations
+
+- The Python instrumentation covers Chat Completions and Embeddings. Calls to the Responses API aren't traced.
+- The Python instrumentation doesn't report cached input tokens, so the cache hit rate reads zero and the estimated cost doesn't include the cache discount.
+
+## Check the data
+
+Make one request, then open **AI Observability → Traces**. The call appears as **Chat · gpt-4o-mini**, or under your wrapping span's name, with its tokens and estimated cost.
+
+## Related resources
+
+- [OpenAI instrumentation for Python](https://pypi.org/project/opentelemetry-instrumentation-openai-v2/)
+- [OpenAI instrumentation for Node.js](https://github.com/open-telemetry/opentelemetry-js-contrib/tree/main/packages/instrumentation-openai)
+- [OpenAI Agents SDK](../agents-sdk/)
diff --git a/src/content/docs/guides/ai-observability/pydantic-ai.mdx b/src/content/docs/guides/ai-observability/pydantic-ai.mdx
new file mode 100644
index 00000000..2240f848
--- /dev/null
+++ b/src/content/docs/guides/ai-observability/pydantic-ai.mdx
@@ -0,0 +1,87 @@
+---
+title: "Send Pydantic AI Traces to KloudMate"
+description: "Export Pydantic AI's built-in OpenTelemetry traces to KloudMate to track agent runs, model calls, tool calls, tokens, and cost in AI Observability."
+sidebar:
+ label: "Pydantic AI"
+ order: 8
+---
+
+Pydantic AI has OpenTelemetry tracing built in, and its spans already follow the GenAI semantic conventions. Point an OTLP exporter at KloudMate and turn instrumentation on to see every agent run in [AI Observability](/ai-observability/). You don't need a Logfire account.
+
+## Prerequisites
+
+- A KloudMate API key, from **Settings → Ingest Keys**.
+- Pydantic AI 1.89 or later.
+
+## Install the packages
+
+```bash
+pip install pydantic-ai opentelemetry-sdk opentelemetry-exporter-otlp-proto-http
+```
+
+## Turn on instrumentation
+
+Set up a tracer provider with an OTLP exporter, then instrument every agent:
+
+```python
+from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
+from opentelemetry.sdk.trace import TracerProvider
+from opentelemetry.sdk.trace.export import BatchSpanProcessor
+from opentelemetry.trace import set_tracer_provider
+from pydantic_ai import Agent
+
+tracer_provider = TracerProvider()
+tracer_provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter()))
+set_tracer_provider(tracer_provider)
+
+Agent.instrument_all()
+```
+
+Call `tracer_provider.shutdown()` before a script exits, so the last spans are sent.
+
+## Point the exporter at KloudMate
+
+`OTLPSpanExporter()` reads the standard OpenTelemetry variables. Set them in the environment that runs your app:
+
+```bash
+export OTEL_SERVICE_NAME="support-agent"
+export OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"
+export OTEL_EXPORTER_OTLP_ENDPOINT="https://otel.kloudmate.com:4318"
+export OTEL_EXPORTER_OTLP_HEADERS="Authorization=YOUR_KLOUDMATE_API_KEY"
+```
+
+KloudMate expects the raw API key in the `Authorization` header, with no `Bearer` prefix. If the [KloudMate agent](/kloudmate-agent/) runs on the same machine, set the endpoint to `http://localhost:4318` and leave out the header.
+
+:::tip
+If you already use the Logfire SDK, you can send to KloudMate with `logfire.configure(send_to_logfire=False)` followed by `logfire.instrument_pydantic_ai()`, using the same environment variables.
+:::
+
+## Group a conversation
+
+Pydantic AI sends a conversation ID automatically. A new run starts a new conversation, and a run that passes `message_history` from an earlier run continues it. To use your own chat or thread ID instead, pass `conversation_id`:
+
+```python
+result = await agent.run(question, conversation_id="chat-42")
+```
+
+## Control what's captured
+
+Pydantic AI records prompts, responses, and tool arguments by default. To leave them out, pass `InstrumentationSettings`:
+
+```python
+from pydantic_ai import Agent, InstrumentationSettings
+
+Agent.instrument_all(InstrumentationSettings(include_content=False))
+```
+
+Token counts, models, and latency are still recorded. Pydantic AI also reports its own cost estimate on each call it can price, and KloudMate keeps that value instead of calculating one.
+
+## Check the data
+
+Run the agent once, then open **AI Observability → Traces**. The run appears as a turn named after the agent, with its model, steps, tokens, and estimated cost.
+
+## Related resources
+
+- [Pydantic AI and OpenTelemetry](https://pydantic.dev/docs/ai/integrations/logfire/)
+- [Use AI Observability](/ai-observability/ai-observability-interface/)
+- [Send GenAI traces](/ai-observability/send-genai-traces/)
diff --git a/src/content/docs/guides/ai-observability/vercel-ai-sdk.mdx b/src/content/docs/guides/ai-observability/vercel-ai-sdk.mdx
new file mode 100644
index 00000000..74159f8f
--- /dev/null
+++ b/src/content/docs/guides/ai-observability/vercel-ai-sdk.mdx
@@ -0,0 +1,141 @@
+---
+title: "Send Vercel AI SDK Traces to KloudMate"
+description: "Export OpenTelemetry traces from the Vercel AI SDK to KloudMate to track model calls, tool calls, tokens, and cost in AI Observability."
+sidebar:
+ label: "Vercel AI SDK"
+ order: 9
+---
+
+import { Tabs, TabItem } from '@astrojs/starlight/components';
+
+The Vercel AI SDK emits an OpenTelemetry span for each `generateText` or `streamText` call, each model request, and each tool call. Send those spans to KloudMate to see every call in [AI Observability](/ai-observability/) with its model, tokens, estimated cost, and errors.
+
+This guide covers AI SDK 7. Earlier versions are covered in [AI SDK 5 and 6](#ai-sdk-5-and-6).
+
+## Prerequisites
+
+- A KloudMate API key, from **Settings → Ingest Keys**.
+- Node.js 22 or later.
+
+## Install the packages
+
+```bash
+npm install ai @ai-sdk/otel @opentelemetry/api @opentelemetry/sdk-node @opentelemetry/exporter-trace-otlp-proto
+```
+
+## Register the telemetry integration
+
+The AI SDK creates spans through an OpenTelemetry tracer, so start the OpenTelemetry SDK first and then register the AI SDK integration. Both run once, before your first AI call.
+
+
+
+
+Create `telemetry.ts` and import it first in your entry file:
+
+```typescript
+import { OpenTelemetry } from '@ai-sdk/otel';
+import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto';
+import { NodeSDK } from '@opentelemetry/sdk-node';
+import { registerTelemetry } from 'ai';
+
+const sdk = new NodeSDK({ traceExporter: new OTLPTraceExporter() });
+sdk.start();
+
+registerTelemetry(new OpenTelemetry());
+
+export const shutdownTelemetry = () => sdk.shutdown();
+```
+
+Call `await shutdownTelemetry()` before a script exits, so the last spans are sent.
+
+
+
+
+Install `@vercel/otel` and register both in `instrumentation.ts`:
+
+```typescript
+import { OpenTelemetry } from '@ai-sdk/otel';
+import { registerOTel } from '@vercel/otel';
+import { registerTelemetry } from 'ai';
+
+export function register() {
+ registerOTel({ serviceName: 'support-agent' });
+ registerTelemetry(new OpenTelemetry());
+}
+```
+
+
+
+
+## Point the exporter at KloudMate
+
+Set the standard OpenTelemetry variables in the environment that runs your app:
+
+```bash
+export OTEL_SERVICE_NAME="support-agent"
+export OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"
+export OTEL_EXPORTER_OTLP_ENDPOINT="https://otel.kloudmate.com:4318"
+export OTEL_EXPORTER_OTLP_HEADERS="Authorization=YOUR_KLOUDMATE_API_KEY"
+```
+
+KloudMate expects the raw API key in the `Authorization` header, with no `Bearer` prefix. If the [KloudMate agent](/kloudmate-agent/) runs on the same machine, set the endpoint to `http://localhost:4318` and leave out the header.
+
+## Name your calls
+
+Once the integration is registered, every AI SDK call is traced. Give each call a `functionId`, which becomes the agent name in KloudMate:
+
+```typescript
+const result = await generateText({
+ model,
+ prompt,
+ tools,
+ telemetry: { functionId: 'support-agent' },
+});
+```
+
+Prompts and responses are recorded by default. To leave them out, set `recordInputs: false` or `recordOutputs: false` in the same `telemetry` object. To skip tracing for one call, set `isEnabled: false`.
+
+## Group a conversation
+
+The AI SDK doesn't send a conversation ID, so each call shows as a separate run. To group the calls of one conversation, wrap them in a span that sets `gen_ai.conversation.id`:
+
+```typescript
+import { trace } from '@opentelemetry/api';
+
+const tracer = trace.getTracer('support-agent');
+
+await tracer.startActiveSpan('handle_message', async (span) => {
+ span.setAttribute('gen_ai.conversation.id', chatId);
+ try {
+ await generateText({ model, prompt, telemetry: { functionId: 'support-agent' } });
+ } finally {
+ span.end();
+ }
+});
+```
+
+KloudMate copies the ID only to spans that arrive in the same export as the wrapping span. If a request runs longer than the exporter's batch delay, 5 seconds by default, set `OTEL_BSP_SCHEDULE_DELAY` (in milliseconds) above the request's duration.
+
+## AI SDK 5 and 6
+
+Earlier versions don't use `@ai-sdk/otel` or `registerTelemetry`. Start the OpenTelemetry `NodeSDK` as above, then turn on telemetry for each call:
+
+```typescript
+const result = await generateText({
+ model,
+ prompt,
+ experimental_telemetry: { isEnabled: true, functionId: 'support-agent' },
+});
+```
+
+These versions emit spans with `ai.*` attributes, which KloudMate maps to the GenAI conventions when they arrive. With caching turned on, the Anthropic and Amazon Bedrock providers in AI SDK 5 report input tokens without the cached part, so those calls can show a lower cost than you were billed.
+
+## Check the data
+
+Run one call, then open **AI Observability → Traces**. The run appears as **support-agent Turn** with its model, steps, tokens, and estimated cost.
+
+## Related resources
+
+- [AI SDK telemetry](https://ai-sdk.dev/docs/ai-sdk-core/telemetry)
+- [Use AI Observability](/ai-observability/ai-observability-interface/)
+- [Send GenAI traces](/ai-observability/send-genai-traces/)
diff --git a/src/content/docs/guides/index.mdx b/src/content/docs/guides/index.mdx
index 737d7565..731f144f 100644
--- a/src/content/docs/guides/index.mdx
+++ b/src/content/docs/guides/index.mdx
@@ -13,11 +13,23 @@ Walkthroughs for sending application logs directly to KloudMate.
- [Send Winston Logs with OpenTelemetry (Node.js)](/guides/logs/send-winston-logs-with-opentelemetry-nodejs/)
-## LLM & AI Guides
-
-Instrument LLM and agent applications and send their traces to KloudMate.
-
-- [Instrument a Python LLM App with OpenLLMetry](/guides/llm/instrument-python-app-with-openllmetry/)
+## AI Observability Guides
+
+Send traces from LLM apps and agent frameworks to [AI Observability](/ai-observability/).
+
+- [Claude Code and Cowork](/guides/ai-observability/anthropic/send-claude-code-telemetry/)
+- [Claude API and SDK](/guides/ai-observability/anthropic/send-claude-api-traces/)
+- [OpenAI SDK](/guides/ai-observability/openai/openai-sdk/)
+- [OpenAI Agents SDK](/guides/ai-observability/openai/agents-sdk/)
+- [OpenAI Codex](/guides/ai-observability/openai/codex/)
+- [CrewAI](/guides/ai-observability/crewai/)
+- [Google ADK](/guides/ai-observability/google-adk/)
+- [LangChain and LangGraph](/guides/ai-observability/langchain-langgraph/)
+- [LlamaIndex](/guides/ai-observability/llamaindex/)
+- [Mastra](/guides/ai-observability/mastra/)
+- [Pydantic AI](/guides/ai-observability/pydantic-ai/)
+- [Vercel AI SDK](/guides/ai-observability/vercel-ai-sdk/)
+- [Instrument a Python LLM App with OpenLLMetry](/guides/ai-observability/instrument-python-app-with-openllmetry/)
## Workflow Guides
diff --git a/src/content/docs/guides/llm/_meta.json b/src/content/docs/guides/llm/_meta.json
deleted file mode 100644
index 22a0a885..00000000
--- a/src/content/docs/guides/llm/_meta.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "label": "LLM & AI",
- "order": 4
-}