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
171 changes: 171 additions & 0 deletions docs/onboarding/ai-observability/google-adk.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import { OnboardingComponentsContext, createInstallation } from 'scenes/onboarding/shared/OnboardingDocsContentWrapper'

import { StepDefinition } from '../steps'

export const getGoogleADKSteps = (ctx: OnboardingComponentsContext): StepDefinition[] => {
const { CodeBlock, CalloutBox, Markdown, dedent, snippets } = ctx
const NotableGenerationProperties = snippets?.NotableGenerationProperties

return [
{
title: 'Install dependencies',
badge: 'required',
content: (
<>
<CalloutBox type="fyi" icon="IconInfo" title="Full working example">
<Markdown>
See the complete [Node.js
example](https://github.com/PostHog/posthog-js/tree/main/examples/example-ai-adk) on GitHub.
</Markdown>
</CalloutBox>

<Markdown>
Install the PostHog SDK alongside the [Google Agent Development Kit for
TypeScript](https://github.com/google/adk-js) (`@google/adk`). For the Python and Go ADKs, use
the [OpenTelemetry
integration](https://posthog.com/docs/ai-observability/installation/opentelemetry) instead: they
emit `gen_ai.*` spans that PostHog captures automatically. ADK Go sends message content as log
records, so its generations arrive without prompts and responses.
</Markdown>

<CodeBlock
language="bash"
code={dedent`
npm install @posthog/ai posthog-node @google/adk zod
`}
/>
</>
),
},
{
title: 'Add the PostHog plugin',
badge: 'required',
content: (
<>
<Markdown>
Create a PostHog client and register `PostHogADKPlugin` on your ADK `Runner`. The plugin hooks
the run, agent, tool, and model callbacks and captures the full hierarchy: an `$ai_trace` per
invocation, `$ai_span` events for agent runs and tool calls, and one `$ai_generation` per model
call. It **does not** proxy your calls.
</Markdown>

<CodeBlock
language="typescript"
code={dedent`
import { FunctionTool, InMemorySessionService, LlmAgent, Runner } from '@google/adk'
import { PostHogADKPlugin } from '@posthog/ai/adk'
import { PostHog } from 'posthog-node'
import { z } from 'zod'

const posthog = new PostHog('<ph_project_token>', { host: '<ph_client_api_host>' })

const getWeather = new FunctionTool({
name: 'get_weather',
description: 'Get the current weather for a city.',
parameters: z.object({ city: z.string() }),
execute: ({ city }) => \`The weather in \${city} is sunny, 72F\`,
})

const agent = new LlmAgent({
name: 'assistant',
model: 'gemini-3.6-flash',
instruction: 'You are a helpful assistant.',
tools: [getWeather],
})

const sessionService = new InMemorySessionService()
const runner = new Runner({
appName: 'my-app',
agent,
sessionService,
plugins: [new PostHogADKPlugin({ client: posthog })],
})
`}
/>
</>
),
},
{
title: 'Run your agent',
badge: 'required',
content: (
<>
<Markdown>
{dedent`
Run your agent as normal. Each invocation becomes a trace, the ADK session ID becomes
\`$ai_session_id\`, and the run's \`userId\` becomes the events' distinct ID. Pass
\`distinctId\` to the plugin to attribute events to a different PostHog person.
`}
</Markdown>

<CodeBlock
language="typescript"
code={dedent`
await sessionService.createSession({
appName: 'my-app',
userId: 'user_123',
sessionId: 'conversation-abc',
})

for await (const event of runner.runAsync({
userId: 'user_123',
sessionId: 'conversation-abc',
newMessage: { role: 'user', parts: [{ text: "What's the weather in Paris?" }] },
Comment thread
marco-g-pm marked this conversation as resolved.
})) {
for (const part of event.content?.parts ?? []) {
if (part.text) {
console.log(part.text)
}
}
}
`}
/>

<Markdown>
{dedent`
The question above makes the agent call the tool, so this run captures:

- a trace for the invocation
- a span for the \`assistant\` agent run
- a span for the \`get_weather\` tool call
- a generation for each of the two model calls (the tool request, then the answer)
`}
</Markdown>

<Markdown>
Call `await posthog.shutdown()` before your process exits so batched events are flushed.
</Markdown>

<Markdown>
{dedent`
You can expect captured \`$ai_generation\` events to have the following properties:
`}
</Markdown>

{NotableGenerationProperties && <NotableGenerationProperties />}
</>
),
},
{
title: 'Plugin options',
badge: 'optional',
content: (
<Markdown>
{dedent`
\`PostHogADKPlugin\` accepts these options besides \`client\`:

- \`distinctId\`: a string, or a resolver \`(context) => string\` called per model call. Defaults to the ADK \`userId\`.
- \`provider\`: the \`$ai_provider\` label. Defaults to \`gemini\`. Set it when routing ADK to another provider so costs are derived from the right model catalog.
- \`privacyMode\`: redacts captured input and output content.
- \`groups\`: [group analytics](https://posthog.com/docs/product-analytics/group-analytics) attached to every event.
- \`properties\`: extra properties merged into every event.
- \`captureImmediate\`: awaits delivery per event instead of batching. Useful in serverless environments.
- \`onError\`: called when capturing an event fails. Capture errors never throw into the model flow.
`}
</Markdown>
),
},
]
}

export const GoogleADKInstallation = createInstallation(getGoogleADKSteps)
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { DSPyInstallation } from '@posthog/shared-onboarding/ai-observability/ds
import { EveInstallation } from '@posthog/shared-onboarding/ai-observability/eve'
import { FireworksAIInstallation } from '@posthog/shared-onboarding/ai-observability/fireworks-ai'
import { GoogleInstallation } from '@posthog/shared-onboarding/ai-observability/google'
import { GoogleADKInstallation } from '@posthog/shared-onboarding/ai-observability/google-adk'
import { GroqInstallation } from '@posthog/shared-onboarding/ai-observability/groq'
import { HeliconeInstallation } from '@posthog/shared-onboarding/ai-observability/helicone'
import { HuggingFaceInstallation } from '@posthog/shared-onboarding/ai-observability/hugging-face'
Expand Down Expand Up @@ -80,6 +81,10 @@ const LLMGoogleInstructionsWrapper = withOnboardingDocsWrapper({
Installation: GoogleInstallation,
snippets: PROVIDER_SNIPPETS,
})
const LLMGoogleADKInstructionsWrapper = withOnboardingDocsWrapper({
Installation: GoogleADKInstallation,
snippets: PROVIDER_SNIPPETS,
})
const LLMOpenRouterInstructionsWrapper = withOnboardingDocsWrapper({
Installation: OpenRouterInstallation,
snippets: PROVIDER_SNIPPETS,
Expand Down Expand Up @@ -238,6 +243,7 @@ export const AIObservabilitySDKInstructions: SDKInstructionsMap = {
[SDKKey.ANTHROPIC]: LLMAnthropicInstructionsWrapper,
[SDKKey.AWS_BEDROCK]: LLMAWSBedrockInstructionsWrapper,
[SDKKey.GOOGLE_GEMINI]: LLMGoogleInstructionsWrapper,
[SDKKey.GOOGLE_ADK]: LLMGoogleADKInstructionsWrapper,
[SDKKey.VERCEL_AI]: LLMVercelAIInstructionsWrapper,
[SDKKey.EVE]: LLMEveInstructionsWrapper,
[SDKKey.VERCEL_AI_GATEWAY]: LLMVercelAIGatewayInstructionsWrapper,
Expand Down
7 changes: 7 additions & 0 deletions frontend/src/scenes/onboarding/legacy/sdks/allSDKs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,13 @@ export const ALL_SDKS: SDK[] = [
image: geminiImage,
docsLink: 'https://posthog.com/docs/ai-observability/installation/google',
},
{
name: 'Google ADK',
key: SDKKey.GOOGLE_ADK,
tags: [SDKTag.FRAMEWORK],
image: geminiImage,
docsLink: 'https://posthog.com/docs/ai-observability/installation/google-adk',
},
{
name: 'Vercel AI SDK',
key: SDKKey.VERCEL_AI,
Expand Down
1 change: 1 addition & 0 deletions frontend/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7125,6 +7125,7 @@ export enum SDKKey {
FLUTTER = 'flutter',
GATSBY = 'gatsby',
GO = 'go',
GOOGLE_ADK = 'google_adk',
GOOGLE_GEMINI = 'google_gemini',
GOOGLE_TAG_MANAGER = 'google_tag_manager',
GROQ = 'groq',
Expand Down
Loading