
Instrument Llm Analytics
- 126 installs
- 70 repo stars
- Updated August 4, 2026
- posthog/ai-plugin
instrument-llm-analytics is a Claude Code skill for ai & agent building.
About
instrument-llm-analytics is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- instrument-llm-analytics
- AI & Agent Building
- AI-coding skill
Instrument Llm Analytics by the numbers
- 126 all-time installs (skills.sh)
- +6 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #3,699 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/posthog/ai-plugin --skill instrument-llm-analyticsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 126 |
|---|---|
| repo stars | ★ 70 |
| Last updated | August 4, 2026 |
| Repository | posthog/ai-plugin ↗ |
How do I helps with ai & agent building tasks during AI-assisted development.?
Helps with ai & agent building tasks during AI-assisted development.
Who is it for?
Best when you're working on ai & agent building and need structured help with instrument llm analytics.
Skip if: Teams with no ai & agent building needs, or anyone wanting a generic chat assistant without this specific workflow.
When should I use this skill?
When you need to helps with ai & agent building tasks during AI-assisted development., or when instrument-llm-analytics is a claude code skill for ai & agent building.
What you get
Structured output aligned to instrument-llm-analytics: instrument-llm-analytics, AI & Agent Building.
Files
Add PostHog LLM analytics
Use this skill to add PostHog LLM analytics that trace AI model usage in new or changed code. Use it after implementing LLM features or reviewing PRs to ensure all generations are captured with token counts, latency, and costs. If PostHog is not yet installed, this skill also covers initial SDK setup. Supports any provider or framework.
Supported providers: OpenAI, Azure OpenAI, Anthropic, Google, Cohere, Mistral, Perplexity, DeepSeek, Groq, Together AI, Fireworks AI, xAI, Cerebras, Hugging Face, Ollama, OpenRouter.
Supported frameworks: LangChain, LlamaIndex, CrewAI, AutoGen, DSPy, LangGraph, Pydantic AI, Vercel AI, LiteLLM, Instructor, Semantic Kernel, Mirascope, Mastra, SmolAgents, OpenAI Agents.
Proxy/gateway: Portkey, Helicone.
Instructions
Follow these steps IN ORDER:
STEP 1: Analyze the codebase and detect the LLM stack.
- Look for LLM provider SDKs (openai, anthropic, google-generativeai, etc.) and AI frameworks (langchain, llamaindex, crewai, etc.) in dependency files and imports.
- Look for lockfiles to determine the package manager.
- Check for existing PostHog or observability setup. If PostHog is already installed and LLM tracing is configured, skip to STEP 4 to add tracing for any new LLM calls.
STEP 2: Research instrumentation. (Skip if PostHog LLM tracing is already set up.) 2.1. Find the reference file below that matches the detected provider or framework — it is the source of truth for callback setup, middleware configuration, and event capture. Read it now. 2.2. If no reference matches, use manual-capture.md as a fallback — it covers the generic event capture approach that works with any provider.
STEP 3: Install the PostHog SDK. (Skip if PostHog is already set up.)
- Add the PostHog SDK and any required callback/integration packages.
- Do not manually edit dependency files — use the package manager's install command.
- Always install packages as a background task. Don't await completion; proceed with other work immediately.
STEP 4: Add LLM tracing.
- Instrument LLM calls to capture input tokens, output tokens, model name, latency, and costs for every generation.
- Follow the provider-specific reference for the exact callback/middleware setup.
- Do not alter the fundamental architecture of existing files. Make additions minimal and targeted.
- You must read a file immediately before attempting to write it.
STEP 5: Link to users.
- Associate LLM generations with identified users via distinct IDs when possible.
STEP 6: Set up environment variables.
- Check if the project already has PostHog environment variables configured (e.g. in
.env,.env.local, or framework-specific env files). If valid values already exist, skip this step. - If the PostHog API key is missing, use the PostHog MCP server's
projects-gettool to retrieve the project'sapi_token. If multiple projects are returned, ask the user which project to use. If the MCP server is not connected or not authenticated, ask the user for their PostHog project API key instead. - For the PostHog host URL, use
https://us.i.posthog.comfor US Cloud orhttps://eu.i.posthog.comfor EU Cloud. - Write these values to the appropriate env file using the framework's naming convention.
- Reference these environment variables in code instead of hardcoding them.
Reference files
references/openai.md- Openai observability installation - docsreferences/azure-openai.md- Azure openai observability installation - docsreferences/README.md- PostHog.ai for .netreferences/anthropic.md- Anthropic ai observability installation - docsreferences/google.md- Google ai observability installation - docsreferences/cohere.md- Cohere ai observability installation - docsreferences/mistral.md- Mistral ai observability installation - docsreferences/perplexity.md- Perplexity ai observability installation - docsreferences/deepseek.md- Deepseek ai observability installation - docsreferences/groq.md- Groq ai observability installation - docsreferences/together-ai.md- Together ai observability installation - docsreferences/fireworks-ai.md- Fireworks ai observability installation - docsreferences/xai.md- Xai observability installation - docsreferences/cerebras.md- Cerebras ai observability installation - docsreferences/hugging-face.md- Hugging face ai observability installation - docsreferences/ollama.md- Ollama ai observability installation - docsreferences/openrouter.md- Openrouter ai observability installation - docsreferences/langchain.md- Langchain ai observability installation - docsreferences/llamaindex.md- Llamaindex ai observability installation - docsreferences/crewai.md- Crewai observability installation - docsreferences/autogen.md- Autogen ai observability installation - docsreferences/dspy.md- Dspy ai observability installation - docsreferences/langgraph.md- Langgraph ai observability installation - docsreferences/pydantic-ai.md- Pydantic ai observability installation - docsreferences/vercel-ai.md- Vercel ai SDK observability installation - docsreferences/litellm.md- Litellm ai observability installation - docsreferences/instructor.md- Instructor ai observability installation - docsreferences/semantic-kernel.md- Semantic kernel ai observability installation - docsreferences/mirascope.md- Mirascope ai observability installation - docsreferences/mastra.md- Mastra ai observability installation - docsreferences/smolagents.md- Smolagents ai observability installation - docsreferences/openai-agents.md- Openai agents SDK observability installation - docsreferences/portkey.md- Portkey ai observability installation - docsreferences/helicone.md- Helicone ai observability installation - docsreferences/manual-capture.md- Manual capture ai observability installation - docsreferences/basics.md- Ai observability basics - docsreferences/traces.md- Traces - docsreferences/calculating-costs.md- Calculating llm costs - docs
Each provider reference contains installation instructions, SDK setup, and code examples specific to that provider or framework. Find the reference that matches the user's stack.
If the user's provider isn't listed, use manual-capture.md as a fallback — it covers the generic event capture approach that works with any provider.
Key principles
- Environment variables: Always use environment variables for PostHog and LLM provider keys. Never hardcode them.
- Minimal changes: Add LLM analytics alongside existing LLM calls. Don't replace or restructure existing code.
- Trace all generations: Capture input tokens, output tokens, model name, latency, and costs for every LLM call.
- Link to users: Associate LLM generations with identified users via distinct IDs when possible.
- One provider at a time: Only instrument the provider(s) the user is actually using. Don't add instrumentation for providers not present in the codebase.
Anthropic AI Observability installation - Docs
1. 1
Install dependencies
Required
Full working examples
See the complete Node.js and Python examples on GitHub. If you're using the PostHog SDK wrapper instead of OpenTelemetry, see the Node.js wrapper and Python wrapper examples.
Install the OpenTelemetry SDK, the Anthropic instrumentation, and the Anthropic SDK.
PostHog AI
Python
pip install anthropic opentelemetry-sdk "posthog[otel]" opentelemetry-instrumentation-anthropicNode
npm install @anthropic-ai/sdk @posthog/ai @opentelemetry/sdk-node @opentelemetry/resources @traceloop/instrumentation-anthropic2. 2
Set up OpenTelemetry tracing
Required
Configure OpenTelemetry to auto-instrument Anthropic SDK calls and export traces to PostHog. PostHog converts gen_ai.* spans into $ai_generation events automatically.
PostHog AI
Python
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.resources import Resource, SERVICE_NAME
from posthog.ai.otel import PostHogSpanProcessor
from opentelemetry.instrumentation.anthropic import AnthropicInstrumentor
resource = Resource(attributes={
SERVICE_NAME: "my-app",
"posthog.distinct_id": "user_123", # optional: identifies the user in PostHog
"foo": "bar", # custom properties are passed through
})
provider = TracerProvider(resource=resource)
provider.add_span_processor(
PostHogSpanProcessor(
api_key="<ph_project_token>",
host="https://us.i.posthog.com",
)
)
trace.set_tracer_provider(provider)
AnthropicInstrumentor().instrument()Node
import { NodeSDK } from '@opentelemetry/sdk-node'
import { resourceFromAttributes } from '@opentelemetry/resources'
import { PostHogSpanProcessor } from '@posthog/ai/otel'
import { AnthropicInstrumentation } from '@traceloop/instrumentation-anthropic'
const sdk = new NodeSDK({
resource: resourceFromAttributes({
'service.name': 'my-app',
'posthog.distinct_id': 'user_123', // optional: identifies the user in PostHog
foo: 'bar', // custom properties are passed through
}),
spanProcessors: [
new PostHogSpanProcessor({
apiKey: '<ph_project_token>',
host: 'https://us.i.posthog.com',
}),
],
instrumentations: [new AnthropicInstrumentation()],
})
sdk.start()3. 3
Call Anthropic
Required
Now, when you use the Anthropic SDK to call LLMs, PostHog automatically captures $ai_generation events via the OpenTelemetry instrumentation.
PostHog AI
Python
import anthropic
client = anthropic.Anthropic(api_key="sk-ant-api...")
response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1024,
messages=[
{"role": "user", "content": "Tell me a fun fact about hedgehogs"}
],
)
print(response.content[0].text)Node
import Anthropic from '@anthropic-ai/sdk'
const client = new Anthropic({ apiKey: 'sk-ant-api...' })
const response = await client.messages.create({
model: 'claude-sonnet-4-20250514',
max_tokens: 1024,
messages: [{ role: 'user', content: 'Tell me a fun fact about hedgehogs' }],
})
console.log(response.content[0].text)Note: This also works with theAsyncAnthropicclient as well asAnthropicBedrock,AnthropicVertex, and the async versions of those.
Note: If you want to capture LLM events anonymously, omit the posthog.distinct_id resource attribute. See our docs on anonymous vs identified events to learn more.You can expect captured $ai_generation events to have the following properties:
| Property | Description |
|---|---|
| $ai_model | The specific model, like gpt-5-mini or claude-4-sonnet |
| $ai_latency | The latency of the LLM call in seconds |
| $ai_time_to_first_token | Time to first token in seconds (streaming only) |
| $ai_tools | Tools and functions available to the LLM |
| $ai_input | List of messages sent to the LLM |
| $ai_input_tokens | The number of tokens in the input (often found in response.usage) |
| $ai_output_choices | List of response choices from the LLM |
| $ai_output_tokens | The number of tokens in the output (often found in response.usage) |
| $ai_total_cost_usd | The total cost in USD (input + output) |
| [[...]](/docs/ai-observability/generations.md#event-properties) | See full list of properties |
4. ## Verify traces and generations
Recommended
Confirm LLM events are being sent to PostHog
Let's make sure LLM events are being captured and sent to PostHog. Under AI Observability, you should see rows of data appear in the Traces and Generations tabs.
!LLM generations in PostHog!LLM generations in PostHog
Check for LLM events in PostHog
5. 4
Next steps
Recommended
Now that you're capturing AI conversations, continue with the resources below to learn what else AI Observability enables within the PostHog platform.
| Resource | Description |
|---|---|
| Basics | Learn the basics of how LLM calls become events in PostHog. |
| Generations | Read about the $ai_generation event and its properties. |
| Traces | Explore the trace hierarchy and how to use it to debug LLM calls. |
| Spans | Review spans and their role in representing individual operations. |
| Anaylze LLM performance | Learn how to create dashboards to analyze LLM performance. |
Community questions
Ask a question
Was this page useful?
HelpfulCould be better
AutoGen AI Observability installation - Docs
1. 1
Install dependencies
Required
Full working examples
See the complete Python example on GitHub. If you're using the PostHog SDK wrapper instead of OpenTelemetry, see the Python wrapper example.
Install the OpenTelemetry SDK, the OpenAI instrumentation, and AutoGen.
pip install autogen-agentchat "autogen-ext[openai]" openai opentelemetry-sdk "posthog[otel]" opentelemetry-instrumentation-openai-v22. 2
Set up OpenTelemetry tracing
Required
Configure OpenTelemetry to auto-instrument OpenAI SDK calls and export traces to PostHog. PostHog converts gen_ai.* spans into $ai_generation events automatically.
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.resources import Resource, SERVICE_NAME
from posthog.ai.otel import PostHogSpanProcessor
from opentelemetry.instrumentation.openai_v2 import OpenAIInstrumentor
resource = Resource(attributes={
SERVICE_NAME: "my-app",
"posthog.distinct_id": "user_123", # optional: identifies the user in PostHog
"foo": "bar", # custom properties are passed through
})
provider = TracerProvider(resource=resource)
provider.add_span_processor(
PostHogSpanProcessor(
api_key="<ph_project_token>",
host="https://us.i.posthog.com",
)
)
trace.set_tracer_provider(provider)
OpenAIInstrumentor().instrument()3. 3
Run your agents
Required
Use AutoGen as normal. PostHog automatically captures an $ai_generation event for each LLM call made through the OpenAI SDK that AutoGen uses internally.
import asyncio
from autogen_agentchat.agents import AssistantAgent
from autogen_ext.models.openai import OpenAIChatCompletionClient
model_client = OpenAIChatCompletionClient(
model="gpt-4o",
api_key="your_openai_api_key",
)
agent = AssistantAgent("assistant", model_client=model_client)
async def main():
result = await agent.run(task="Say 'Hello World!'")
print(result)
await model_client.close()
asyncio.run(main())Note: If you want to capture LLM events anonymously, omit the posthog.distinct_id resource attribute. See our docs on anonymous vs identified events to learn more.You can expect captured $ai_generation events to have the following properties:
| Property | Description |
|---|---|
| $ai_model | The specific model, like gpt-5-mini or claude-4-sonnet |
| $ai_latency | The latency of the LLM call in seconds |
| $ai_time_to_first_token | Time to first token in seconds (streaming only) |
| $ai_tools | Tools and functions available to the LLM |
| $ai_input | List of messages sent to the LLM |
| $ai_input_tokens | The number of tokens in the input (often found in response.usage) |
| $ai_output_choices | List of response choices from the LLM |
| $ai_output_tokens | The number of tokens in the output (often found in response.usage) |
| $ai_total_cost_usd | The total cost in USD (input + output) |
| [[...]](/docs/ai-observability/generations.md#event-properties) | See full list of properties |
4. ## Verify traces and generations
Recommended
Confirm LLM events are being sent to PostHog
Let's make sure LLM events are being captured and sent to PostHog. Under AI Observability, you should see rows of data appear in the Traces and Generations tabs.
!LLM generations in PostHog!LLM generations in PostHog
Check for LLM events in PostHog
5. 4
Next steps
Recommended
Now that you're capturing AI conversations, continue with the resources below to learn what else AI Observability enables within the PostHog platform.
| Resource | Description |
|---|---|
| Basics | Learn the basics of how LLM calls become events in PostHog. |
| Generations | Read about the $ai_generation event and its properties. |
| Traces | Explore the trace hierarchy and how to use it to debug LLM calls. |
| Spans | Review spans and their role in representing individual operations. |
| Anaylze LLM performance | Learn how to create dashboards to analyze LLM performance. |
Community questions
Ask a question
Was this page useful?
HelpfulCould be better
Azure OpenAI observability installation - Docs
1. 1
Install dependencies
Required
Full working examples
See the complete Node.js and Python examples on GitHub. If you're using the PostHog SDK wrapper instead of OpenTelemetry, see the Node.js wrapper and Python wrapper examples.
Install the OpenTelemetry SDK, the OpenAI instrumentation, and the OpenAI SDK.
PostHog AI
Python
pip install openai opentelemetry-sdk "posthog[otel]" opentelemetry-instrumentation-openai-v2Node
npm install openai @posthog/ai @opentelemetry/sdk-node @opentelemetry/resources @opentelemetry/instrumentation-openai2. 2
Set up OpenTelemetry tracing
Required
Configure OpenTelemetry to auto-instrument OpenAI SDK calls and export traces to PostHog. PostHog converts gen_ai.* spans into $ai_generation events automatically.
PostHog AI
Python
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.resources import Resource, SERVICE_NAME
from posthog.ai.otel import PostHogSpanProcessor
from opentelemetry.instrumentation.openai_v2 import OpenAIInstrumentor
resource = Resource(attributes={
SERVICE_NAME: "my-app",
"posthog.distinct_id": "user_123", # optional: identifies the user in PostHog
"foo": "bar", # custom properties are passed through
})
provider = TracerProvider(resource=resource)
provider.add_span_processor(
PostHogSpanProcessor(
api_key="<ph_project_token>",
host="https://us.i.posthog.com",
)
)
trace.set_tracer_provider(provider)
OpenAIInstrumentor().instrument()Node
import { NodeSDK } from '@opentelemetry/sdk-node'
import { resourceFromAttributes } from '@opentelemetry/resources'
import { PostHogSpanProcessor } from '@posthog/ai/otel'
import { OpenAIInstrumentation } from '@opentelemetry/instrumentation-openai'
const sdk = new NodeSDK({
resource: resourceFromAttributes({
'service.name': 'my-app',
'posthog.distinct_id': 'user_123', // optional: identifies the user in PostHog
foo: 'bar', // custom properties are passed through
}),
spanProcessors: [
new PostHogSpanProcessor({
apiKey: '<ph_project_token>',
host: 'https://us.i.posthog.com',
}),
],
instrumentations: [new OpenAIInstrumentation()],
})
sdk.start()3. 3
Call Azure OpenAI
Required
Now, when you call Azure OpenAI, PostHog automatically captures $ai_generation events via the OpenTelemetry instrumentation.
PostHog AI
Python
import openai
client = openai.AzureOpenAI(
api_key="<azure_openai_api_key>",
api_version="2024-10-21",
azure_endpoint="https://<your-resource>.openai.azure.com",
)
response = client.chat.completions.create(
model="<your-deployment-name>",
messages=[
{"role": "user", "content": "Tell me a fun fact about hedgehogs"}
],
)
print(response.choices[0].message.content)Node
import { AzureOpenAI } from 'openai'
const client = new AzureOpenAI({
apiKey: '<azure_openai_api_key>',
apiVersion: '2024-10-21',
endpoint: 'https://<your-resource>.openai.azure.com',
})
const response = await client.chat.completions.create({
model: '<your-deployment-name>',
messages: [{ role: 'user', content: 'Tell me a fun fact about hedgehogs' }],
})
console.log(response.choices[0].message.content)Note: If you want to capture LLM events anonymously, omit the posthog.distinct_id resource attribute. See our docs on anonymous vs identified events to learn more.You can expect captured $ai_generation events to have the following properties:
| Property | Description |
|---|---|
| $ai_model | The specific model, like gpt-5-mini or claude-4-sonnet |
| $ai_latency | The latency of the LLM call in seconds |
| $ai_time_to_first_token | Time to first token in seconds (streaming only) |
| $ai_tools | Tools and functions available to the LLM |
| $ai_input | List of messages sent to the LLM |
| $ai_input_tokens | The number of tokens in the input (often found in response.usage) |
| $ai_output_choices | List of response choices from the LLM |
| $ai_output_tokens | The number of tokens in the output (often found in response.usage) |
| $ai_total_cost_usd | The total cost in USD (input + output) |
| [[...]](/docs/ai-observability/generations.md#event-properties) | See full list of properties |
4. ## Verify traces and generations
Recommended
Confirm LLM events are being sent to PostHog
Let's make sure LLM events are being captured and sent to PostHog. Under AI Observability, you should see rows of data appear in the Traces and Generations tabs.
!LLM generations in PostHog!LLM generations in PostHog
Check for LLM events in PostHog
5. 4
Next steps
Recommended
Now that you're capturing AI conversations, continue with the resources below to learn what else AI Observability enables within the PostHog platform.
| Resource | Description |
|---|---|
| Basics | Learn the basics of how LLM calls become events in PostHog. |
| Generations | Read about the $ai_generation event and its properties. |
| Traces | Explore the trace hierarchy and how to use it to debug LLM calls. |
| Spans | Review spans and their role in representing individual operations. |
| Anaylze LLM performance | Learn how to create dashboards to analyze LLM performance. |
.NET support
PostHog.AI adds AI observability for .NET applications using Azure OpenAI. It is currently pre-release, so expect breaking changes before a stable release.
Install the packages:
Terminal
PostHog AI
dotnet add package PostHog.AI
dotnet add package Azure.AI.OpenAIWhen using dependency injection, register PostHog first, then register an Azure OpenAI client with the PostHog handler:
C#
PostHog AI
using System.ClientModel.Primitives;
using Azure;
using Azure.AI.OpenAI;
using Microsoft.Extensions.DependencyInjection;
using PostHog.AI;
using PostHog.Config;
var services = new ServiceCollection();
services.AddPostHog(options =>
{
options.PostConfigure(posthogOptions =>
{
posthogOptions.ProjectToken = "<ph_project_token>";
posthogOptions.HostUrl = new Uri("https://us.i.posthog.com");
});
});
services.AddPostHogAI();
services
.AddHttpClient("PostHogAzureOpenAIClient")
.AddPostHogOpenAIHandler();
services.AddSingleton<AzureOpenAIClient>(sp =>
{
var httpClientFactory = sp.GetRequiredService<IHttpClientFactory>();
var httpClient = httpClientFactory.CreateClient("PostHogAzureOpenAIClient");
var options = new AzureOpenAIClientOptions
{
Transport = new HttpClientPipelineTransport(httpClient),
};
return new AzureOpenAIClient(
new Uri("<azure_openai_endpoint>"),
new AzureKeyCredential("<azure_openai_api_key>"),
options);
});
var serviceProvider = services.BuildServiceProvider();
var azureOpenAIClient = serviceProvider.GetRequiredService<AzureOpenAIClient>();Use PostHogAIContext to attach trace, session, span, and user context to AI calls made inside a scope:
C#
PostHog AI
using PostHog.AI;
using (PostHogAIContext.BeginScope(
distinctId: "user-123",
traceId: "trace-abc",
sessionId: "session-xyz",
spanId: "span-1",
spanName: "summarize_text",
parentId: null))
{
var chatClient = azureOpenAIClient.GetChatClient("<deployment_name>");
await chatClient.CompleteChatAsync("Summarize this text");
}The integration captures $ai_generation and $ai_embedding events with model, latency, token, error, trace, session, and span properties. For more .NET SDK details, see the .NET library docs.
Community questions
Ask a question
Was this page useful?
HelpfulCould be better
AI Observability basics - Docs
This page covers how your LLM calls become analytics in PostHog and defines key concepts for AI Observability.
How LLM calls become events
PostHog's AI Observability works by wrapping your existing LLM provider's SDK to capture requests and responses. Your API calls still go directly to your provider, but the wrapper extracts metadata from each call and sends it to PostHog as an event.
graph LR Call\["LLM call"\] --> Wrapper\["Wrapped SDK"\] Wrapper --> Provider\["LLM provider API"\] Wrapper --> Event\["Event"\] Event --> PostHog\["PostHog"\]
Events sent to PostHog are called generations. A generation represents a single LLM call. For example, when you send a prompt to Claude and get a response back, that's one generation.
Generations are represented using the event name $ai_generation. Each generation captures the model, provider, input, output, token counts, latency, and cost.
Generation properties
Tokens and costs
Tokens are the units LLMs use to process text. LLM providers charge based on token usage:
- Input tokens are tokens used in the message you send to an LLM
- Output tokens are tokens used in the message you receive from an LLM
PostHog automatically calculates costs by matching your model and provider against pricing data. We use OpenRouter's pricing as our primary source, with fallback to manually maintained pricing for additional models.
You can also set custom pricing if you have negotiated rates or use unsupported models.
Message roles
When you send messages to an LLM, each message has a role that tells the model how to interpret it:
| Role | Purpose | Example |
|---|---|---|
| system | Instructions that define the assistant's behavior | "You are a helpful assistant that speaks like a pirate" |
| user | Messages from the end user | "What's the weather today?" |
| assistant | Previous model responses, used for conversation history | "Arrr, it be sunny with a chance of scurvy!" |
PostHog captures the full message array with roles intact, so you can see exactly what context the model had when it generated a response.
Grouping related events
Most LLM applications involve multiple calls. Traces, spans, and sessions let you see how they connect:
graph TD Session\["Session"\] Trace\["Trace"\] Generation1\["Generation"\] Span\["Span"\] Generation2\["Generation"\] Session -.-> Trace Trace --> Generation1 Trace --> Span Span --> Generation2
Here's a breakdown of this hierarchy:
| Term | Definition | Example |
|---|---|---|
| Session | Groups multiple traces together | A user's conversation thread |
| Trace | Contains generations and spans for a single request | One chatbot message and response |
| Span | Tracks an operation within a trace | A retrieval step or function call |
| Generation | An LLM call, tracked as $ai_generation events | Sending a prompt to Claude |
| Embedding | Converts text into vectors | Vectorizing documents for RAG |
Community questions
Ask a question
Was this page useful?
HelpfulCould be better
Calculating LLM costs - Docs
How are LLM costs calculated?
PostHog calculates cost based on the number of input (prompt) and output (completion) tokens generated by specific AI models, plus additional cost components like per-request and per-web-search pricing when applicable.
Model matching
We use OpenRouter's pricing data as our primary source to match your model and calculate costs. OpenRouter provides comprehensive pricing information for models across different providers, and we use both the $ai_provider and $ai_model properties from your events to find the exact pricing.
If OpenRouter doesn't have pricing data for a specific model, we fall back to our manually maintained pricing database for additional model support.
Cost components
The total cost ($ai_total_cost_usd) is calculated from multiple components:
- Token-based costs: Input (prompt) and output (completion) tokens
- Cache costs: Cached token reads and writes (when applicable)
- Request-based costs: Some models charge per request in addition to token costs
- Web search costs: Some models charge per web search performed
For cached LLM responses, our pricing models include cached token pricing which we automatically apply.
We also take into account the reasoning / thinking tokens for models that support it.
Cache token accounting
Different LLM providers report cache tokens differently:
- Exclusive counting (Anthropic/Claude) - Cache tokens are separate from
$ai_input_tokens. For example, if you have 100 input tokens and 50 cached tokens,$ai_input_tokensis 100. - Inclusive counting (OpenAI and most others) - Cache tokens are included in
$ai_input_tokens. Using the same example,$ai_input_tokensis 150.
PostHog auto-detects which counting style to use based on the $ai_provider and $ai_model properties. For Anthropic and Claude models, PostHog assumes exclusive counting. For all other providers, it assumes inclusive counting.
Overriding auto-detection
If you're manually capturing events or using a framework wrapper that normalizes token counts differently, override the auto-detection by setting the $ai_cache_reporting_exclusive property:
-
$ai_cache_reporting_exclusive: true- Cache tokens are separate from input tokens (Anthropic-style) -
$ai_cache_reporting_exclusive: false- Input tokens already include cache tokens
When not set, PostHog resolves this automatically and writes the resolved value back to the event for downstream consumers.
Setting custom pricing
You can override PostHog's automatic cost calculation by providing custom pricing for your LLM models. This is useful when:
- You have negotiated custom pricing with your LLM provider
- You're using a model that PostHog doesn't support yet
- PostHog's automatic pricing doesn't match your specific use case
Option 1: Custom price per token
If you know your pricing per token, you can set the following custom properties when calling your LLM:
-
$ai_input_token_price(required): Price per input/prompt token -
$ai_output_token_price(required): Price per output/completion token -
$ai_cache_read_token_price(optional): Price per cached token read -
$ai_cache_write_token_price(optional): Price per cached token write -
$ai_request_price(optional): Price per request (for models that charge per request) -
$ai_web_search_price(optional): Price per web search (for models that charge per search)
Important: Token prices should be per individual token, not per million tokens. For example, if your provider charges $0.03 per 1M tokens, you would set $ai_input_token_price: 0.00000003 (0.03 / 1,000,000).
PostHog AI
JavaScript
import { OpenAI } from '@posthog/ai'
import { PostHog } from 'posthog-node'
const phClient = new PostHog(
'<ph_project_token>',
{ host: 'https://us.i.posthog.com' }
)
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
posthog: phClient
})
const response = await openai.responses.create({
model: 'my-custom-model',
messages: [{ role: 'user', content: 'Hello' }],
posthogProperties: {
$ai_input_token_price: 0.00000003, // $0.03 per 1M tokens = $0.00000003 per token
$ai_output_token_price: 0.00000006, // $0.06 per 1M tokens = $0.00000006 per token
// Optional: cache pricing
$ai_cache_read_token_price: 0.000000015,
$ai_cache_write_token_price: 0.0000000375
}
})Python
from posthog.ai.openai import OpenAI
from posthog import Posthog
posthog = Posthog(
"<ph_project_token>",
host="https://us.i.posthog.com"
)
client = OpenAI(
api_key="sk-...",
posthog_client=posthog
)
response = client.responses.create(
model="my-custom-model",
messages=[{"role": "user", "content": "Hello"}],
posthog_properties={
"$ai_input_token_price": 0.00000003, # $0.03 per 1M tokens = $0.00000003 per token
"$ai_output_token_price": 0.00000006, # $0.06 per 1M tokens = $0.00000006 per token
# Optional: cache pricing
"$ai_cache_read_token_price": 0.000000015,
"$ai_cache_write_token_price": 0.0000000375
}
)Both $ai_input_token_price and $ai_output_token_price must be provided for custom pricing to take effect. PostHog will then calculate the total cost based on the token counts and your custom prices.
Option 2: Pre-calculated costs
If you're manually capturing LLM events and have already calculated the total costs yourself, you can send them directly:
-
$ai_input_cost_usd: Total cost for input/prompt tokens in USD -
$ai_output_cost_usd: Total cost for output/completion tokens in USD -
$ai_request_cost_usd: Total cost for requests in USD -
$ai_web_search_cost_usd: Total cost for web searches in USD
PostHog AI
JavaScript
// After making your LLM call and calculating costs
posthog.capture('$ai_generation', {
$ai_trace_id: traceId,
$ai_model: 'my-custom-model',
$ai_provider: 'my-provider',
$ai_input_tokens: inputTokens,
$ai_output_tokens: outputTokens,
$ai_input_cost_usd: 0.0042,
$ai_output_cost_usd: 0.0028
// ... other required properties
})Python
# After making your LLM call and calculating costs
posthog.capture(
event='$ai_generation',
properties={
'$ai_trace_id': trace_id,
'$ai_model': 'my-custom-model',
'$ai_provider': 'my-provider',
'$ai_input_tokens': input_tokens,
'$ai_output_tokens': output_tokens,
'$ai_input_cost_usd': 0.0042,
'$ai_output_cost_usd': 0.0028
# ... other required properties
}
)PostHog will use these values directly without any additional calculation.
Precedence
Cost calculation follows this precedence order:
1. Pre-calculated costs ($ai_input_cost_usd, $ai_output_cost_usd, $ai_request_cost_usd, $ai_web_search_cost_usd): These values are used directly without any additional calculation 2. Custom pricing ($ai_input_token_price, $ai_output_token_price, $ai_request_price, $ai_web_search_price): PostHog calculates costs from token counts and usage metrics using your custom prices 3. Automatic model matching: PostHog matches your model using OpenRouter's pricing data first, then falls back to manually maintained pricing if needed
Cost calculation metadata
When PostHog calculates costs automatically, it sets the following metadata properties on the event to help you understand how costs were determined:
| Property | Description | Examples |
|---|---|---|
| $ai_model_cost_used | The model identifier used for cost calculation. May differ from the requested model when a variant or alias is resolved. | openai/gpt-4o-mini |
| $ai_cost_model_source | Where the cost data for this model was sourced from. | openrouter, manual, custom, passthrough |
| $ai_cost_model_provider | The provider used to look up the cost for this model. | openai, anthropic, custom |
These properties are useful for debugging cost discrepancies or understanding which pricing was applied when using model aliases or custom configurations.
You can find the code for this on GitHub.
Community questions
Ask a question
Was this page useful?
HelpfulCould be better
Cerebras AI Observability installation - Docs
1. 1
Install dependencies
Required
Full working examples
See the complete Node.js and Python examples on GitHub. If you're using the PostHog SDK wrapper instead of OpenTelemetry, see the Node.js wrapper and Python wrapper examples.
Install the OpenTelemetry SDK, the OpenAI instrumentation, and the OpenAI SDK.
PostHog AI
Python
pip install openai opentelemetry-sdk "posthog[otel]" opentelemetry-instrumentation-openai-v2Node
npm install openai @posthog/ai @opentelemetry/sdk-node @opentelemetry/resources @opentelemetry/instrumentation-openai2. 2
Set up OpenTelemetry tracing
Required
Configure OpenTelemetry to auto-instrument OpenAI SDK calls and export traces to PostHog. PostHog converts gen_ai.* spans into $ai_generation events automatically.
PostHog AI
Python
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.resources import Resource, SERVICE_NAME
from posthog.ai.otel import PostHogSpanProcessor
from opentelemetry.instrumentation.openai_v2 import OpenAIInstrumentor
resource = Resource(attributes={
SERVICE_NAME: "my-app",
"posthog.distinct_id": "user_123", # optional: identifies the user in PostHog
"foo": "bar", # custom properties are passed through
})
provider = TracerProvider(resource=resource)
provider.add_span_processor(
PostHogSpanProcessor(
api_key="<ph_project_token>",
host="https://us.i.posthog.com",
)
)
trace.set_tracer_provider(provider)
OpenAIInstrumentor().instrument()Node
import { NodeSDK } from '@opentelemetry/sdk-node'
import { resourceFromAttributes } from '@opentelemetry/resources'
import { PostHogSpanProcessor } from '@posthog/ai/otel'
import { OpenAIInstrumentation } from '@opentelemetry/instrumentation-openai'
const sdk = new NodeSDK({
resource: resourceFromAttributes({
'service.name': 'my-app',
'posthog.distinct_id': 'user_123', // optional: identifies the user in PostHog
foo: 'bar', // custom properties are passed through
}),
spanProcessors: [
new PostHogSpanProcessor({
apiKey: '<ph_project_token>',
host: 'https://us.i.posthog.com',
}),
],
instrumentations: [new OpenAIInstrumentation()],
})
sdk.start()3. 3
Call Cerebras
Required
Now, when you use the OpenAI SDK to call Cerebras, PostHog automatically captures $ai_generation events via the OpenTelemetry instrumentation.
PostHog AI
Python
import openai
client = openai.OpenAI(
base_url="https://api.cerebras.ai/v1",
api_key="<cerebras_api_key>",
)
response = client.chat.completions.create(
model="llama-3.3-70b",
max_completion_tokens=1024,
messages=[
{"role": "user", "content": "Tell me a fun fact about hedgehogs"}
],
)
print(response.choices[0].message.content)Node
import OpenAI from 'openai'
const client = new OpenAI({
baseURL: 'https://api.cerebras.ai/v1',
apiKey: '<cerebras_api_key>',
})
const response = await client.chat.completions.create({
model: 'llama-3.3-70b',
max_completion_tokens: 1024,
messages: [{ role: 'user', content: 'Tell me a fun fact about hedgehogs' }],
})
console.log(response.choices[0].message.content)Note: If you want to capture LLM events anonymously, omit the posthog.distinct_id resource attribute. See our docs on anonymous vs identified events to learn more.You can expect captured $ai_generation events to have the following properties:
| Property | Description |
|---|---|
| $ai_model | The specific model, like gpt-5-mini or claude-4-sonnet |
| $ai_latency | The latency of the LLM call in seconds |
| $ai_time_to_first_token | Time to first token in seconds (streaming only) |
| $ai_tools | Tools and functions available to the LLM |
| $ai_input | List of messages sent to the LLM |
| $ai_input_tokens | The number of tokens in the input (often found in response.usage) |
| $ai_output_choices | List of response choices from the LLM |
| $ai_output_tokens | The number of tokens in the output (often found in response.usage) |
| $ai_total_cost_usd | The total cost in USD (input + output) |
| [[...]](/docs/ai-observability/generations.md#event-properties) | See full list of properties |
4. ## Verify traces and generations
Recommended
Confirm LLM events are being sent to PostHog
Let's make sure LLM events are being captured and sent to PostHog. Under AI Observability, you should see rows of data appear in the Traces and Generations tabs.
!LLM generations in PostHog!LLM generations in PostHog
Check for LLM events in PostHog
5. 4
Next steps
Recommended
Now that you're capturing AI conversations, continue with the resources below to learn what else AI Observability enables within the PostHog platform.
| Resource | Description |
|---|---|
| Basics | Learn the basics of how LLM calls become events in PostHog. |
| Generations | Read about the $ai_generation event and its properties. |
| Traces | Explore the trace hierarchy and how to use it to debug LLM calls. |
| Spans | Review spans and their role in representing individual operations. |
| Anaylze LLM performance | Learn how to create dashboards to analyze LLM performance. |
Community questions
Ask a question
Was this page useful?
HelpfulCould be better
Cohere AI Observability installation - Docs
1. 1
Install dependencies
Required
Full working examples
See the complete Node.js and Python examples on GitHub. If you're using the PostHog SDK wrapper instead of OpenTelemetry, see the Node.js wrapper and Python wrapper examples.
Install the OpenTelemetry SDK, the OpenAI instrumentation, and the OpenAI SDK.
PostHog AI
Python
pip install openai opentelemetry-sdk "posthog[otel]" opentelemetry-instrumentation-openai-v2Node
npm install openai @posthog/ai @opentelemetry/sdk-node @opentelemetry/resources @opentelemetry/instrumentation-openai2. 2
Set up OpenTelemetry tracing
Required
Configure OpenTelemetry to auto-instrument OpenAI SDK calls and export traces to PostHog. PostHog converts gen_ai.* spans into $ai_generation events automatically.
PostHog AI
Python
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.resources import Resource, SERVICE_NAME
from posthog.ai.otel import PostHogSpanProcessor
from opentelemetry.instrumentation.openai_v2 import OpenAIInstrumentor
resource = Resource(attributes={
SERVICE_NAME: "my-app",
"posthog.distinct_id": "user_123", # optional: identifies the user in PostHog
"foo": "bar", # custom properties are passed through
})
provider = TracerProvider(resource=resource)
provider.add_span_processor(
PostHogSpanProcessor(
api_key="<ph_project_token>",
host="https://us.i.posthog.com",
)
)
trace.set_tracer_provider(provider)
OpenAIInstrumentor().instrument()Node
import { NodeSDK } from '@opentelemetry/sdk-node'
import { resourceFromAttributes } from '@opentelemetry/resources'
import { PostHogSpanProcessor } from '@posthog/ai/otel'
import { OpenAIInstrumentation } from '@opentelemetry/instrumentation-openai'
const sdk = new NodeSDK({
resource: resourceFromAttributes({
'service.name': 'my-app',
'posthog.distinct_id': 'user_123', // optional: identifies the user in PostHog
foo: 'bar', // custom properties are passed through
}),
spanProcessors: [
new PostHogSpanProcessor({
apiKey: '<ph_project_token>',
host: 'https://us.i.posthog.com',
}),
],
instrumentations: [new OpenAIInstrumentation()],
})
sdk.start()3. 3
Call Cohere
Required
Now, when you use the OpenAI SDK to call Cohere, PostHog automatically captures $ai_generation events via the OpenTelemetry instrumentation.
PostHog AI
Python
import openai
client = openai.OpenAI(
base_url="https://api.cohere.ai/compatibility/v1",
api_key="<cohere_api_key>",
)
response = client.chat.completions.create(
model="command-a-03-2025",
max_completion_tokens=1024,
messages=[
{"role": "user", "content": "Tell me a fun fact about hedgehogs"}
],
)
print(response.choices[0].message.content)Node
import OpenAI from 'openai'
const client = new OpenAI({
baseURL: 'https://api.cohere.ai/compatibility/v1',
apiKey: '<cohere_api_key>',
})
const response = await client.chat.completions.create({
model: 'command-a-03-2025',
max_completion_tokens: 1024,
messages: [{ role: 'user', content: 'Tell me a fun fact about hedgehogs' }],
})
console.log(response.choices[0].message.content)Note: If you want to capture LLM events anonymously, omit the posthog.distinct_id resource attribute. See our docs on anonymous vs identified events to learn more.You can expect captured $ai_generation events to have the following properties:
| Property | Description |
|---|---|
| $ai_model | The specific model, like gpt-5-mini or claude-4-sonnet |
| $ai_latency | The latency of the LLM call in seconds |
| $ai_time_to_first_token | Time to first token in seconds (streaming only) |
| $ai_tools | Tools and functions available to the LLM |
| $ai_input | List of messages sent to the LLM |
| $ai_input_tokens | The number of tokens in the input (often found in response.usage) |
| $ai_output_choices | List of response choices from the LLM |
| $ai_output_tokens | The number of tokens in the output (often found in response.usage) |
| $ai_total_cost_usd | The total cost in USD (input + output) |
| [[...]](/docs/ai-observability/generations.md#event-properties) | See full list of properties |
4. ## Verify traces and generations
Recommended
Confirm LLM events are being sent to PostHog
Let's make sure LLM events are being captured and sent to PostHog. Under AI Observability, you should see rows of data appear in the Traces and Generations tabs.
!LLM generations in PostHog!LLM generations in PostHog
Check for LLM events in PostHog
5. 4
Next steps
Recommended
Now that you're capturing AI conversations, continue with the resources below to learn what else AI Observability enables within the PostHog platform.
| Resource | Description |
|---|---|
| Basics | Learn the basics of how LLM calls become events in PostHog. |
| Generations | Read about the $ai_generation event and its properties. |
| Traces | Explore the trace hierarchy and how to use it to debug LLM calls. |
| Spans | Review spans and their role in representing individual operations. |
| Anaylze LLM performance | Learn how to create dashboards to analyze LLM performance. |
Community questions
Ask a question
Was this page useful?
HelpfulCould be better
CrewAI observability installation - Docs
1. 1
Install the PostHog SDK
Required
Setting up analytics starts with installing the PostHog SDK. CrewAI uses LiteLLM under the hood, and PostHog integrates with LiteLLM's callback system.
pip install posthog2. 2
Install CrewAI
Required
Install CrewAI. PostHog instruments your LLM calls through LiteLLM's callback system that CrewAI uses natively.
pip install crewai litellm3. 3
Configure PostHog with LiteLLM
Required
Set your PostHog project token and host as environment variables, then configure LiteLLM to use PostHog as a callback handler. You can find your project token in your project settings.
import os
import litellm
from crewai import Agent, Task, Crew
# Set PostHog environment variables
os.environ["POSTHOG_API_KEY"] = "<ph_project_token>"
os.environ["POSTHOG_API_URL"] = "https://us.i.posthog.com"
# Enable PostHog callbacks in LiteLLM
litellm.success_callback = ["posthog"]
litellm.failure_callback = ["posthog"]How this works
CrewAI uses LiteLLM under the hood for LLM provider access. By configuring PostHog as a LiteLLM callback, all LLM calls made through CrewAI are automatically captured as $ai_generation events without proxying your calls.
4. 4
Run your crew
Required
Run your CrewAI agents as normal. PostHog automatically captures generation events for each LLM call.
researcher = Agent(
role="Researcher",
goal="Find interesting facts about hedgehogs",
backstory="You are an expert wildlife researcher.",
)
task = Task(
description="Research three fun facts about hedgehogs.",
expected_output="A list of three fun facts.",
agent=researcher,
)
crew = Crew(
agents=[researcher],
tasks=[task],
)
result = crew.kickoff()
print(result)You can expect captured $ai_generation events to have the following properties:
| Property | Description |
|---|---|
| $ai_model | The specific model, like gpt-5-mini or claude-4-sonnet |
| $ai_latency | The latency of the LLM call in seconds |
| $ai_time_to_first_token | Time to first token in seconds (streaming only) |
| $ai_tools | Tools and functions available to the LLM |
| $ai_input | List of messages sent to the LLM |
| $ai_input_tokens | The number of tokens in the input (often found in response.usage) |
| $ai_output_choices | List of response choices from the LLM |
| $ai_output_tokens | The number of tokens in the output (often found in response.usage) |
| $ai_total_cost_usd | The total cost in USD (input + output) |
| [[...]](/docs/ai-observability/generations.md#event-properties) | See full list of properties |
5. ## Verify traces and generations
Recommended
Confirm LLM events are being sent to PostHog
Let's make sure LLM events are being captured and sent to PostHog. Under AI Observability, you should see rows of data appear in the Traces and Generations tabs.
!LLM generations in PostHog!LLM generations in PostHog
Check for LLM events in PostHog
6. 5
Next steps
Recommended
Now that you're capturing AI conversations, continue with the resources below to learn what else AI Observability enables within the PostHog platform.
| Resource | Description |
|---|---|
| Basics | Learn the basics of how LLM calls become events in PostHog. |
| Generations | Read about the $ai_generation event and its properties. |
| Traces | Explore the trace hierarchy and how to use it to debug LLM calls. |
| Spans | Review spans and their role in representing individual operations. |
| Anaylze LLM performance | Learn how to create dashboards to analyze LLM performance. |
Community questions
Ask a question
Was this page useful?
HelpfulCould be better
DeepSeek AI Observability installation - Docs
1. 1
Install dependencies
Required
Full working examples
See the complete Node.js and Python examples on GitHub. If you're using the PostHog SDK wrapper instead of OpenTelemetry, see the Node.js wrapper and Python wrapper examples.
Install the OpenTelemetry SDK, the OpenAI instrumentation, and the OpenAI SDK.
PostHog AI
Python
pip install openai opentelemetry-sdk "posthog[otel]" opentelemetry-instrumentation-openai-v2Node
npm install openai @posthog/ai @opentelemetry/sdk-node @opentelemetry/resources @opentelemetry/instrumentation-openai2. 2
Set up OpenTelemetry tracing
Required
Configure OpenTelemetry to auto-instrument OpenAI SDK calls and export traces to PostHog. PostHog converts gen_ai.* spans into $ai_generation events automatically.
PostHog AI
Python
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.resources import Resource, SERVICE_NAME
from posthog.ai.otel import PostHogSpanProcessor
from opentelemetry.instrumentation.openai_v2 import OpenAIInstrumentor
resource = Resource(attributes={
SERVICE_NAME: "my-app",
"posthog.distinct_id": "user_123", # optional: identifies the user in PostHog
"foo": "bar", # custom properties are passed through
})
provider = TracerProvider(resource=resource)
provider.add_span_processor(
PostHogSpanProcessor(
api_key="<ph_project_token>",
host="https://us.i.posthog.com",
)
)
trace.set_tracer_provider(provider)
OpenAIInstrumentor().instrument()Node
import { NodeSDK } from '@opentelemetry/sdk-node'
import { resourceFromAttributes } from '@opentelemetry/resources'
import { PostHogSpanProcessor } from '@posthog/ai/otel'
import { OpenAIInstrumentation } from '@opentelemetry/instrumentation-openai'
const sdk = new NodeSDK({
resource: resourceFromAttributes({
'service.name': 'my-app',
'posthog.distinct_id': 'user_123', // optional: identifies the user in PostHog
foo: 'bar', // custom properties are passed through
}),
spanProcessors: [
new PostHogSpanProcessor({
apiKey: '<ph_project_token>',
host: 'https://us.i.posthog.com',
}),
],
instrumentations: [new OpenAIInstrumentation()],
})
sdk.start()3. 3
Call DeepSeek
Required
Now, when you use the OpenAI SDK to call DeepSeek, PostHog automatically captures $ai_generation events via the OpenTelemetry instrumentation.
PostHog AI
Python
import openai
client = openai.OpenAI(
base_url="https://api.deepseek.com",
api_key="<deepseek_api_key>",
)
response = client.chat.completions.create(
model="deepseek-chat",
max_completion_tokens=1024,
messages=[
{"role": "user", "content": "Tell me a fun fact about hedgehogs"}
],
)
print(response.choices[0].message.content)Node
import OpenAI from 'openai'
const client = new OpenAI({
baseURL: 'https://api.deepseek.com',
apiKey: '<deepseek_api_key>',
})
const response = await client.chat.completions.create({
model: 'deepseek-chat',
max_completion_tokens: 1024,
messages: [{ role: 'user', content: 'Tell me a fun fact about hedgehogs' }],
})
console.log(response.choices[0].message.content)Note: If you want to capture LLM events anonymously, omit the posthog.distinct_id resource attribute. See our docs on anonymous vs identified events to learn more.You can expect captured $ai_generation events to have the following properties:
| Property | Description |
|---|---|
| $ai_model | The specific model, like gpt-5-mini or claude-4-sonnet |
| $ai_latency | The latency of the LLM call in seconds |
| $ai_time_to_first_token | Time to first token in seconds (streaming only) |
| $ai_tools | Tools and functions available to the LLM |
| $ai_input | List of messages sent to the LLM |
| $ai_input_tokens | The number of tokens in the input (often found in response.usage) |
| $ai_output_choices | List of response choices from the LLM |
| $ai_output_tokens | The number of tokens in the output (often found in response.usage) |
| $ai_total_cost_usd | The total cost in USD (input + output) |
| [[...]](/docs/ai-observability/generations.md#event-properties) | See full list of properties |
4. ## Verify traces and generations
Recommended
Confirm LLM events are being sent to PostHog
Let's make sure LLM events are being captured and sent to PostHog. Under AI Observability, you should see rows of data appear in the Traces and Generations tabs.
!LLM generations in PostHog!LLM generations in PostHog
Check for LLM events in PostHog
5. 4
Next steps
Recommended
Now that you're capturing AI conversations, continue with the resources below to learn what else AI Observability enables within the PostHog platform.
| Resource | Description |
|---|---|
| Basics | Learn the basics of how LLM calls become events in PostHog. |
| Generations | Read about the $ai_generation event and its properties. |
| Traces | Explore the trace hierarchy and how to use it to debug LLM calls. |
| Spans | Review spans and their role in representing individual operations. |
| Anaylze LLM performance | Learn how to create dashboards to analyze LLM performance. |
Community questions
Ask a question
Was this page useful?
HelpfulCould be better
DSPy AI Observability installation - Docs
1. 1
Install the PostHog SDK
Required
Setting up analytics starts with installing the PostHog SDK. The DSPy integration uses PostHog's LiteLLM callback.
pip install posthog2. 2
Install DSPy and LiteLLM
Required
Install DSPy and LiteLLM. DSPy uses LiteLLM natively for provider access, and PostHog integrates with LiteLLM's callback system.
pip install dspy litellm3. 3
Configure PostHog with LiteLLM
Required
Set your PostHog project token and host as environment variables, then configure LiteLLM to use PostHog as a callback handler. You can find your project token in your project settings.
import os
import dspy
import litellm
# Set PostHog environment variables
os.environ["POSTHOG_API_KEY"] = "<ph_project_token>"
os.environ["POSTHOG_API_URL"] = "https://us.i.posthog.com"
# Enable PostHog callbacks in LiteLLM
litellm.success_callback = ["posthog"]
litellm.failure_callback = ["posthog"]
# Configure DSPy to use an LLM
lm = dspy.LM("openai/gpt-5-mini", api_key="your_openai_api_key")
dspy.configure(lm=lm)How this works
DSPy uses LiteLLM under the hood for LLM provider access. By configuring PostHog as a LiteLLM callback, all LLM calls made through DSPy are automatically captured as $ai_generation events.
4. 4
Run DSPy modules
Required
Use DSPy as normal. PostHog automatically captures an $ai_generation event for each LLM call made through LiteLLM.
# Define a simple signature
class QA(dspy.Signature):
"""Answer the question."""
question: str = dspy.InputField()
answer: str = dspy.OutputField()
# Create and run a module
predictor = dspy.Predict(QA)
result = predictor(
question="What is a fun fact about hedgehogs?"
)
print(result.answer)You can expect captured $ai_generation events to have the following properties:
| Property | Description |
|---|---|
| $ai_model | The specific model, like gpt-5-mini or claude-4-sonnet |
| $ai_latency | The latency of the LLM call in seconds |
| $ai_time_to_first_token | Time to first token in seconds (streaming only) |
| $ai_tools | Tools and functions available to the LLM |
| $ai_input | List of messages sent to the LLM |
| $ai_input_tokens | The number of tokens in the input (often found in response.usage) |
| $ai_output_choices | List of response choices from the LLM |
| $ai_output_tokens | The number of tokens in the output (often found in response.usage) |
| $ai_total_cost_usd | The total cost in USD (input + output) |
| [[...]](/docs/ai-observability/generations.md#event-properties) | See full list of properties |
5. ## Verify traces and generations
Recommended
Confirm LLM events are being sent to PostHog
Let's make sure LLM events are being captured and sent to PostHog. Under AI Observability, you should see rows of data appear in the Traces and Generations tabs.
!LLM generations in PostHog!LLM generations in PostHog
Check for LLM events in PostHog
6. 5
Next steps
Recommended
Now that you're capturing AI conversations, continue with the resources below to learn what else AI Observability enables within the PostHog platform.
| Resource | Description |
|---|---|
| Basics | Learn the basics of how LLM calls become events in PostHog. |
| Generations | Read about the $ai_generation event and its properties. |
| Traces | Explore the trace hierarchy and how to use it to debug LLM calls. |
| Spans | Review spans and their role in representing individual operations. |
| Anaylze LLM performance | Learn how to create dashboards to analyze LLM performance. |
Community questions
Ask a question
Was this page useful?
HelpfulCould be better
Fireworks AI Observability installation - Docs
1. 1
Install dependencies
Required
Full working examples
See the complete Node.js and Python examples on GitHub. If you're using the PostHog SDK wrapper instead of OpenTelemetry, see the Node.js wrapper and Python wrapper examples.
Install the OpenTelemetry SDK, the OpenAI instrumentation, and the OpenAI SDK.
PostHog AI
Python
pip install openai opentelemetry-sdk "posthog[otel]" opentelemetry-instrumentation-openai-v2Node
npm install openai @posthog/ai @opentelemetry/sdk-node @opentelemetry/resources @opentelemetry/instrumentation-openai2. 2
Set up OpenTelemetry tracing
Required
Configure OpenTelemetry to auto-instrument OpenAI SDK calls and export traces to PostHog. PostHog converts gen_ai.* spans into $ai_generation events automatically.
PostHog AI
Python
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.resources import Resource, SERVICE_NAME
from posthog.ai.otel import PostHogSpanProcessor
from opentelemetry.instrumentation.openai_v2 import OpenAIInstrumentor
resource = Resource(attributes={
SERVICE_NAME: "my-app",
"posthog.distinct_id": "user_123", # optional: identifies the user in PostHog
"foo": "bar", # custom properties are passed through
})
provider = TracerProvider(resource=resource)
provider.add_span_processor(
PostHogSpanProcessor(
api_key="<ph_project_token>",
host="https://us.i.posthog.com",
)
)
trace.set_tracer_provider(provider)
OpenAIInstrumentor().instrument()Node
import { NodeSDK } from '@opentelemetry/sdk-node'
import { resourceFromAttributes } from '@opentelemetry/resources'
import { PostHogSpanProcessor } from '@posthog/ai/otel'
import { OpenAIInstrumentation } from '@opentelemetry/instrumentation-openai'
const sdk = new NodeSDK({
resource: resourceFromAttributes({
'service.name': 'my-app',
'posthog.distinct_id': 'user_123', // optional: identifies the user in PostHog
foo: 'bar', // custom properties are passed through
}),
spanProcessors: [
new PostHogSpanProcessor({
apiKey: '<ph_project_token>',
host: 'https://us.i.posthog.com',
}),
],
instrumentations: [new OpenAIInstrumentation()],
})
sdk.start()3. 3
Call Fireworks AI
Required
Now, when you use the OpenAI SDK to call Fireworks AI, PostHog automatically captures $ai_generation events via the OpenTelemetry instrumentation.
PostHog AI
Python
import openai
client = openai.OpenAI(
base_url="https://api.fireworks.ai/inference/v1",
api_key="<fireworks_api_key>",
)
response = client.chat.completions.create(
model="accounts/fireworks/models/llama-v3p3-70b-instruct",
max_completion_tokens=1024,
messages=[
{"role": "user", "content": "Tell me a fun fact about hedgehogs"}
],
)
print(response.choices[0].message.content)Node
import OpenAI from 'openai'
const client = new OpenAI({
baseURL: 'https://api.fireworks.ai/inference/v1',
apiKey: '<fireworks_api_key>',
})
const response = await client.chat.completions.create({
model: 'accounts/fireworks/models/llama-v3p3-70b-instruct',
max_completion_tokens: 1024,
messages: [{ role: 'user', content: 'Tell me a fun fact about hedgehogs' }],
})
console.log(response.choices[0].message.content)Note: If you want to capture LLM events anonymously, omit the posthog.distinct_id resource attribute. See our docs on anonymous vs identified events to learn more.You can expect captured $ai_generation events to have the following properties:
| Property | Description |
|---|---|
| $ai_model | The specific model, like gpt-5-mini or claude-4-sonnet |
| $ai_latency | The latency of the LLM call in seconds |
| $ai_time_to_first_token | Time to first token in seconds (streaming only) |
| $ai_tools | Tools and functions available to the LLM |
| $ai_input | List of messages sent to the LLM |
| $ai_input_tokens | The number of tokens in the input (often found in response.usage) |
| $ai_output_choices | List of response choices from the LLM |
| $ai_output_tokens | The number of tokens in the output (often found in response.usage) |
| $ai_total_cost_usd | The total cost in USD (input + output) |
| [[...]](/docs/ai-observability/generations.md#event-properties) | See full list of properties |
4. ## Verify traces and generations
Recommended
Confirm LLM events are being sent to PostHog
Let's make sure LLM events are being captured and sent to PostHog. Under AI Observability, you should see rows of data appear in the Traces and Generations tabs.
!LLM generations in PostHog!LLM generations in PostHog
Check for LLM events in PostHog
5. 4
Next steps
Recommended
Now that you're capturing AI conversations, continue with the resources below to learn what else AI Observability enables within the PostHog platform.
| Resource | Description |
|---|---|
| Basics | Learn the basics of how LLM calls become events in PostHog. |
| Generations | Read about the $ai_generation event and its properties. |
| Traces | Explore the trace hierarchy and how to use it to debug LLM calls. |
| Spans | Review spans and their role in representing individual operations. |
| Anaylze LLM performance | Learn how to create dashboards to analyze LLM performance. |
Community questions
Ask a question
Was this page useful?
HelpfulCould be better
Google AI Observability installation - Docs
1. 1
Install dependencies
Required
Full working examples
See the complete Node.js and Python examples on GitHub. If you're using the PostHog SDK wrapper instead of OpenTelemetry, see the Node.js wrapper and Python wrapper examples.
Install the OpenTelemetry SDK, the Google Gen AI instrumentation, and the Google Gen AI SDK.
PostHog AI
Python
pip install google-genai opentelemetry-sdk "posthog[otel]" opentelemetry-instrumentation-google-generativeaiNode
npm install @google/genai @posthog/ai @opentelemetry/sdk-node @opentelemetry/resources @traceloop/instrumentation-google-generativeai2. 2
Set up OpenTelemetry tracing
Required
Configure OpenTelemetry to auto-instrument Google Gen AI SDK calls and export traces to PostHog. PostHog converts gen_ai.* spans into $ai_generation events automatically.
PostHog AI
Python
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.resources import Resource, SERVICE_NAME
from posthog.ai.otel import PostHogSpanProcessor
from opentelemetry.instrumentation.google_generativeai import GoogleGenerativeAiInstrumentor
resource = Resource(attributes={
SERVICE_NAME: "my-app",
"posthog.distinct_id": "user_123", # optional: identifies the user in PostHog
"foo": "bar", # custom properties are passed through
})
provider = TracerProvider(resource=resource)
provider.add_span_processor(
PostHogSpanProcessor(
api_key="<ph_project_token>",
host="https://us.i.posthog.com",
)
)
trace.set_tracer_provider(provider)
GoogleGenerativeAiInstrumentor().instrument()Node
import { NodeSDK } from '@opentelemetry/sdk-node'
import { resourceFromAttributes } from '@opentelemetry/resources'
import { PostHogSpanProcessor } from '@posthog/ai/otel'
import { GenAIInstrumentation } from '@traceloop/instrumentation-google-generativeai'
const sdk = new NodeSDK({
resource: resourceFromAttributes({
'service.name': 'my-app',
'posthog.distinct_id': 'user_123', // optional: identifies the user in PostHog
foo: 'bar', // custom properties are passed through
}),
spanProcessors: [
new PostHogSpanProcessor({
apiKey: '<ph_project_token>',
host: 'https://us.i.posthog.com',
}),
],
instrumentations: [new GenAIInstrumentation()],
})
sdk.start()3. 3
Call Google Gen AI LLMs
Required
Now, when you use the Google Gen AI SDK to call Gemini, PostHog automatically captures $ai_generation events via the OpenTelemetry instrumentation.
PostHog AI
Python
from google import genai
client = genai.Client(api_key="your_gemini_api_key")
response = client.models.generate_content(
model="gemini-2.5-flash",
contents=[{"role": "user", "parts": [{"text": "Tell me a fun fact about hedgehogs"}]}],
)
print(response.text)Node
import { GoogleGenAI } from '@google/genai'
const client = new GoogleGenAI({ apiKey: 'your_gemini_api_key' })
const response = await client.models.generateContent({
model: 'gemini-2.5-flash',
contents: 'Tell me a fun fact about hedgehogs',
})
console.log(response.text)Note: This integration also works with Vertex AI via Google Cloud Platform. Initialize the Google Gen AI client withvertexai=True, project=..., location=...(Python) or{ vertexai: true, project: '...', location: '...' }(Node) and the OpenTelemetry instrumentation will capture those calls the same way.
Note: If you want to capture LLM events anonymously, omit the posthog.distinct_id resource attribute. See our docs on anonymous vs identified events to learn more.You can expect captured $ai_generation events to have the following properties:
| Property | Description |
|---|---|
| $ai_model | The specific model, like gpt-5-mini or claude-4-sonnet |
| $ai_latency | The latency of the LLM call in seconds |
| $ai_time_to_first_token | Time to first token in seconds (streaming only) |
| $ai_tools | Tools and functions available to the LLM |
| $ai_input | List of messages sent to the LLM |
| $ai_input_tokens | The number of tokens in the input (often found in response.usage) |
| $ai_output_choices | List of response choices from the LLM |
| $ai_output_tokens | The number of tokens in the output (often found in response.usage) |
| $ai_total_cost_usd | The total cost in USD (input + output) |
| [[...]](/docs/ai-observability/generations.md#event-properties) | See full list of properties |
4. 4
Capture embeddings
Optional
PostHog can also capture embedding generations as $ai_embedding events. The OpenTelemetry instrumentation automatically captures these when you use the embed_content API:
PostHog AI
Python
response = client.models.embed_content(
model="gemini-embedding-001",
contents="The quick brown fox",
)Node
const response = await client.models.embedContent({
model: 'gemini-embedding-001',
contents: 'The quick brown fox',
})5. ## Verify traces and generations
Recommended
Confirm LLM events are being sent to PostHog
Let's make sure LLM events are being captured and sent to PostHog. Under AI Observability, you should see rows of data appear in the Traces and Generations tabs.
!LLM generations in PostHog!LLM generations in PostHog
Check for LLM events in PostHog
6. 5
Next steps
Recommended
Now that you're capturing AI conversations, continue with the resources below to learn what else AI Observability enables within the PostHog platform.
| Resource | Description |
|---|---|
| Basics | Learn the basics of how LLM calls become events in PostHog. |
| Generations | Read about the $ai_generation event and its properties. |
| Traces | Explore the trace hierarchy and how to use it to debug LLM calls. |
| Spans | Review spans and their role in representing individual operations. |
| Anaylze LLM performance | Learn how to create dashboards to analyze LLM performance. |
Community questions
Ask a question
Was this page useful?
HelpfulCould be better
Groq AI Observability installation - Docs
1. 1
Install dependencies
Required
Full working examples
See the complete Node.js and Python examples on GitHub. If you're using the PostHog SDK wrapper instead of OpenTelemetry, see the Node.js wrapper and Python wrapper examples.
Install the OpenTelemetry SDK, the OpenAI instrumentation, and the OpenAI SDK.
PostHog AI
Python
pip install openai opentelemetry-sdk "posthog[otel]" opentelemetry-instrumentation-openai-v2Node
npm install openai @posthog/ai @opentelemetry/sdk-node @opentelemetry/resources @opentelemetry/instrumentation-openai2. 2
Set up OpenTelemetry tracing
Required
Configure OpenTelemetry to auto-instrument OpenAI SDK calls and export traces to PostHog. PostHog converts gen_ai.* spans into $ai_generation events automatically.
PostHog AI
Python
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.resources import Resource, SERVICE_NAME
from posthog.ai.otel import PostHogSpanProcessor
from opentelemetry.instrumentation.openai_v2 import OpenAIInstrumentor
resource = Resource(attributes={
SERVICE_NAME: "my-app",
"posthog.distinct_id": "user_123", # optional: identifies the user in PostHog
"foo": "bar", # custom properties are passed through
})
provider = TracerProvider(resource=resource)
provider.add_span_processor(
PostHogSpanProcessor(
api_key="<ph_project_token>",
host="https://us.i.posthog.com",
)
)
trace.set_tracer_provider(provider)
OpenAIInstrumentor().instrument()Node
import { NodeSDK } from '@opentelemetry/sdk-node'
import { resourceFromAttributes } from '@opentelemetry/resources'
import { PostHogSpanProcessor } from '@posthog/ai/otel'
import { OpenAIInstrumentation } from '@opentelemetry/instrumentation-openai'
const sdk = new NodeSDK({
resource: resourceFromAttributes({
'service.name': 'my-app',
'posthog.distinct_id': 'user_123', // optional: identifies the user in PostHog
foo: 'bar', // custom properties are passed through
}),
spanProcessors: [
new PostHogSpanProcessor({
apiKey: '<ph_project_token>',
host: 'https://us.i.posthog.com',
}),
],
instrumentations: [new OpenAIInstrumentation()],
})
sdk.start()3. 3
Call Groq
Required
Now, when you use the OpenAI SDK to call Groq, PostHog automatically captures $ai_generation events via the OpenTelemetry instrumentation.
PostHog AI
Python
import openai
client = openai.OpenAI(
base_url="https://api.groq.com/openai/v1",
api_key="<groq_api_key>",
)
response = client.chat.completions.create(
model="llama-3.3-70b-versatile",
max_completion_tokens=1024,
messages=[
{"role": "user", "content": "Tell me a fun fact about hedgehogs"}
],
)
print(response.choices[0].message.content)Node
import OpenAI from 'openai'
const client = new OpenAI({
baseURL: 'https://api.groq.com/openai/v1',
apiKey: '<groq_api_key>',
})
const response = await client.chat.completions.create({
model: 'llama-3.3-70b-versatile',
max_completion_tokens: 1024,
messages: [{ role: 'user', content: 'Tell me a fun fact about hedgehogs' }],
})
console.log(response.choices[0].message.content)Note: If you want to capture LLM events anonymously, omit the posthog.distinct_id resource attribute. See our docs on anonymous vs identified events to learn more.You can expect captured $ai_generation events to have the following properties:
| Property | Description |
|---|---|
| $ai_model | The specific model, like gpt-5-mini or claude-4-sonnet |
| $ai_latency | The latency of the LLM call in seconds |
| $ai_time_to_first_token | Time to first token in seconds (streaming only) |
| $ai_tools | Tools and functions available to the LLM |
| $ai_input | List of messages sent to the LLM |
| $ai_input_tokens | The number of tokens in the input (often found in response.usage) |
| $ai_output_choices | List of response choices from the LLM |
| $ai_output_tokens | The number of tokens in the output (often found in response.usage) |
| $ai_total_cost_usd | The total cost in USD (input + output) |
| [[...]](/docs/ai-observability/generations.md#event-properties) | See full list of properties |
4. ## Verify traces and generations
Recommended
Confirm LLM events are being sent to PostHog
Let's make sure LLM events are being captured and sent to PostHog. Under AI Observability, you should see rows of data appear in the Traces and Generations tabs.
!LLM generations in PostHog!LLM generations in PostHog
Check for LLM events in PostHog
5. 4
Next steps
Recommended
Now that you're capturing AI conversations, continue with the resources below to learn what else AI Observability enables within the PostHog platform.
| Resource | Description |
|---|---|
| Basics | Learn the basics of how LLM calls become events in PostHog. |
| Generations | Read about the $ai_generation event and its properties. |
| Traces | Explore the trace hierarchy and how to use it to debug LLM calls. |
| Spans | Review spans and their role in representing individual operations. |
| Anaylze LLM performance | Learn how to create dashboards to analyze LLM performance. |
Community questions
Ask a question
Was this page useful?
HelpfulCould be better
Helicone AI Observability installation - Docs
1. 1
Install dependencies
Required
Full working examples
See the complete Node.js and Python examples on GitHub. If you're using the PostHog SDK wrapper instead of OpenTelemetry, see the Node.js wrapper and Python wrapper examples.
About Helicone
Helicone is an open-source AI gateway that provides access to 100+ LLM providers through an OpenAI-compatible interface. The Helicone API key handles authentication and routing to your chosen model provider.
Install the OpenTelemetry SDK, the OpenAI instrumentation, and the OpenAI SDK.
PostHog AI
Python
pip install openai opentelemetry-sdk "posthog[otel]" opentelemetry-instrumentation-openai-v2Node
npm install openai @posthog/ai @opentelemetry/sdk-node @opentelemetry/resources @opentelemetry/instrumentation-openai2. 2
Set up OpenTelemetry tracing
Required
Configure OpenTelemetry to auto-instrument OpenAI SDK calls and export traces to PostHog. PostHog converts gen_ai.* spans into $ai_generation events automatically.
PostHog AI
Python
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.resources import Resource, SERVICE_NAME
from posthog.ai.otel import PostHogSpanProcessor
from opentelemetry.instrumentation.openai_v2 import OpenAIInstrumentor
resource = Resource(attributes={
SERVICE_NAME: "my-app",
"posthog.distinct_id": "user_123", # optional: identifies the user in PostHog
"foo": "bar", # custom properties are passed through
})
provider = TracerProvider(resource=resource)
provider.add_span_processor(
PostHogSpanProcessor(
api_key="<ph_project_token>",
host="https://us.i.posthog.com",
)
)
trace.set_tracer_provider(provider)
OpenAIInstrumentor().instrument()Node
import { NodeSDK } from '@opentelemetry/sdk-node'
import { resourceFromAttributes } from '@opentelemetry/resources'
import { PostHogSpanProcessor } from '@posthog/ai/otel'
import { OpenAIInstrumentation } from '@opentelemetry/instrumentation-openai'
const sdk = new NodeSDK({
resource: resourceFromAttributes({
'service.name': 'my-app',
'posthog.distinct_id': 'user_123', // optional: identifies the user in PostHog
foo: 'bar', // custom properties are passed through
}),
spanProcessors: [
new PostHogSpanProcessor({
apiKey: '<ph_project_token>',
host: 'https://us.i.posthog.com',
}),
],
instrumentations: [new OpenAIInstrumentation()],
})
sdk.start()3. 3
Call Helicone
Required
Now, when you call Helicone with the OpenAI SDK, PostHog automatically captures $ai_generation events via the OpenTelemetry instrumentation.
PostHog AI
Python
import openai
client = openai.OpenAI(
base_url="https://ai-gateway.helicone.ai/",
api_key="<helicone_api_key>",
)
response = client.chat.completions.create(
model="gpt-5-mini",
messages=[
{"role": "user", "content": "Tell me a fun fact about hedgehogs"}
],
)
print(response.choices[0].message.content)Node
import OpenAI from 'openai'
const client = new OpenAI({
baseURL: 'https://ai-gateway.helicone.ai/',
apiKey: '<helicone_api_key>',
})
const response = await client.chat.completions.create({
model: 'gpt-5-mini',
messages: [{ role: 'user', content: 'Tell me a fun fact about hedgehogs' }],
})
console.log(response.choices[0].message.content)Note: If you want to capture LLM events anonymously, omit the posthog.distinct_id resource attribute. See our docs on anonymous vs identified events to learn more.You can expect captured $ai_generation events to have the following properties:
| Property | Description |
|---|---|
| $ai_model | The specific model, like gpt-5-mini or claude-4-sonnet |
| $ai_latency | The latency of the LLM call in seconds |
| $ai_time_to_first_token | Time to first token in seconds (streaming only) |
| $ai_tools | Tools and functions available to the LLM |
| $ai_input | List of messages sent to the LLM |
| $ai_input_tokens | The number of tokens in the input (often found in response.usage) |
| $ai_output_choices | List of response choices from the LLM |
| $ai_output_tokens | The number of tokens in the output (often found in response.usage) |
| $ai_total_cost_usd | The total cost in USD (input + output) |
| [[...]](/docs/ai-observability/generations.md#event-properties) | See full list of properties |
4. ## Verify traces and generations
Recommended
Confirm LLM events are being sent to PostHog
Let's make sure LLM events are being captured and sent to PostHog. Under AI Observability, you should see rows of data appear in the Traces and Generations tabs.
!LLM generations in PostHog!LLM generations in PostHog
Check for LLM events in PostHog
5. 4
Next steps
Recommended
Now that you're capturing AI conversations, continue with the resources below to learn what else AI Observability enables within the PostHog platform.
| Resource | Description |
|---|---|
| Basics | Learn the basics of how LLM calls become events in PostHog. |
| Generations | Read about the $ai_generation event and its properties. |
| Traces | Explore the trace hierarchy and how to use it to debug LLM calls. |
| Spans | Review spans and their role in representing individual operations. |
| Anaylze LLM performance | Learn how to create dashboards to analyze LLM performance. |
Community questions
Ask a question
Was this page useful?
HelpfulCould be better
Hugging Face AI Observability installation - Docs
1. 1
Install dependencies
Required
Full working examples
See the complete Node.js and Python examples on GitHub. If you're using the PostHog SDK wrapper instead of OpenTelemetry, see the Node.js wrapper and Python wrapper examples.
Install the OpenTelemetry SDK, the OpenAI instrumentation, and the OpenAI SDK.
PostHog AI
Python
pip install openai opentelemetry-sdk "posthog[otel]" opentelemetry-instrumentation-openai-v2Node
npm install openai @posthog/ai @opentelemetry/sdk-node @opentelemetry/resources @opentelemetry/instrumentation-openai2. 2
Set up OpenTelemetry tracing
Required
Configure OpenTelemetry to auto-instrument OpenAI SDK calls and export traces to PostHog. PostHog converts gen_ai.* spans into $ai_generation events automatically.
PostHog AI
Python
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.resources import Resource, SERVICE_NAME
from posthog.ai.otel import PostHogSpanProcessor
from opentelemetry.instrumentation.openai_v2 import OpenAIInstrumentor
resource = Resource(attributes={
SERVICE_NAME: "my-app",
"posthog.distinct_id": "user_123", # optional: identifies the user in PostHog
"foo": "bar", # custom properties are passed through
})
provider = TracerProvider(resource=resource)
provider.add_span_processor(
PostHogSpanProcessor(
api_key="<ph_project_token>",
host="https://us.i.posthog.com",
)
)
trace.set_tracer_provider(provider)
OpenAIInstrumentor().instrument()Node
import { NodeSDK } from '@opentelemetry/sdk-node'
import { resourceFromAttributes } from '@opentelemetry/resources'
import { PostHogSpanProcessor } from '@posthog/ai/otel'
import { OpenAIInstrumentation } from '@opentelemetry/instrumentation-openai'
const sdk = new NodeSDK({
resource: resourceFromAttributes({
'service.name': 'my-app',
'posthog.distinct_id': 'user_123', // optional: identifies the user in PostHog
foo: 'bar', // custom properties are passed through
}),
spanProcessors: [
new PostHogSpanProcessor({
apiKey: '<ph_project_token>',
host: 'https://us.i.posthog.com',
}),
],
instrumentations: [new OpenAIInstrumentation()],
})
sdk.start()3. 3
Call Hugging Face
Required
Now, when you use the OpenAI SDK to call Hugging Face, PostHog automatically captures $ai_generation events via the OpenTelemetry instrumentation.
PostHog AI
Python
import openai
client = openai.OpenAI(
base_url="https://router.huggingface.co/v1/",
api_key="<huggingface_api_key>",
)
response = client.chat.completions.create(
model="meta-llama/Llama-3.3-70B-Instruct",
max_completion_tokens=1024,
messages=[
{"role": "user", "content": "Tell me a fun fact about hedgehogs"}
],
)
print(response.choices[0].message.content)Node
import OpenAI from 'openai'
const client = new OpenAI({
baseURL: 'https://router.huggingface.co/v1/',
apiKey: '<huggingface_api_key>',
})
const response = await client.chat.completions.create({
model: 'meta-llama/Llama-3.3-70B-Instruct',
max_completion_tokens: 1024,
messages: [{ role: 'user', content: 'Tell me a fun fact about hedgehogs' }],
})
console.log(response.choices[0].message.content)Note: If you want to capture LLM events anonymously, omit the posthog.distinct_id resource attribute. See our docs on anonymous vs identified events to learn more.You can expect captured $ai_generation events to have the following properties:
| Property | Description |
|---|---|
| $ai_model | The specific model, like gpt-5-mini or claude-4-sonnet |
| $ai_latency | The latency of the LLM call in seconds |
| $ai_time_to_first_token | Time to first token in seconds (streaming only) |
| $ai_tools | Tools and functions available to the LLM |
| $ai_input | List of messages sent to the LLM |
| $ai_input_tokens | The number of tokens in the input (often found in response.usage) |
| $ai_output_choices | List of response choices from the LLM |
| $ai_output_tokens | The number of tokens in the output (often found in response.usage) |
| $ai_total_cost_usd | The total cost in USD (input + output) |
| [[...]](/docs/ai-observability/generations.md#event-properties) | See full list of properties |
4. ## Verify traces and generations
Recommended
Confirm LLM events are being sent to PostHog
Let's make sure LLM events are being captured and sent to PostHog. Under AI Observability, you should see rows of data appear in the Traces and Generations tabs.
!LLM generations in PostHog!LLM generations in PostHog
Check for LLM events in PostHog
5. 4
Next steps
Recommended
Now that you're capturing AI conversations, continue with the resources below to learn what else AI Observability enables within the PostHog platform.
| Resource | Description |
|---|---|
| Basics | Learn the basics of how LLM calls become events in PostHog. |
| Generations | Read about the $ai_generation event and its properties. |
| Traces | Explore the trace hierarchy and how to use it to debug LLM calls. |
| Spans | Review spans and their role in representing individual operations. |
| Anaylze LLM performance | Learn how to create dashboards to analyze LLM performance. |
Community questions
Ask a question
Was this page useful?
HelpfulCould be better
Instructor AI Observability installation - Docs
1. 1
Install dependencies
Required
Full working examples
See the complete Node.js and Python examples on GitHub. If you're using the PostHog SDK wrapper instead of OpenTelemetry, see the Node.js wrapper and Python wrapper examples.
Install the OpenTelemetry SDK, the OpenAI instrumentation, and Instructor.
PostHog AI
Python
pip install instructor openai opentelemetry-sdk "posthog[otel]" opentelemetry-instrumentation-openai-v2Node
npm install @instructor-ai/instructor openai zod @posthog/ai @opentelemetry/sdk-node @opentelemetry/resources @opentelemetry/instrumentation-openai2. 2
Set up OpenTelemetry tracing
Required
Configure OpenTelemetry to auto-instrument OpenAI SDK calls and export traces to PostHog. PostHog converts gen_ai.* spans into $ai_generation events automatically.
PostHog AI
Python
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.resources import Resource, SERVICE_NAME
from posthog.ai.otel import PostHogSpanProcessor
from opentelemetry.instrumentation.openai_v2 import OpenAIInstrumentor
resource = Resource(attributes={
SERVICE_NAME: "my-app",
"posthog.distinct_id": "user_123", # optional: identifies the user in PostHog
"foo": "bar", # custom properties are passed through
})
provider = TracerProvider(resource=resource)
provider.add_span_processor(
PostHogSpanProcessor(
api_key="<ph_project_token>",
host="https://us.i.posthog.com",
)
)
trace.set_tracer_provider(provider)
OpenAIInstrumentor().instrument()Node
import { NodeSDK } from '@opentelemetry/sdk-node'
import { resourceFromAttributes } from '@opentelemetry/resources'
import { PostHogSpanProcessor } from '@posthog/ai/otel'
import { OpenAIInstrumentation } from '@opentelemetry/instrumentation-openai'
const sdk = new NodeSDK({
resource: resourceFromAttributes({
'service.name': 'my-app',
'posthog.distinct_id': 'user_123', // optional: identifies the user in PostHog
foo: 'bar', // custom properties are passed through
}),
spanProcessors: [
new PostHogSpanProcessor({
apiKey: '<ph_project_token>',
host: 'https://us.i.posthog.com',
}),
],
instrumentations: [new OpenAIInstrumentation()],
})
sdk.start()3. 3
Extract structured data
Required
Use Instructor to extract structured data from LLM responses. PostHog automatically captures an $ai_generation event for each call made through the OpenAI SDK that Instructor wraps.
PostHog AI
Python
import instructor
import openai
from pydantic import BaseModel
class User(BaseModel):
name: str
age: int
client = instructor.from_openai(openai.OpenAI(api_key="your_openai_api_key"))
user = client.chat.completions.create(
model="gpt-4o-mini",
response_model=User,
messages=[{"role": "user", "content": "Extract: John is 30 years old"}],
)
print(user)Node
import OpenAI from 'openai'
import Instructor from '@instructor-ai/instructor'
import { z } from 'zod'
const oai = new OpenAI({ apiKey: 'your_openai_api_key' })
const client = Instructor({ client: oai, mode: 'TOOLS' })
const UserSchema = z.object({
name: z.string(),
age: z.number(),
})
const user = await client.chat.completions.create({
model: 'gpt-4o-mini',
response_model: { schema: UserSchema, name: 'User' },
messages: [{ role: 'user', content: 'Extract: John is 30 years old' }],
})
console.log(user)Note: If you want to capture LLM events anonymously, omit the posthog.distinct_id resource attribute. See our docs on anonymous vs identified events to learn more.You can expect captured $ai_generation events to have the following properties:
| Property | Description |
|---|---|
| $ai_model | The specific model, like gpt-5-mini or claude-4-sonnet |
| $ai_latency | The latency of the LLM call in seconds |
| $ai_time_to_first_token | Time to first token in seconds (streaming only) |
| $ai_tools | Tools and functions available to the LLM |
| $ai_input | List of messages sent to the LLM |
| $ai_input_tokens | The number of tokens in the input (often found in response.usage) |
| $ai_output_choices | List of response choices from the LLM |
| $ai_output_tokens | The number of tokens in the output (often found in response.usage) |
| $ai_total_cost_usd | The total cost in USD (input + output) |
| [[...]](/docs/ai-observability/generations.md#event-properties) | See full list of properties |
4. ## Verify traces and generations
Recommended
Confirm LLM events are being sent to PostHog
Let's make sure LLM events are being captured and sent to PostHog. Under AI Observability, you should see rows of data appear in the Traces and Generations tabs.
!LLM generations in PostHog!LLM generations in PostHog
Check for LLM events in PostHog
5. 4
Next steps
Recommended
Now that you're capturing AI conversations, continue with the resources below to learn what else AI Observability enables within the PostHog platform.
| Resource | Description |
|---|---|
| Basics | Learn the basics of how LLM calls become events in PostHog. |
| Generations | Read about the $ai_generation event and its properties. |
| Traces | Explore the trace hierarchy and how to use it to debug LLM calls. |
| Spans | Review spans and their role in representing individual operations. |
| Anaylze LLM performance | Learn how to create dashboards to analyze LLM performance. |
Community questions
Ask a question
Was this page useful?
HelpfulCould be better
LangChain AI Observability installation - Docs
1. 1
Install dependencies
Required
Full working examples
See the complete Node.js and Python examples on GitHub. If you're using the PostHog SDK wrapper instead of OpenTelemetry, see the Node.js wrapper and Python wrapper examples.
Install the OpenTelemetry SDK, the LangChain instrumentation, and LangChain with OpenAI.
PostHog AI
Python
pip install langchain langchain-core langchain-openai opentelemetry-sdk "posthog[otel]" opentelemetry-instrumentation-langchainNode
npm install langchain @langchain/core @langchain/openai @posthog/ai @opentelemetry/sdk-node @opentelemetry/resources @traceloop/instrumentation-langchain2. 2
Set up OpenTelemetry tracing
Required
Configure OpenTelemetry to auto-instrument LangChain calls and export traces to PostHog. PostHog converts gen_ai.* spans into $ai_generation events automatically.
PostHog AI
Python
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.resources import Resource, SERVICE_NAME
from posthog.ai.otel import PostHogSpanProcessor
from opentelemetry.instrumentation.langchain import LangchainInstrumentor
resource = Resource(attributes={
SERVICE_NAME: "my-app",
"posthog.distinct_id": "user_123", # optional: identifies the user in PostHog
"foo": "bar", # custom properties are passed through
})
provider = TracerProvider(resource=resource)
provider.add_span_processor(
PostHogSpanProcessor(
api_key="<ph_project_token>",
host="https://us.i.posthog.com",
)
)
trace.set_tracer_provider(provider)
LangchainInstrumentor().instrument()Node
import { NodeSDK } from '@opentelemetry/sdk-node'
import { resourceFromAttributes } from '@opentelemetry/resources'
import { PostHogSpanProcessor } from '@posthog/ai/otel'
import { LangChainInstrumentation } from '@traceloop/instrumentation-langchain'
const sdk = new NodeSDK({
resource: resourceFromAttributes({
'service.name': 'my-app',
'posthog.distinct_id': 'user_123', // optional: identifies the user in PostHog
foo: 'bar', // custom properties are passed through
}),
spanProcessors: [
new PostHogSpanProcessor({
apiKey: '<ph_project_token>',
host: 'https://us.i.posthog.com',
}),
],
instrumentations: [new LangChainInstrumentation()],
})
sdk.start()3. 3
Call LangChain
Required
Use LangChain as normal. The OpenTelemetry instrumentation automatically captures $ai_generation events for each LLM call — no callback handlers needed.
PostHog AI
Python
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
prompt = ChatPromptTemplate.from_messages([
("system", "You are a helpful assistant."),
("user", "{input}")
])
model = ChatOpenAI(openai_api_key="your_openai_api_key")
chain = prompt | model
response = chain.invoke({"input": "Tell me a joke about programming"})
print(response.content)Node
import { ChatOpenAI } from '@langchain/openai'
import { ChatPromptTemplate } from '@langchain/core/prompts'
const prompt = ChatPromptTemplate.fromMessages([
["system", "You are a helpful assistant."],
["user", "{input}"]
])
const model = new ChatOpenAI({ apiKey: "your_openai_api_key" })
const chain = prompt.pipe(model)
const response = await chain.invoke({ input: "Tell me a joke about programming" })
console.log(response.content)Note: If you want to capture LLM events anonymously, omit the posthog.distinct_id resource attribute. See our docs on anonymous vs identified events to learn more.PostHog automatically captures an $ai_generation event along with these properties:
| Property | Description |
|---|---|
| $ai_model | The specific model, like gpt-5-mini or claude-4-sonnet |
| $ai_latency | The latency of the LLM call in seconds |
| $ai_time_to_first_token | Time to first token in seconds (streaming only) |
| $ai_tools | Tools and functions available to the LLM |
| $ai_input | List of messages sent to the LLM |
| $ai_input_tokens | The number of tokens in the input (often found in response.usage) |
| $ai_output_choices | List of response choices from the LLM |
| $ai_output_tokens | The number of tokens in the output (often found in response.usage) |
| $ai_total_cost_usd | The total cost in USD (input + output) |
| [[...]](/docs/ai-observability/generations.md#event-properties) | See full list of properties |
It also automatically creates a trace hierarchy based on how LangChain components are nested.
4. ## Verify traces and generations
Recommended
Confirm LLM events are being sent to PostHog
Let's make sure LLM events are being captured and sent to PostHog. Under AI Observability, you should see rows of data appear in the Traces and Generations tabs.
!LLM generations in PostHog!LLM generations in PostHog
Check for LLM events in PostHog
5. 4
Next steps
Recommended
Now that you're capturing AI conversations, continue with the resources below to learn what else AI Observability enables within the PostHog platform.
| Resource | Description |
|---|---|
| Basics | Learn the basics of how LLM calls become events in PostHog. |
| Generations | Read about the $ai_generation event and its properties. |
| Traces | Explore the trace hierarchy and how to use it to debug LLM calls. |
| Spans | Review spans and their role in representing individual operations. |
| Anaylze LLM performance | Learn how to create dashboards to analyze LLM performance. |
Community questions
Ask a question
Was this page useful?
HelpfulCould be better
LangGraph AI Observability installation - Docs
1. 1
Install dependencies
Required
Full working examples
See the complete Node.js and Python examples on GitHub. If you're using the PostHog SDK wrapper instead of OpenTelemetry, see the Node.js wrapper and Python wrapper examples.
Install the OpenTelemetry SDK, the LangChain instrumentation, and LangGraph with OpenAI.
PostHog AI
Python
pip install langgraph langchain-core langchain-openai opentelemetry-sdk "posthog[otel]" opentelemetry-instrumentation-langchainNode
npm install @langchain/langgraph @langchain/openai @langchain/core zod @posthog/ai @opentelemetry/sdk-node @opentelemetry/resources @traceloop/instrumentation-langchain2. 2
Set up OpenTelemetry tracing
Required
Configure OpenTelemetry to auto-instrument LangChain calls and export traces to PostHog. LangGraph is built on LangChain, so the same instrumentation captures all LLM calls. PostHog converts gen_ai.* spans into $ai_generation events automatically.
PostHog AI
Python
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.resources import Resource, SERVICE_NAME
from posthog.ai.otel import PostHogSpanProcessor
from opentelemetry.instrumentation.langchain import LangchainInstrumentor
resource = Resource(attributes={
SERVICE_NAME: "my-app",
"posthog.distinct_id": "user_123", # optional: identifies the user in PostHog
"foo": "bar", # custom properties are passed through
})
provider = TracerProvider(resource=resource)
provider.add_span_processor(
PostHogSpanProcessor(
api_key="<ph_project_token>",
host="https://us.i.posthog.com",
)
)
trace.set_tracer_provider(provider)
LangchainInstrumentor().instrument()Node
import { NodeSDK } from '@opentelemetry/sdk-node'
import { resourceFromAttributes } from '@opentelemetry/resources'
import { PostHogSpanProcessor } from '@posthog/ai/otel'
import { LangChainInstrumentation } from '@traceloop/instrumentation-langchain'
const sdk = new NodeSDK({
resource: resourceFromAttributes({
'service.name': 'my-app',
'posthog.distinct_id': 'user_123', // optional: identifies the user in PostHog
foo: 'bar', // custom properties are passed through
}),
spanProcessors: [
new PostHogSpanProcessor({
apiKey: '<ph_project_token>',
host: 'https://us.i.posthog.com',
}),
],
instrumentations: [new LangChainInstrumentation()],
})
sdk.start()3. 3
Run your graph
Required
Use LangGraph as normal. The OpenTelemetry instrumentation automatically captures $ai_generation events for each LLM call — no callback handlers needed.
PostHog AI
Python
from langgraph.prebuilt import create_react_agent
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
@tool
def get_weather(city: str) -> str:
"""Get the weather for a given city."""
return f"It's always sunny in {city}!"
model = ChatOpenAI(api_key="your_openai_api_key")
agent = create_react_agent(model, tools=[get_weather])
result = agent.invoke(
{"messages": [{"role": "user", "content": "What's the weather in Paris?"}]}
)
print(result["messages"][-1].content)Node
import { createReactAgent } from '@langchain/langgraph/prebuilt';
import { ChatOpenAI } from '@langchain/openai';
import { tool } from '@langchain/core/tools';
import { z } from 'zod';
const getWeather = tool(
(input) => `It's always sunny in ${input.city}!`,
{
name: 'get_weather',
description: 'Get the weather for a given city',
schema: z.object({
city: z.string().describe('The city to get the weather for'),
}),
}
);
const model = new ChatOpenAI({ apiKey: 'your_openai_api_key' });
const agent = createReactAgent({ llm: model, tools: [getWeather] });
const result = await agent.invoke(
{ messages: [{ role: 'user', content: "What's the weather in Paris?" }] }
);
console.log(result.messages[result.messages.length - 1].content);Note: If you want to capture LLM events anonymously, omit the posthog.distinct_id resource attribute. See our docs on anonymous vs identified events to learn more.PostHog automatically captures $ai_generation events and creates a trace hierarchy based on how LangGraph components are nested. You can expect captured events to have the following properties:
| Property | Description |
|---|---|
| $ai_model | The specific model, like gpt-5-mini or claude-4-sonnet |
| $ai_latency | The latency of the LLM call in seconds |
| $ai_time_to_first_token | Time to first token in seconds (streaming only) |
| $ai_tools | Tools and functions available to the LLM |
| $ai_input | List of messages sent to the LLM |
| $ai_input_tokens | The number of tokens in the input (often found in response.usage) |
| $ai_output_choices | List of response choices from the LLM |
| $ai_output_tokens | The number of tokens in the output (often found in response.usage) |
| $ai_total_cost_usd | The total cost in USD (input + output) |
| [[...]](/docs/ai-observability/generations.md#event-properties) | See full list of properties |
4. ## Verify traces and generations
Recommended
Confirm LLM events are being sent to PostHog
Let's make sure LLM events are being captured and sent to PostHog. Under AI Observability, you should see rows of data appear in the Traces and Generations tabs.
!LLM generations in PostHog!LLM generations in PostHog
Check for LLM events in PostHog
5. 4
Next steps
Recommended
Now that you're capturing AI conversations, continue with the resources below to learn what else AI Observability enables within the PostHog platform.
| Resource | Description |
|---|---|
| Basics | Learn the basics of how LLM calls become events in PostHog. |
| Generations | Read about the $ai_generation event and its properties. |
| Traces | Explore the trace hierarchy and how to use it to debug LLM calls. |
| Spans | Review spans and their role in representing individual operations. |
| Anaylze LLM performance | Learn how to create dashboards to analyze LLM performance. |
Community questions
Ask a question
Was this page useful?
HelpfulCould be better
PostHog.AI for .NET
[!WARNING]
PostHog.AI is currently pre-release. We're making it available publicly to solicit feedback, but there will be breaking changes until it reaches a stable release.Please see the main PostHog docs.
SDK usage examples and code snippets live in the official documentation so they stay up to date.
Documentation
Related skills
FAQ
What does instrument-llm-analytics do?
instrument-llm-analytics is a Claude Code skill for ai & agent building.
When should I use instrument-llm-analytics?
When you need to helps with ai & agent building tasks during AI-assisted development., or when instrument-llm-analytics is a claude code skill for ai & agent building.
What are the main capabilities?
instrument-llm-analytics; AI & Agent Building; AI-coding skill.