Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions contents/docs/ai-engineering/ai-wizard.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ Running `npx @posthog/wizard` starts the default integration flow. The wizard al
| `wizard audit <subcommand>` | Audit an existing integration – see [Audit subcommands](#audit-subcommands) |
| `wizard revenue-analytics` | Wire up Stripe + PostHog revenue analytics |
| `wizard mcp-analytics` | Instrument your own MCP server with [MCP Analytics](/docs/mcp-analytics) |
| `wizard ai-observability` | Instrument your LLM calls with [AI Observability](/docs/ai-observability) |
| `wizard migrate` | Migrate from another analytics or feature-flag vendor |
| `wizard upload-source-maps` | Upload source maps to PostHog Error Tracking |
| `wizard mcp add` / `wizard mcp remove` / `wizard mcp tutorial` | Manage the PostHog MCP server for your AI agent, or explore the MCP tutorial |
Expand Down
12 changes: 11 additions & 1 deletion contents/docs/ai-observability/installation/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,18 @@ title: Install AI Observability with PostHog
---

import LLMAnalyticsInstallationPlatforms from '../_snippets/llms-installation-platforms'
import WizardCommand from 'components/WizardCommand'

## AI wizard

Install PostHog in seconds with our wizard by running this command in your project directory with your terminal (it also works for [LLM coding agents](/blog/envoy-wizard-llm-agent) like Cursor and Bolt):

<WizardCommand command="ai-observability" />

Wait for it to finish and test the setup once the wizard is complete.

## Platforms

Choose your platform below to get started with AI Observability installation.

<LLMAnalyticsInstallationPlatforms columns={3} />

7 changes: 6 additions & 1 deletion contents/docs/ai-observability/start-here.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { QuestLog, QuestLogItem } from 'components/Docs/QuestLog'
import { IconGraph, IconWarning, IconRewindPlay } from '@posthog/icons'
import LLMsInstallationPlatforms from './_snippets/llms-installation-platforms.tsx'
import OSButton from 'components/OSButton'
import WizardCommand from 'components/WizardCommand'


<QuestLog firstSpeechBubble="Let's get started!" lastSpeechBubble="Time to integrate AI Observability!">
Expand All @@ -28,7 +29,11 @@ AI Observability gives you x-ray vision into your LLM applications. You can trac

> **New to LLM observability?** See [core concepts](/docs/ai-observability/basics) for a primer on events, tokens, and traces.

The first step is to install a PostHog SDK to capture conversations, requests, and responses from an LLM provider.
The first step is to install a PostHog SDK to capture conversations, requests, and responses from an LLM provider. The fastest way is our wizard, which installs the SDK and instruments your LLM calls for you (it also works for [LLM coding agents](/blog/envoy-wizard-llm-agent) like Cursor and Bolt):

<WizardCommand command="ai-observability" />

Prefer to do it by hand? Choose your platform below:

### Platforms

Expand Down
18 changes: 18 additions & 0 deletions src/components/AIObservabilityWizardHint/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React from 'react'
import WizardHint from 'components/WizardHint'

/**
* WizardHint preconfigured for AI observability: pushes `npx @posthog/wizard ai-observability`,
* which installs the SDK and instruments a project's LLM calls instead of setting them up by hand.
*/
export default function AIObservabilityWizardHint({ className = '' }: { className?: string }): JSX.Element {
return (
<WizardHint
className={className}
command="ai-observability"
dismissKey="ai-observability-wizard-hint"
title="Let AI instrument your LLM calls for you"
subtitle="Skip the manual setup — run this in your project and the wizard installs the SDK and wires up AI Observability for you."
/>
)
}
69 changes: 12 additions & 57 deletions src/components/WarehouseWizardHint/index.tsx
Original file line number Diff line number Diff line change
@@ -1,65 +1,20 @@
import React from 'react'
import { IconX } from '@posthog/icons'
import CloudinaryImage from 'components/CloudinaryImage'
import WizardCommand from 'components/WizardCommand'

// Persist dismissal so the hint doesn't nag a user who has already seen it. Mirrors the
// product app's WarehouseWizardHint, which uses the same localStorage key.
// theme-init.js sets `warehouse-wizard-hint-dismissed` on <html> before paint when this
// key is set; global.css hides `.warehouse-wizard-hint` when that class is present.
const DISMISSED_KEY = 'warehouse-wizard-hint-dismissed'
const DISMISSED_CLASS = 'warehouse-wizard-hint-dismissed'
import WizardHint from 'components/WizardHint'

/**
* Agent-flavored nudge that pushes the `npx @posthog/wizard warehouse` CLI, which auto-detects
* and connects a user's databases/APIs straight from their codebase instead of setting up a
* source by hand. Visual design matches [`WizardCTA`](../WizardCTA).
* WizardHint preconfigured for the data warehouse: pushes `npx @posthog/wizard warehouse`, which
* auto-detects and connects a user's databases/APIs straight from their codebase instead of
* setting up a source by hand. The dismissal key mirrors the product app's WarehouseWizardHint,
* which uses the same localStorage key.
*/
export default function WarehouseWizardHint({ className = '' }: { className?: string }): JSX.Element {
const handleDismiss = () => {
localStorage.setItem(DISMISSED_KEY, '1')
document.documentElement.classList.add(DISMISSED_CLASS)
}

return (
<div className={`warehouse-wizard-hint relative ${className}`}>
<button
type="button"
onClick={handleDismiss}
aria-label="Dismiss"
className="absolute cursor-pointer rounded-full bg-white dark:bg-secondary p-1 top-1 right-1 translate-x-1/2 -translate-y-1/2 z-10 text-secondary hover:text-primary border border-secondary"
>
<IconX className="size-4" />
</button>
<div className="relative overflow-hidden rounded not-prose border border-secondary">
<CloudinaryImage
src="https://res.cloudinary.com/dmukukwp6/image/upload/texture_tan_9608fcca70.png"
className="dark:hidden absolute inset-0 -bottom-12"
imgClassName="h-full w-full object-cover"
/>
<CloudinaryImage
src="https://res.cloudinary.com/dmukukwp6/image/upload/texture_tan_dark_a92b0e022d.png"
className="hidden dark:block absolute inset-0 -bottom-12"
imgClassName="h-full w-full object-cover"
/>
<div className="relative flex flex-col-reverse @lg:flex-row items-center justify-between pl-5 @lg:pl-8 pr-8 py-4 @lg:py-3 gap-4">
<div className="flex-1 text-center @lg:text-left max-w-lg">
<p className="text-lg font-bold !mb-0">Let AI connect your sources for you</p>
<p className="!mt-1 !mb-3 text-sm opacity-75">
Skip the manual setup — run this in your project and the wizard auto-detects your databases
and APIs and connects them to PostHog.
</p>
<WizardCommand command="warehouse" />
</div>
<div className="shrink-0">
<img
src="https://res.cloudinary.com/dmukukwp6/image/upload/wizard_3f8bb7a240.png"
alt="PostHog Wizard hedgehog"
className="w-36 @lg:w-32 @xl:w-40 @2xl:w-48"
/>
</div>
</div>
</div>
</div>
<WizardHint
className={className}
command="warehouse"
dismissKey="warehouse-wizard-hint"
title="Let AI connect your sources for you"
subtitle="Skip the manual setup — run this in your project and the wizard auto-detects your databases and APIs and connects them to PostHog."
/>
)
}
40 changes: 40 additions & 0 deletions src/components/WizardHint/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# WizardHint

Collapsible banner that nudges readers toward a `npx @posthog/wizard <command>` CLI flow as the automated alternative to manual setup. Visual design matches `WizardCTA` (wizard hedgehog, tan texture background, inline copyable command). Closing it collapses to a slim bar (hedgehog + title + chevron) that expands back to the full banner on click, so the command stays reachable.

## Usage

```tsx
import WizardHint from 'components/WizardHint'

<WizardHint
command="warehouse"
title="Let AI connect your sources for you"
subtitle="Skip the manual setup — run this in your project and the wizard auto-detects your databases and APIs and connects them to PostHog."
dismissKey="warehouse-wizard-hint"
/>
```

Prefer the preconfigured variants where one exists:

- `components/WarehouseWizardHint` — data warehouse sources (`npx @posthog/wizard warehouse`). Rendered by `templates/DataWarehouseSource.tsx`, `templates/Handbook.tsx` (data warehouse source paths), and `pages/context-warehouse/sources.tsx`.
- `components/AIObservabilityWizardHint` — AI observability SDK installation (`npx @posthog/wizard ai-observability`). Rendered by `templates/Handbook.tsx` on `/docs/ai-observability/installation/*` pages.

## Props

| Prop | Type | Description |
| --- | --- | --- |
| `command` | `string` | Wizard subcommand appended to `npx @posthog/wizard` |
| `title` | `string` | Bold headline |
| `subtitle` | `string` | Supporting line under the headline |
| `dismissKey` | `string` | Base name for the collapse wiring (see below) |
| `className` | `string?` | Extra classes on the outer wrapper |

## Collapse wiring

Collapsing stores `${dismissKey}-dismissed = '1'` in localStorage and adds the same class to `<html>`; expanding removes both. Both states are always rendered — CSS keyed on the `<html>` class decides which one shows, so the collapsed state applies before first paint with no hydration flash. Each variant needs two static registrations:

1. `static/scripts/theme-init.js` — add the key to the dismissed-hint list so the `<html>` class is set pre-paint.
2. `src/styles/global.css` — extend the two `.wizard-hint-full` / `.wizard-hint-collapsed` rule groups with the new variant's `html.<dismissKey>-dismissed .<dismissKey>` selectors.

The `warehouse-wizard-hint` key mirrors the product app's hint, which shares the same localStorage key.
94 changes: 94 additions & 0 deletions src/components/WizardHint/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import React from 'react'
import { IconX, IconChevronDown } from '@posthog/icons'
import CloudinaryImage from 'components/CloudinaryImage'
import WizardCommand from 'components/WizardCommand'

const HEDGEHOG_SRC = 'https://res.cloudinary.com/dmukukwp6/image/upload/wizard_3f8bb7a240.png'

/**
* Collapsible agent-flavored nudge that pushes a `npx @posthog/wizard <command>` CLI flow as the
* automated alternative to manual setup. Visual design matches [`WizardCTA`](../WizardCTA).
*
* Collapsing persists in localStorage under `${dismissKey}-dismissed`. theme-init.js reads that key
* before first paint and sets the same class on <html>; per-variant rules in global.css swap the
* full banner for the slim re-expandable bar (both are always rendered, so there is no hydration
* flash in either state). Adding a new variant means wiring the new key in both places — see the
* README.
*/
export default function WizardHint({
command,
title,
subtitle,
dismissKey,
className = '',
}: {
/** Wizard subcommand, e.g. "warehouse" or "ai-observability" */
command: string
title: string
subtitle: string
/** Base name for the collapse localStorage key, <html> class, and wrapper class */
dismissKey: string
className?: string
}): JSX.Element {
const handleCollapse = () => {
localStorage.setItem(`${dismissKey}-dismissed`, '1')
document.documentElement.classList.add(`${dismissKey}-dismissed`)
}

const handleExpand = () => {
localStorage.removeItem(`${dismissKey}-dismissed`)
document.documentElement.classList.remove(`${dismissKey}-dismissed`)
}

return (
<div className={`${dismissKey} ${className}`}>
<div className="wizard-hint-full relative">
<button
type="button"
onClick={handleCollapse}
aria-label="Collapse"
className="absolute cursor-pointer rounded-full bg-white dark:bg-secondary p-1 top-1 right-1 translate-x-1/2 -translate-y-1/2 z-10 text-secondary hover:text-primary border border-secondary"
>
<IconX className="size-4" />
</button>
<div className="relative overflow-hidden rounded not-prose border border-secondary">
<CloudinaryImage
src="https://res.cloudinary.com/dmukukwp6/image/upload/texture_tan_9608fcca70.png"
className="dark:hidden absolute inset-0 -bottom-12"
imgClassName="h-full w-full object-cover"
/>
<CloudinaryImage
src="https://res.cloudinary.com/dmukukwp6/image/upload/texture_tan_dark_a92b0e022d.png"
className="hidden dark:block absolute inset-0 -bottom-12"
imgClassName="h-full w-full object-cover"
/>
<div className="relative flex flex-col-reverse @lg:flex-row items-center justify-between pl-5 @lg:pl-8 pr-8 py-4 @lg:py-3 gap-4">
<div className="flex-1 text-center @lg:text-left max-w-lg">
<p className="text-lg font-bold !mb-0">{title}</p>
<p className="!mt-1 !mb-3 text-sm opacity-75">{subtitle}</p>
<WizardCommand command={command} />
</div>
<div className="shrink-0">
<img
src={HEDGEHOG_SRC}
alt="PostHog Wizard hedgehog"
className="w-36 @lg:w-32 @xl:w-40 @2xl:w-48"
/>
</div>
</div>
</div>
</div>
{/* Slim bar shown in place of the banner once collapsed. Hidden by default and toggled
to flex by the global.css rules — no `flex` utility here or it would always show. */}
<button
type="button"
onClick={handleExpand}
className="wizard-hint-collapsed w-full items-center gap-3 rounded not-prose border border-secondary px-4 py-1.5 text-left cursor-pointer text-secondary hover:text-primary hover:border-primary"
>
<img src={HEDGEHOG_SRC} alt="" className="h-8 w-8 object-contain" />
<span className="flex-1 text-sm font-semibold">{title}</span>
<IconChevronDown className="size-4" />
</button>
</div>
)
}
2 changes: 1 addition & 1 deletion src/hooks/productData/ai_observability.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export const aiObservability = {
forumTopicId: 390,
color: 'purple',
colorSecondary: 'green-2',
wizardSupport: 'Coming soon',
wizardSupport: true,
pricingDescription:
'Generations, spans, and traces are captured as regular PostHog events and billed like them – no per-seat pricing, and no markup on the tokens you already pay your model provider for.',
seo: {
Expand Down
4 changes: 2 additions & 2 deletions src/navs/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3705,8 +3705,8 @@ export const docsMenu = {
url: '/docs/ai-engineering/skills-store',
},
{
name: 'AI Observability',
url: 'https://posthog.com/docs/ai-observability',
name: 'AI Observability',
url: '/docs/ai-observability',
},
{
name: 'Markdown and llms.txt',
Expand Down
15 changes: 13 additions & 2 deletions src/styles/global.css
Original file line number Diff line number Diff line change
Expand Up @@ -3152,7 +3152,18 @@ div[data-radix-popper-content-wrapper] {
}
}

/* Set on <html> by theme-init.js before paint when the hint was previously dismissed. */
html.warehouse-wizard-hint-dismissed .warehouse-wizard-hint {
/* Set on <html> by theme-init.js before paint when the hint was previously collapsed.
Collapsing swaps the WizardHint's full banner for its slim re-expandable bar. */
.wizard-hint-collapsed {
display: none;
}

html.warehouse-wizard-hint-dismissed .warehouse-wizard-hint .wizard-hint-full,
html.ai-observability-wizard-hint-dismissed .ai-observability-wizard-hint .wizard-hint-full {
display: none;
}

html.warehouse-wizard-hint-dismissed .warehouse-wizard-hint .wizard-hint-collapsed,
html.ai-observability-wizard-hint-dismissed .ai-observability-wizard-hint .wizard-hint-collapsed {
display: flex;
}
14 changes: 13 additions & 1 deletion src/templates/Handbook.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import IsEU from 'components/IsEU'
import IsUS from 'components/IsUS'
import { CallToAction } from 'components/CallToAction'
import WarehouseWizardHint from 'components/WarehouseWizardHint'
import AIObservabilityWizardHint from 'components/AIObservabilityWizardHint'
import Tooltip from 'components/Tooltip'
import NewsletterForm from 'components/NewsletterForm'
import { MDXRenderer } from 'gatsby-plugin-mdx'
Expand Down Expand Up @@ -364,6 +365,14 @@ export default function Handbook({ data: { post, postHogSource }, pageContext: {
pathname === '/docs/data-warehouse/sources' ||
pathname.startsWith('/docs/data-warehouse/sources/')

// Same nudge for AI observability SDK installation pages. The coding-agent integrations
// (Claude Code, OpenClaw, Pi) are excluded: they instrument the agent itself, so running
// the wizard in a project doesn't set them up. The installation index has its own
// AI wizard section in the MDX.
const showAIObservabilityWizardHint =
pathname.startsWith('/docs/ai-observability/installation/') &&
!['claude-code', 'openclaw', 'pi'].includes(pathname.split('/').filter(Boolean).pop() ?? '')

// Track product interest for cross-subdomain cookie
useProductInterestFromPathname(slug)

Expand Down Expand Up @@ -447,7 +456,10 @@ export default function Handbook({ data: { post, postHogSource }, pageContext: {
: null),
}}
title={title}
belowTitle={showWarehouseWizardHint && <WarehouseWizardHint />}
belowTitle={
(showWarehouseWizardHint && <WarehouseWizardHint />) ||
(showAIObservabilityWizardHint && <AIObservabilityWizardHint />)
}
tableOfContents={frontmatterTableOfContents || tableOfContents}
mdxComponents={components}
commits={commits}
Expand Down
10 changes: 6 additions & 4 deletions static/scripts/theme-init.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,12 @@
)
} catch (err) {}

// Hide dismissed WarehouseWizardHint before first paint
// Hide dismissed WizardHint variants before first paint
try {
if (localStorage.getItem('warehouse-wizard-hint-dismissed') === '1') {
document.documentElement.classList.add('warehouse-wizard-hint-dismissed')
}
;['warehouse-wizard-hint-dismissed', 'ai-observability-wizard-hint-dismissed'].forEach(function (key) {
if (localStorage.getItem(key) === '1') {
document.documentElement.classList.add(key)
}
})
} catch (err) {}
})()
Loading