Files
Richard SolomouandGitHub 64ba499633 feat(examples): migrate AI examples to OpenTelemetry instrumentation (#3349)
## Problem

Our AI examples use PostHog's direct SDK wrappers (`@posthog/ai/openai`, `@posthog/ai/anthropic`, etc.) for tracking LLM calls. We want to silently deprecate these in favor of standard OpenTelemetry auto-instrumentation, which is more portable and follows industry conventions.

## Changes

Migrates 23 Node.js AI examples from PostHog direct wrappers to OpenTelemetry auto-instrumentation (or framework-native integrations where no OTel option exists):

- **15 OpenAI-compatible providers** (Groq, DeepSeek, Mistral, xAI, Together AI, Ollama, Cohere, Hugging Face, Perplexity, Cerebras, Fireworks AI, OpenRouter, Helicone, Vercel AI Gateway, Portkey) → `@opentelemetry/instrumentation-openai`
- **OpenAI** (all 7 files), **Azure OpenAI**, **Instructor** → `@opentelemetry/instrumentation-openai`
- **Anthropic** (chat + streaming) → `@traceloop/instrumentation-anthropic`
- **Vercel AI SDK** (8 files) → built-in `experimental_telemetry`
- **LangChain**, **LangGraph** → `@traceloop/instrumentation-langchain`
- **Mastra** → `@mastra/posthog` exporter (Mastra's native PostHog integration — no mature OTel path for Mastra yet)

All OTel-based examples set resource attributes to demonstrate the full feature set:

```ts
resource: resourceFromAttributes({
  'service.name': 'example-groq-app',
  'posthog.distinct_id': 'example-user',
  foo: 'bar',
  'conversation_id': 'abc-123',
}),
```

These map to `distinct_id` and custom event properties via PostHog's OTLP ingestion endpoint.

**Kept as-is:** Gemini (no Node.js OTel for `@google/genai`), AWS Bedrock/Convex/OTel (already OTel-based).

Key implementation details:

- Uses `SimpleSpanProcessor` instead of `BatchSpanProcessor` so spans export immediately without needing `sdk.shutdown()` (with an inline comment explaining why)
- Uses `resourceFromAttributes` from `@opentelemetry/resources` v2
- Static imports (no dynamic `await import()`)
- OTel deps at `^0.200.0` / `^2.0.0` for consistent resolution
- `example-ai-otel/package.json` bumped to matching OTel versions per reviewer feedback

## Release info Sub-libraries affected

### Libraries affected

- [ ] All of them
- [ ] posthog-js (web)
- [ ] posthog-js-lite (web lite)
- [ ] posthog-node
- [ ] posthog-react-native
- [ ] @posthog/react
- [ ] @posthog/ai
- [ ] @posthog/convex
- [ ] @posthog/next
- [ ] @posthog/nextjs-config
- [ ] @posthog/nuxt
- [ ] @posthog/rollup-plugin
- [ ] @posthog/webpack-plugin
- [ ] @posthog/types

No library version bumps — this only touches `examples/`.

## Checklist

- [ ] Tests for new code
- [x] Accounted for the impact of any changes across different platforms
- [x] Accounted for backwards compatibility of any changes (no breaking changes!)
- [x] Took care not to unnecessarily increase the bundle size

### If releasing new changes

- [ ] Ran `pnpm changeset` to generate a changeset file
- [ ] Added the "release" label to the PR to indicate we're publishing new versions for the affected packages
2026-04-14 14:19:29 +03:00

69 lines
2.3 KiB
TypeScript

/** Mastra agent with PostHog tracking via the official @mastra/posthog exporter. */
import { Mastra } from '@mastra/core'
import { Agent } from '@mastra/core/agent'
import { createTool } from '@mastra/core/tools'
import { Observability } from '@mastra/observability'
import { PosthogExporter } from '@mastra/posthog'
import { z } from 'zod'
const weatherTool = createTool({
id: 'get_weather',
description: 'Get current weather for a location',
inputSchema: z.object({
latitude: z.number(),
longitude: z.number(),
location_name: z.string(),
}),
execute: async ({ latitude, longitude, location_name }) => {
const url = `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}&current=temperature_2m,relative_humidity_2m,wind_speed_10m`
const resp = await fetch(url)
const data = await resp.json()
const current = data.current
return `Weather in ${location_name}: ${current.temperature_2m}°C, humidity ${current.relative_humidity_2m}%, wind ${current.wind_speed_10m} km/h`
},
})
const weatherAgent = new Agent({
id: 'weather-agent',
name: 'Weather Agent',
instructions: 'You are a helpful assistant with access to weather data.',
model: { id: 'openai/gpt-5-mini' },
tools: { get_weather: weatherTool },
})
const mastra = new Mastra({
agents: { weatherAgent },
observability: new Observability({
configs: {
posthog: {
serviceName: 'example-mastra-app',
exporters: [
new PosthogExporter({
apiKey: process.env.POSTHOG_API_KEY!,
host: process.env.POSTHOG_HOST || 'https://us.i.posthog.com',
defaultDistinctId: 'example-user',
}),
],
},
},
}),
})
async function main() {
const agent = mastra.getAgent('weatherAgent')
const result = await agent.generate("What's the weather like in Dublin, Ireland?", {
tracingOptions: {
metadata: {
userId: 'example-user',
sessionId: 'session-abc-123',
foo: 'bar',
conversation_id: 'abc-123',
},
},
})
console.log(result.text)
}
main()