From 0f86edd54830173265c2566e33c87d7b162263d3 Mon Sep 17 00:00:00 2001 From: Carlos Marchal Date: Thu, 15 Oct 2026 10:24:00 +0200 Subject: [PATCH 1/2] chore: otel per request context docs --- .../_snippets/otel-request-context.tsx | 123 ++++++++++++++ .../_snippets/otel-session-id.tsx | 157 ------------------ docs/onboarding/ai-observability/autogen.tsx | 4 +- .../ai-observability/aws-bedrock.tsx | 4 +- .../ai-observability/claude-platform.tsx | 17 ++ .../ai-observability/instructor.tsx | 4 +- .../ai-observability/llamaindex.tsx | 4 +- .../onboarding/ai-observability/mirascope.tsx | 4 +- .../ai-observability/opentelemetry.tsx | 8 +- .../ai-observability/pydantic-ai.tsx | 4 +- .../ai-observability/semantic-kernel.tsx | 4 +- .../ai-observability/smolagents.tsx | 4 +- 12 files changed, 160 insertions(+), 177 deletions(-) create mode 100644 docs/onboarding/ai-observability/_snippets/otel-request-context.tsx delete mode 100644 docs/onboarding/ai-observability/_snippets/otel-session-id.tsx diff --git a/docs/onboarding/ai-observability/_snippets/otel-request-context.tsx b/docs/onboarding/ai-observability/_snippets/otel-request-context.tsx new file mode 100644 index 000000000000..73062ca655e5 --- /dev/null +++ b/docs/onboarding/ai-observability/_snippets/otel-request-context.tsx @@ -0,0 +1,123 @@ +import { OnboardingComponentsContext } from 'scenes/onboarding/shared/OnboardingDocsContentWrapper' + +import { StepDefinition } from '../../steps' + +export interface OtelRequestContextConfig { + /** Language tabs the surrounding page shows, in the order it shows them. */ + languages: ('Python' | 'Node' | 'Go')[] +} + +const PYTHON_CODE = ` +# Requires posthog 7.70.0 or later +import posthog + +with posthog.new_context(): + posthog.identify_context(user_id) + posthog.tag("$ai_session_id", conversation_id) + posthog.tag("plan", "pro") # custom properties work too + + reply = handle_turn(user_message) +`.trim() + +const NODE_CODE = ` +// Requires @posthog/ai 8.14.0 and posthog-node 5.15.0 or later +import { PostHog } from 'posthog-node' + +const posthog = new PostHog('', { host: '' }) + +// Pass the client to the processor you configured earlier +new PostHogSpanProcessor({ + projectToken: '', + host: '', + client: posthog, +}) + +const reply = await posthog.withContext( + { + distinctId: userId, + properties: { + $ai_session_id: conversationId, + plan: 'pro', // custom properties work too + }, + }, + () => handleTurn(userMessage) +) +`.trim() + +const GO_CODE = ` +// Requires github.com/posthog/posthog-go/otel v1.35.0 or later +import "github.com/posthog/posthog-go" + +ctx = posthog.WithRequestContext(ctx, posthog.RequestContext{ + DistinctId: userID, + Properties: posthog.Properties{ + "$ai_session_id": conversationID, + "plan": "pro", // custom properties work too + }, +}) + +reply := handleTurn(ctx, userMessage) +`.trim() + +const CODE_BY_LANGUAGE = { + Python: { language: 'python', file: 'Python', code: PYTHON_CODE }, + Node: { language: 'typescript', file: 'Node', code: NODE_CODE }, + Go: { language: 'go', file: 'Go', code: GO_CODE }, +} + +export const getOtelRequestContextStep = ( + ctx: OnboardingComponentsContext, + config: OtelRequestContextConfig +): StepDefinition => { + const { CodeBlock, Markdown, dedent } = ctx + + return { + title: 'Users and sessions', + badge: 'recommended', + content: ( + <> + + {dedent` + A server usually handles requests for many users, so a resource attribute can't say who + each trace belongs to. Wrap each request in a PostHog context instead. The PostHog span + processor adds the context's distinct ID and properties to every AI span started inside it. + Other span processors and exporters on the same provider don't see them. + + Set \`$ai_session_id\` if your product has multi-turn conversations, so the Sessions tab can + group their traces. Workloads that finish in a single trace, like batch jobs or one-shot + generation, don't need it. + `} + + + CODE_BY_LANGUAGE[language])} /> + + {config.languages.includes('Node') && ( + + {dedent` + On Node, install \`posthog-node\` and pass the client as the \`client\` option of your + \`PostHogSpanProcessor\`. Without it, the processor doesn't read the context. + `} + + )} + + {config.languages.includes('Go') && ( + + {dedent` + In Go, \`posthog.NewRequestContextMiddleware\` fills the context for each HTTP request + from the \`X-PostHog-Distinct-Id\` and \`X-PostHog-Session-Id\` headers. + `} + + )} + + + {dedent` + A distinct ID set this way takes priority over a \`posthog.distinct_id\` resource attribute, + so you can keep the resource attribute as the fallback for work outside a request. If your + instrumentation already sets the \`gen_ai.conversation.id\` attribute, PostHog uses it as + the session ID, and an explicit \`$ai_session_id\` takes priority over it. + `} + + + ), + } +} diff --git a/docs/onboarding/ai-observability/_snippets/otel-session-id.tsx b/docs/onboarding/ai-observability/_snippets/otel-session-id.tsx deleted file mode 100644 index 8b6bf148b31d..000000000000 --- a/docs/onboarding/ai-observability/_snippets/otel-session-id.tsx +++ /dev/null @@ -1,157 +0,0 @@ -import { OnboardingComponentsContext } from 'scenes/onboarding/shared/OnboardingDocsContentWrapper' - -import { StepDefinition } from '../../steps' - -export interface OtelSessionIdConfig { - /** Language tabs the surrounding page shows, in the order it shows them. */ - languages: ('Python' | 'Node' | 'Go')[] -} - -const PYTHON_CODE = ` -import contextvars -from collections.abc import Iterator -from contextlib import contextmanager -from typing import Optional - -from opentelemetry.context import Context -from opentelemetry.sdk.trace import Span, SpanProcessor - -session_id_var: contextvars.ContextVar[Optional[str]] = contextvars.ContextVar( - "ai_session_id", default=None -) - - -class SessionIdSpanProcessor(SpanProcessor): - def on_start(self, span: Span, parent_context: Optional[Context] = None) -> None: - session_id = session_id_var.get() - if session_id is not None: - span.set_attribute("$ai_session_id", session_id) - - -@contextmanager -def ai_session(session_id: str) -> Iterator[None]: - token = session_id_var.set(session_id) - try: - yield - finally: - session_id_var.reset(token) - - -# Register it on the same provider as PostHogSpanProcessor -provider.add_span_processor(SessionIdSpanProcessor()) - -# Resetting on exit keeps the ID off the next request that reuses this thread -with ai_session("conversation-abc"): - reply = handle_turn(user_message) -`.trim() - -const NODE_CODE = ` -import { AsyncLocalStorage } from 'node:async_hooks' -import type { Span, SpanProcessor } from '@opentelemetry/sdk-trace-base' - -const sessionStore = new AsyncLocalStorage() - -class SessionIdSpanProcessor implements SpanProcessor { - onStart(span: Span): void { - const sessionId = sessionStore.getStore() - if (sessionId) { - span.setAttribute('$ai_session_id', sessionId) - } - } - onEnd(): void {} - async shutdown(): Promise {} - async forceFlush(): Promise {} -} - -// Every span started inside the callback carries this session ID -const reply = await sessionStore.run('conversation-abc', () => handleTurn(userMessage)) -`.trim() - -const GO_CODE = ` -import ( - "context" - - "go.opentelemetry.io/otel/attribute" - sdktrace "go.opentelemetry.io/otel/sdk/trace" -) - -type sessionKey struct{} - -// WithAISession returns a context whose spans carry this session ID. -func WithAISession(ctx context.Context, sessionID string) context.Context { - return context.WithValue(ctx, sessionKey{}, sessionID) -} - -type SessionIDSpanProcessor struct{} - -func (SessionIDSpanProcessor) OnStart(parent context.Context, span sdktrace.ReadWriteSpan) { - if sessionID, ok := parent.Value(sessionKey{}).(string); ok { - span.SetAttributes(attribute.String("$ai_session_id", sessionID)) - } -} -func (SessionIDSpanProcessor) OnEnd(sdktrace.ReadOnlySpan) {} -func (SessionIDSpanProcessor) Shutdown(context.Context) error { return nil } -func (SessionIDSpanProcessor) ForceFlush(context.Context) error { return nil } - -// Register it on the same provider as the PostHog processor: -// sdktrace.WithSpanProcessor(SessionIDSpanProcessor{}) - -// Every span started from this context carries the session ID -ctx = WithAISession(ctx, "conversation-abc") -reply := handleTurn(ctx, userMessage) -`.trim() - -const CODE_BY_LANGUAGE = { - Python: { language: 'python', file: 'Python', code: PYTHON_CODE }, - Node: { language: 'typescript', file: 'Node', code: NODE_CODE }, - Go: { language: 'go', file: 'Go', code: GO_CODE }, -} - -export const getOtelSessionIdStep = (ctx: OnboardingComponentsContext, config: OtelSessionIdConfig): StepDefinition => { - const { CodeBlock, Markdown, dedent } = ctx - - return { - title: 'Group traces into sessions', - badge: 'optional', - content: ( - <> - - {dedent` - PostHog groups traces into a session when they share an \`$ai_session_id\`. Set it if your - product has multi-turn conversations, so the Sessions tab can reconstruct them. Workloads - that finish in a single trace, like batch jobs or one-shot generation, do not need it. - - The instrumentation creates the LLM span for you, so there is no call to pass the session ID - to. Add a span processor that sets the \`$ai_session_id\` attribute as each span starts. - PostHog forwards span attributes it does not recognize onto the event, so the value arrives - as the \`$ai_session_id\` property. - - If your instrumentation already sets the \`gen_ai.conversation.id\` attribute, PostHog uses - it as the session ID and you can skip this step. An explicit \`$ai_session_id\` takes - priority. - `} - - - CODE_BY_LANGUAGE[language])} /> - - {config.languages.includes('Node') && ( - - {dedent` - On Node, add \`new SessionIdSpanProcessor()\` to the \`spanProcessors\` array of the - \`NodeSDK\` you configured earlier, next to \`PostHogSpanProcessor\`. Keep the rest of - that setup as it is, including \`instrumentations\` and the \`sdk.start()\` call. - `} - - )} - - - {dedent` - If a process only ever handles one session, set \`$ai_session_id\` as a resource attribute - next to \`service.name\` instead. Resource attributes apply to every span the process emits, - so that only works when the process and the session are the same thing. - `} - - - ), - } -} diff --git a/docs/onboarding/ai-observability/autogen.tsx b/docs/onboarding/ai-observability/autogen.tsx index d00ba0a7c46a..04e167b0ca39 100644 --- a/docs/onboarding/ai-observability/autogen.tsx +++ b/docs/onboarding/ai-observability/autogen.tsx @@ -1,7 +1,7 @@ import { OnboardingComponentsContext, createInstallation } from 'scenes/onboarding/shared/OnboardingDocsContentWrapper' import { StepDefinition } from '../steps' -import { getOtelSessionIdStep } from './_snippets/otel-session-id' +import { getOtelRequestContextStep } from './_snippets/otel-request-context' export const getAutoGenSteps = (ctx: OnboardingComponentsContext): StepDefinition[] => { const { CodeBlock, CalloutBox, Markdown, Blockquote, dedent, snippets } = ctx @@ -125,7 +125,7 @@ export const getAutoGenSteps = (ctx: OnboardingComponentsContext): StepDefinitio ), }, - getOtelSessionIdStep(ctx, { languages: ['Python'] }), + getOtelRequestContextStep(ctx, { languages: ['Python'] }), ] } diff --git a/docs/onboarding/ai-observability/aws-bedrock.tsx b/docs/onboarding/ai-observability/aws-bedrock.tsx index 1d42198a39b1..2d53406f4f84 100644 --- a/docs/onboarding/ai-observability/aws-bedrock.tsx +++ b/docs/onboarding/ai-observability/aws-bedrock.tsx @@ -1,7 +1,7 @@ import { OnboardingComponentsContext, createInstallation } from 'scenes/onboarding/shared/OnboardingDocsContentWrapper' import { PROSE_LANGUAGE, StepDefinition } from '../steps' -import { getOtelSessionIdStep } from './_snippets/otel-session-id' +import { getOtelRequestContextStep } from './_snippets/otel-request-context' export const getAWSBedrockSteps = (ctx: OnboardingComponentsContext): StepDefinition[] => { const { CodeBlock, CalloutBox, Markdown, Blockquote, dedent, snippets } = ctx @@ -207,7 +207,7 @@ export const getAWSBedrockSteps = (ctx: OnboardingComponentsContext): StepDefini ), }, - getOtelSessionIdStep(ctx, { languages: ['Python', 'Node'] }), + getOtelRequestContextStep(ctx, { languages: ['Python', 'Node'] }), ] } diff --git a/docs/onboarding/ai-observability/claude-platform.tsx b/docs/onboarding/ai-observability/claude-platform.tsx index 791f34af0494..0ddf121fb2d4 100644 --- a/docs/onboarding/ai-observability/claude-platform.tsx +++ b/docs/onboarding/ai-observability/claude-platform.tsx @@ -1,6 +1,7 @@ import { OnboardingComponentsContext, createInstallation } from 'scenes/onboarding/shared/OnboardingDocsContentWrapper' import { StepDefinition } from '../steps' +import { getOtelRequestContextStep } from './_snippets/otel-request-context' const ADD_DESTINATION_SCREENSHOT = '/images/docs/ai-observability/claude-platform-add-destination.png' const MANAGED_AGENTS_TRACE_SCREENSHOT = '/images/docs/ai-observability/claude-platform-managed-agents-trace.png' @@ -393,6 +394,21 @@ const getSendTracesStep = (ctx: OnboardingComponentsContext): StepDefinition => } } +const getRequestContextStep = (ctx: OnboardingComponentsContext): StepDefinition => { + const { Markdown } = ctx + const step = getOtelRequestContextStep(ctx, { languages: ['Python', 'Node', 'Go'] }) + + return { + ...step, + content: ( + <> + This step applies to SDK tracing in Python, Node, and Go. + {step.content} + + ), + } +} + const getSeeTraceStep = (ctx: OnboardingComponentsContext): StepDefinition => { const { Markdown, ProductScreenshot, dedent } = ctx @@ -455,6 +471,7 @@ export const getClaudePlatformSteps = (ctx: OnboardingComponentsContext): StepDe getProjectTokenStep(ctx), getSendTracesStep(ctx), getSeeTraceStep(ctx), + getRequestContextStep(ctx), getTroubleshootingStep(ctx), ] diff --git a/docs/onboarding/ai-observability/instructor.tsx b/docs/onboarding/ai-observability/instructor.tsx index fc8a6af646d8..46c5fe0a2fa4 100644 --- a/docs/onboarding/ai-observability/instructor.tsx +++ b/docs/onboarding/ai-observability/instructor.tsx @@ -1,7 +1,7 @@ import { OnboardingComponentsContext, createInstallation } from 'scenes/onboarding/shared/OnboardingDocsContentWrapper' import { StepDefinition } from '../steps' -import { getOtelSessionIdStep } from './_snippets/otel-session-id' +import { getOtelRequestContextStep } from './_snippets/otel-request-context' export const getInstructorSteps = (ctx: OnboardingComponentsContext): StepDefinition[] => { const { CodeBlock, CalloutBox, Markdown, Blockquote, dedent, snippets } = ctx @@ -204,7 +204,7 @@ export const getInstructorSteps = (ctx: OnboardingComponentsContext): StepDefini ), }, - getOtelSessionIdStep(ctx, { languages: ['Python', 'Node'] }), + getOtelRequestContextStep(ctx, { languages: ['Python', 'Node'] }), ] } diff --git a/docs/onboarding/ai-observability/llamaindex.tsx b/docs/onboarding/ai-observability/llamaindex.tsx index 2af1aaacf1f1..e8804b21f13d 100644 --- a/docs/onboarding/ai-observability/llamaindex.tsx +++ b/docs/onboarding/ai-observability/llamaindex.tsx @@ -1,7 +1,7 @@ import { OnboardingComponentsContext, createInstallation } from 'scenes/onboarding/shared/OnboardingDocsContentWrapper' import { StepDefinition } from '../steps' -import { getOtelSessionIdStep } from './_snippets/otel-session-id' +import { getOtelRequestContextStep } from './_snippets/otel-request-context' export const getLlamaIndexSteps = (ctx: OnboardingComponentsContext): StepDefinition[] => { const { CodeBlock, CalloutBox, Markdown, Blockquote, dedent, snippets } = ctx @@ -136,7 +136,7 @@ export const getLlamaIndexSteps = (ctx: OnboardingComponentsContext): StepDefini ), }, - getOtelSessionIdStep(ctx, { languages: ['Python'] }), + getOtelRequestContextStep(ctx, { languages: ['Python'] }), ] } diff --git a/docs/onboarding/ai-observability/mirascope.tsx b/docs/onboarding/ai-observability/mirascope.tsx index 9e5dc8d48c10..e9867e11c6fa 100644 --- a/docs/onboarding/ai-observability/mirascope.tsx +++ b/docs/onboarding/ai-observability/mirascope.tsx @@ -1,7 +1,7 @@ import { OnboardingComponentsContext, createInstallation } from 'scenes/onboarding/shared/OnboardingDocsContentWrapper' import { StepDefinition } from '../steps' -import { getOtelSessionIdStep } from './_snippets/otel-session-id' +import { getOtelRequestContextStep } from './_snippets/otel-request-context' export const getMirascopeSteps = (ctx: OnboardingComponentsContext): StepDefinition[] => { const { CodeBlock, CalloutBox, Markdown, Blockquote, dedent, snippets } = ctx @@ -131,7 +131,7 @@ export const getMirascopeSteps = (ctx: OnboardingComponentsContext): StepDefinit ), }, - getOtelSessionIdStep(ctx, { languages: ['Python'] }), + getOtelRequestContextStep(ctx, { languages: ['Python'] }), ] } diff --git a/docs/onboarding/ai-observability/opentelemetry.tsx b/docs/onboarding/ai-observability/opentelemetry.tsx index ae9da6ea77f8..0b5acfc99d75 100644 --- a/docs/onboarding/ai-observability/opentelemetry.tsx +++ b/docs/onboarding/ai-observability/opentelemetry.tsx @@ -1,7 +1,7 @@ import { OnboardingComponentsContext, createInstallation } from 'scenes/onboarding/shared/OnboardingDocsContentWrapper' import { StepDefinition } from '../steps' -import { getOtelSessionIdStep } from './_snippets/otel-session-id' +import { getOtelRequestContextStep } from './_snippets/otel-request-context' export const getOpenTelemetrySteps = (ctx: OnboardingComponentsContext): StepDefinition[] => { const { CodeBlock, CalloutBox, Markdown, Blockquote, dedent, snippets } = ctx @@ -43,7 +43,7 @@ export const getOpenTelemetrySteps = (ctx: OnboardingComponentsContext): StepDef language: 'bash', file: 'Node', code: dedent` - npm install openai @posthog/ai @opentelemetry/sdk-node @opentelemetry/resources @opentelemetry/instrumentation-openai + npm install openai @posthog/ai posthog-node @opentelemetry/sdk-node @opentelemetry/resources @opentelemetry/instrumentation-openai `, }, { @@ -278,7 +278,7 @@ export const getOpenTelemetrySteps = (ctx: OnboardingComponentsContext): StepDef
{dedent` - **Note:** If you want to capture LLM events anonymously, omit the \`posthog.distinct_id\` resource attribute. See our docs on [anonymous vs identified events](https://posthog.com/docs/data/anonymous-vs-identified-events) to learn more. + **Note:** To capture LLM events anonymously, omit the \`posthog.distinct_id\` resource attribute and don't set a distinct ID in the PostHog context. See our docs on [anonymous vs identified events](https://posthog.com/docs/data/anonymous-vs-identified-events) to learn more. `}
@@ -335,7 +335,7 @@ export const getOpenTelemetrySteps = (ctx: OnboardingComponentsContext): StepDef ), }, - getOtelSessionIdStep(ctx, { languages: ['Python', 'Node', 'Go'] }), + getOtelRequestContextStep(ctx, { languages: ['Python', 'Node', 'Go'] }), { title: 'Other instrumentations, direct OTLP, and troubleshooting', badge: 'optional', diff --git a/docs/onboarding/ai-observability/pydantic-ai.tsx b/docs/onboarding/ai-observability/pydantic-ai.tsx index 3da540a70e73..a858f8ab7a68 100644 --- a/docs/onboarding/ai-observability/pydantic-ai.tsx +++ b/docs/onboarding/ai-observability/pydantic-ai.tsx @@ -1,7 +1,7 @@ import { OnboardingComponentsContext, createInstallation } from 'scenes/onboarding/shared/OnboardingDocsContentWrapper' import { StepDefinition } from '../steps' -import { getOtelSessionIdStep } from './_snippets/otel-session-id' +import { getOtelRequestContextStep } from './_snippets/otel-request-context' export const getPydanticAISteps = (ctx: OnboardingComponentsContext): StepDefinition[] => { const { CodeBlock, CalloutBox, Markdown, Blockquote, dedent, snippets } = ctx @@ -119,7 +119,7 @@ export const getPydanticAISteps = (ctx: OnboardingComponentsContext): StepDefini ), }, - getOtelSessionIdStep(ctx, { languages: ['Python'] }), + getOtelRequestContextStep(ctx, { languages: ['Python'] }), ] } diff --git a/docs/onboarding/ai-observability/semantic-kernel.tsx b/docs/onboarding/ai-observability/semantic-kernel.tsx index dd819db65193..9d1d66f24117 100644 --- a/docs/onboarding/ai-observability/semantic-kernel.tsx +++ b/docs/onboarding/ai-observability/semantic-kernel.tsx @@ -1,7 +1,7 @@ import { OnboardingComponentsContext, createInstallation } from 'scenes/onboarding/shared/OnboardingDocsContentWrapper' import { StepDefinition } from '../steps' -import { getOtelSessionIdStep } from './_snippets/otel-session-id' +import { getOtelRequestContextStep } from './_snippets/otel-request-context' export const getSemanticKernelSteps = (ctx: OnboardingComponentsContext): StepDefinition[] => { const { CodeBlock, CalloutBox, Markdown, Blockquote, dedent, snippets } = ctx @@ -125,7 +125,7 @@ export const getSemanticKernelSteps = (ctx: OnboardingComponentsContext): StepDe ), }, - getOtelSessionIdStep(ctx, { languages: ['Python'] }), + getOtelRequestContextStep(ctx, { languages: ['Python'] }), ] } diff --git a/docs/onboarding/ai-observability/smolagents.tsx b/docs/onboarding/ai-observability/smolagents.tsx index 41d51df6c615..b85efdace0d8 100644 --- a/docs/onboarding/ai-observability/smolagents.tsx +++ b/docs/onboarding/ai-observability/smolagents.tsx @@ -1,7 +1,7 @@ import { OnboardingComponentsContext, createInstallation } from 'scenes/onboarding/shared/OnboardingDocsContentWrapper' import { StepDefinition } from '../steps' -import { getOtelSessionIdStep } from './_snippets/otel-session-id' +import { getOtelRequestContextStep } from './_snippets/otel-request-context' export const getSmolagentsSteps = (ctx: OnboardingComponentsContext): StepDefinition[] => { const { CodeBlock, CalloutBox, Markdown, Blockquote, dedent, snippets } = ctx @@ -117,7 +117,7 @@ export const getSmolagentsSteps = (ctx: OnboardingComponentsContext): StepDefini ), }, - getOtelSessionIdStep(ctx, { languages: ['Python'] }), + getOtelRequestContextStep(ctx, { languages: ['Python'] }), ] } From 5a3be28b9eecbf670976ccd80a95441191f71f69 Mon Sep 17 00:00:00 2001 From: Carlos Marchal Date: Thu, 15 Oct 2026 15:42:02 +0200 Subject: [PATCH 2/2] chore: otel request context review fixes --- .../_snippets/otel-request-context.tsx | 43 +++++++++++++------ docs/onboarding/ai-observability/autogen.tsx | 7 +-- .../ai-observability/aws-bedrock.tsx | 7 +-- .../ai-observability/claude-platform.tsx | 5 ++- .../ai-observability/instructor.tsx | 7 +-- .../ai-observability/llamaindex.tsx | 7 +-- .../onboarding/ai-observability/mirascope.tsx | 7 +-- .../ai-observability/pydantic-ai.tsx | 7 +-- .../ai-observability/semantic-kernel.tsx | 7 +-- .../ai-observability/smolagents.tsx | 7 +-- 10 files changed, 67 insertions(+), 37 deletions(-) diff --git a/docs/onboarding/ai-observability/_snippets/otel-request-context.tsx b/docs/onboarding/ai-observability/_snippets/otel-request-context.tsx index 73062ca655e5..c4ef9bc50473 100644 --- a/docs/onboarding/ai-observability/_snippets/otel-request-context.tsx +++ b/docs/onboarding/ai-observability/_snippets/otel-request-context.tsx @@ -5,6 +5,8 @@ import { StepDefinition } from '../../steps' export interface OtelRequestContextConfig { /** Language tabs the surrounding page shows, in the order it shows them. */ languages: ('Python' | 'Node' | 'Go')[] + /** The tracer setup class the page's Node setup step uses. Defaults to `NodeSDK`. */ + nodeTracerSetup?: 'NodeSDK' | 'NodeTracerProvider' } const PYTHON_CODE = ` @@ -19,17 +21,24 @@ with posthog.new_context(): reply = handle_turn(user_message) `.trim() -const NODE_CODE = ` +const NODE_SETUP_VARIABLES = { NodeSDK: 'sdk', NodeTracerProvider: 'provider' } + +const getNodeCode = (tracerSetup: 'NodeSDK' | 'NodeTracerProvider'): string => + ` // Requires @posthog/ai 8.14.0 and posthog-node 5.15.0 or later import { PostHog } from 'posthog-node' const posthog = new PostHog('', { host: '' }) -// Pass the client to the processor you configured earlier -new PostHogSpanProcessor({ - projectToken: '', - host: '', - client: posthog, +const ${NODE_SETUP_VARIABLES[tracerSetup]} = new ${tracerSetup}({ + // ...the rest of your setup from the earlier step + spanProcessors: [ + new PostHogSpanProcessor({ + projectToken: '', + host: '', + client: posthog, + }), + ], }) const reply = await posthog.withContext( @@ -59,10 +68,18 @@ ctx = posthog.WithRequestContext(ctx, posthog.RequestContext{ reply := handleTurn(ctx, userMessage) `.trim() -const CODE_BY_LANGUAGE = { - Python: { language: 'python', file: 'Python', code: PYTHON_CODE }, - Node: { language: 'typescript', file: 'Node', code: NODE_CODE }, - Go: { language: 'go', file: 'Go', code: GO_CODE }, +const getCodeBlock = ( + language: OtelRequestContextConfig['languages'][number], + config: OtelRequestContextConfig +): { language: string; file: string; code: string } => { + switch (language) { + case 'Python': + return { language: 'python', file: 'Python', code: PYTHON_CODE } + case 'Node': + return { language: 'typescript', file: 'Node', code: getNodeCode(config.nodeTracerSetup ?? 'NodeSDK') } + case 'Go': + return { language: 'go', file: 'Go', code: GO_CODE } + } } export const getOtelRequestContextStep = ( @@ -89,7 +106,7 @@ export const getOtelRequestContextStep = ( `} - CODE_BY_LANGUAGE[language])} /> + getCodeBlock(language, config))} /> {config.languages.includes('Node') && ( @@ -104,7 +121,9 @@ export const getOtelRequestContextStep = ( {dedent` In Go, \`posthog.NewRequestContextMiddleware\` fills the context for each HTTP request - from the \`X-PostHog-Distinct-Id\` and \`X-PostHog-Session-Id\` headers. + from the \`X-PostHog-Distinct-Id\` and \`X-PostHog-Session-Id\` headers. The session + header sets the web session ID (\`$session_id\`), not \`$ai_session_id\`, so set + \`$ai_session_id\` in \`Properties\` to group traces in the Sessions tab. `} )} diff --git a/docs/onboarding/ai-observability/autogen.tsx b/docs/onboarding/ai-observability/autogen.tsx index 04e167b0ca39..990b866d1b79 100644 --- a/docs/onboarding/ai-observability/autogen.tsx +++ b/docs/onboarding/ai-observability/autogen.tsx @@ -109,9 +109,10 @@ export const getAutoGenSteps = (ctx: OnboardingComponentsContext): StepDefinitio
- **Note:** If you want to capture LLM events anonymously, omit the `posthog.distinct_id` - resource attribute. See our docs on [anonymous vs identified - events](https://posthog.com/docs/data/anonymous-vs-identified-events) to learn more. + **Note:** To capture LLM events anonymously, omit the `posthog.distinct_id` resource + attribute and don't set a distinct ID in the PostHog context. See our docs on [anonymous vs + identified events](https://posthog.com/docs/data/anonymous-vs-identified-events) to learn + more.
diff --git a/docs/onboarding/ai-observability/aws-bedrock.tsx b/docs/onboarding/ai-observability/aws-bedrock.tsx index 2d53406f4f84..4f0e4567196d 100644 --- a/docs/onboarding/ai-observability/aws-bedrock.tsx +++ b/docs/onboarding/ai-observability/aws-bedrock.tsx @@ -191,9 +191,10 @@ export const getAWSBedrockSteps = (ctx: OnboardingComponentsContext): StepDefini
- **Note:** If you want to capture LLM events anonymously, omit the `posthog_distinct_id`. See - our docs on [anonymous vs identified - events](https://posthog.com/docs/data/anonymous-vs-identified-events) to learn more. + **Note:** To capture LLM events anonymously, omit the `posthog.distinct_id` resource + attribute and don't set a distinct ID in the PostHog context. See our docs on [anonymous vs + identified events](https://posthog.com/docs/data/anonymous-vs-identified-events) to learn + more.
diff --git a/docs/onboarding/ai-observability/claude-platform.tsx b/docs/onboarding/ai-observability/claude-platform.tsx index 0ddf121fb2d4..4cae20a930c1 100644 --- a/docs/onboarding/ai-observability/claude-platform.tsx +++ b/docs/onboarding/ai-observability/claude-platform.tsx @@ -396,7 +396,10 @@ const getSendTracesStep = (ctx: OnboardingComponentsContext): StepDefinition => const getRequestContextStep = (ctx: OnboardingComponentsContext): StepDefinition => { const { Markdown } = ctx - const step = getOtelRequestContextStep(ctx, { languages: ['Python', 'Node', 'Go'] }) + const step = getOtelRequestContextStep(ctx, { + languages: ['Python', 'Node', 'Go'], + nodeTracerSetup: 'NodeTracerProvider', + }) return { ...step, diff --git a/docs/onboarding/ai-observability/instructor.tsx b/docs/onboarding/ai-observability/instructor.tsx index 46c5fe0a2fa4..a4fa7b0a4cb6 100644 --- a/docs/onboarding/ai-observability/instructor.tsx +++ b/docs/onboarding/ai-observability/instructor.tsx @@ -188,9 +188,10 @@ export const getInstructorSteps = (ctx: OnboardingComponentsContext): StepDefini
- **Note:** If you want to capture LLM events anonymously, omit the `posthog.distinct_id` - resource attribute. See our docs on [anonymous vs identified - events](https://posthog.com/docs/data/anonymous-vs-identified-events) to learn more. + **Note:** To capture LLM events anonymously, omit the `posthog.distinct_id` resource + attribute and don't set a distinct ID in the PostHog context. See our docs on [anonymous vs + identified events](https://posthog.com/docs/data/anonymous-vs-identified-events) to learn + more.
diff --git a/docs/onboarding/ai-observability/llamaindex.tsx b/docs/onboarding/ai-observability/llamaindex.tsx index e8804b21f13d..b94396cf380f 100644 --- a/docs/onboarding/ai-observability/llamaindex.tsx +++ b/docs/onboarding/ai-observability/llamaindex.tsx @@ -120,9 +120,10 @@ export const getLlamaIndexSteps = (ctx: OnboardingComponentsContext): StepDefini
- **Note:** If you want to capture LLM events anonymously, omit the `posthog.distinct_id` - resource attribute. See our docs on [anonymous vs identified - events](https://posthog.com/docs/data/anonymous-vs-identified-events) to learn more. + **Note:** To capture LLM events anonymously, omit the `posthog.distinct_id` resource + attribute and don't set a distinct ID in the PostHog context. See our docs on [anonymous vs + identified events](https://posthog.com/docs/data/anonymous-vs-identified-events) to learn + more.
diff --git a/docs/onboarding/ai-observability/mirascope.tsx b/docs/onboarding/ai-observability/mirascope.tsx index e9867e11c6fa..f740f8c134bb 100644 --- a/docs/onboarding/ai-observability/mirascope.tsx +++ b/docs/onboarding/ai-observability/mirascope.tsx @@ -115,9 +115,10 @@ export const getMirascopeSteps = (ctx: OnboardingComponentsContext): StepDefinit
- **Note:** If you want to capture LLM events anonymously, omit the `posthog.distinct_id` - resource attribute. See our docs on [anonymous vs identified - events](https://posthog.com/docs/data/anonymous-vs-identified-events) to learn more. + **Note:** To capture LLM events anonymously, omit the `posthog.distinct_id` resource + attribute and don't set a distinct ID in the PostHog context. See our docs on [anonymous vs + identified events](https://posthog.com/docs/data/anonymous-vs-identified-events) to learn + more.
diff --git a/docs/onboarding/ai-observability/pydantic-ai.tsx b/docs/onboarding/ai-observability/pydantic-ai.tsx index a858f8ab7a68..a7730fb0dfe3 100644 --- a/docs/onboarding/ai-observability/pydantic-ai.tsx +++ b/docs/onboarding/ai-observability/pydantic-ai.tsx @@ -103,9 +103,10 @@ export const getPydanticAISteps = (ctx: OnboardingComponentsContext): StepDefini
- **Note:** If you want to capture LLM events anonymously, omit the `posthog.distinct_id` - resource attribute. See our docs on [anonymous vs identified - events](https://posthog.com/docs/data/anonymous-vs-identified-events) to learn more. + **Note:** To capture LLM events anonymously, omit the `posthog.distinct_id` resource + attribute and don't set a distinct ID in the PostHog context. See our docs on [anonymous vs + identified events](https://posthog.com/docs/data/anonymous-vs-identified-events) to learn + more.
diff --git a/docs/onboarding/ai-observability/semantic-kernel.tsx b/docs/onboarding/ai-observability/semantic-kernel.tsx index 9d1d66f24117..c09ed90bced2 100644 --- a/docs/onboarding/ai-observability/semantic-kernel.tsx +++ b/docs/onboarding/ai-observability/semantic-kernel.tsx @@ -109,9 +109,10 @@ export const getSemanticKernelSteps = (ctx: OnboardingComponentsContext): StepDe
- **Note:** If you want to capture LLM events anonymously, omit the `posthog.distinct_id` - resource attribute. See our docs on [anonymous vs identified - events](https://posthog.com/docs/data/anonymous-vs-identified-events) to learn more. + **Note:** To capture LLM events anonymously, omit the `posthog.distinct_id` resource + attribute and don't set a distinct ID in the PostHog context. See our docs on [anonymous vs + identified events](https://posthog.com/docs/data/anonymous-vs-identified-events) to learn + more.
diff --git a/docs/onboarding/ai-observability/smolagents.tsx b/docs/onboarding/ai-observability/smolagents.tsx index b85efdace0d8..8ca58605f090 100644 --- a/docs/onboarding/ai-observability/smolagents.tsx +++ b/docs/onboarding/ai-observability/smolagents.tsx @@ -101,9 +101,10 @@ export const getSmolagentsSteps = (ctx: OnboardingComponentsContext): StepDefini
- **Note:** If you want to capture LLM events anonymously, omit the `posthog.distinct_id` - resource attribute. See our docs on [anonymous vs identified - events](https://posthog.com/docs/data/anonymous-vs-identified-events) to learn more. + **Note:** To capture LLM events anonymously, omit the `posthog.distinct_id` resource + attribute and don't set a distinct ID in the PostHog context. See our docs on [anonymous vs + identified events](https://posthog.com/docs/data/anonymous-vs-identified-events) to learn + more.