docs(llma): add Claude Agent SDK installation onboarding content#53011
Conversation
Adds the shared onboarding TSX for the Claude Agent SDK integration. Covers query() drop-in, instrument() reuse, and multi-turn tool usage. Part of #53010.
Prompt To Fix All With AIThis is a comment left during a code review.
Path: docs/onboarding/llm-analytics/claude-agent-sdk.tsx
Line: 144-160
Comment:
**`async for` used outside async context — snippet won't run**
The `async for` loops at the end of this block are at module/top-level without being wrapped in an `async def` and `asyncio.run()`. Python will raise a `SyntaxError` for this in any normal script. Compare with step 3, which correctly wraps everything in `async def main()` and calls `asyncio.run(main())`.
```suggestion
<CodeBlock
language="python"
code={dedent`
import asyncio
from posthog import Posthog
from posthog.ai.claude_agent_sdk import instrument
from claude_agent_sdk import ClaudeAgentOptions
posthog = Posthog(
"<ph_project_token>",
host="<ph_client_api_host>"
)
ph = instrument(
client=posthog,
distinct_id="user_123",
properties={"app": "my-agent"},
)
options = ClaudeAgentOptions(max_turns=10)
async def main():
# All queries share the same PostHog config
async for msg in ph.query(prompt="Question 1", options=options):
...
async for msg in ph.query(prompt="Question 2", options=options):
...
asyncio.run(main())
posthog.shutdown()
`}
/>
```
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: docs/onboarding/llm-analytics/claude-agent-sdk.tsx
Line: 181-222
Comment:
**Incomplete imports and missing `asyncio` wrapper in tool-usage example**
This code block has three problems that would prevent it from working as a standalone script:
1. `query` is used but never imported in this block (it was imported in step 3, but each code block should be self-contained or clearly note the dependency).
2. `posthog` is referenced but not initialized anywhere in this block.
3. `async for` at module level is a `SyntaxError` outside an `async def`/`asyncio.run()`.
A self-contained, runnable snippet would look like:
```python
import asyncio
from posthog import Posthog
from posthog.ai.claude_agent_sdk import query
from claude_agent_sdk import ClaudeAgentOptions, AssistantMessage, TextBlock, ToolUseBlock
posthog = Posthog("<ph_project_token>", host="<ph_client_api_host>")
options = ClaudeAgentOptions(
max_turns=10,
allowed_tools=["Read", "Glob", "Grep", "Bash"],
permission_mode="bypassPermissions",
cwd="/path/to/your/project",
)
async def main():
async for message in query(
prompt="Read the README and summarize this project",
options=options,
posthog_client=posthog,
posthog_distinct_id="user_123",
):
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, TextBlock):
print(block.text)
elif isinstance(block, ToolUseBlock):
print(f"Tool: {block.name}")
asyncio.run(main())
posthog.shutdown()
```
How can I resolve this? If you propose a fix, please make it concise.Reviews (1): Last reviewed commit: "docs(llma): add Claude Agent SDK install..." | Re-trigger Greptile |
| ) | ||
|
|
||
| ph = instrument( | ||
| client=posthog, | ||
| distinct_id="user_123", | ||
| properties={"app": "my-agent"}, | ||
| ) | ||
|
|
||
| options = ClaudeAgentOptions(max_turns=10) | ||
|
|
||
| # All queries share the same PostHog config | ||
| async for msg in ph.query(prompt="Question 1", options=options): | ||
| ... | ||
| async for msg in ph.query(prompt="Question 2", options=options): | ||
| ... | ||
| `} | ||
| /> |
There was a problem hiding this comment.
async for used outside async context — snippet won't run
The async for loops at the end of this block are at module/top-level without being wrapped in an async def and asyncio.run(). Python will raise a SyntaxError for this in any normal script. Compare with step 3, which correctly wraps everything in async def main() and calls asyncio.run(main()).
| ) | |
| ph = instrument( | |
| client=posthog, | |
| distinct_id="user_123", | |
| properties={"app": "my-agent"}, | |
| ) | |
| options = ClaudeAgentOptions(max_turns=10) | |
| # All queries share the same PostHog config | |
| async for msg in ph.query(prompt="Question 1", options=options): | |
| ... | |
| async for msg in ph.query(prompt="Question 2", options=options): | |
| ... | |
| `} | |
| /> | |
| <CodeBlock | |
| language="python" | |
| code={dedent` | |
| import asyncio | |
| from posthog import Posthog | |
| from posthog.ai.claude_agent_sdk import instrument | |
| from claude_agent_sdk import ClaudeAgentOptions | |
| posthog = Posthog( | |
| "<ph_project_token>", | |
| host="<ph_client_api_host>" | |
| ) | |
| ph = instrument( | |
| client=posthog, | |
| distinct_id="user_123", | |
| properties={"app": "my-agent"}, | |
| ) | |
| options = ClaudeAgentOptions(max_turns=10) | |
| async def main(): | |
| # All queries share the same PostHog config | |
| async for msg in ph.query(prompt="Question 1", options=options): | |
| ... | |
| async for msg in ph.query(prompt="Question 2", options=options): | |
| ... | |
| asyncio.run(main()) | |
| posthog.shutdown() | |
| `} | |
| /> |
Prompt To Fix With AI
This is a comment left during a code review.
Path: docs/onboarding/llm-analytics/claude-agent-sdk.tsx
Line: 144-160
Comment:
**`async for` used outside async context — snippet won't run**
The `async for` loops at the end of this block are at module/top-level without being wrapped in an `async def` and `asyncio.run()`. Python will raise a `SyntaxError` for this in any normal script. Compare with step 3, which correctly wraps everything in `async def main()` and calls `asyncio.run(main())`.
```suggestion
<CodeBlock
language="python"
code={dedent`
import asyncio
from posthog import Posthog
from posthog.ai.claude_agent_sdk import instrument
from claude_agent_sdk import ClaudeAgentOptions
posthog = Posthog(
"<ph_project_token>",
host="<ph_client_api_host>"
)
ph = instrument(
client=posthog,
distinct_id="user_123",
properties={"app": "my-agent"},
)
options = ClaudeAgentOptions(max_turns=10)
async def main():
# All queries share the same PostHog config
async for msg in ph.query(prompt="Question 1", options=options):
...
async for msg in ph.query(prompt="Question 2", options=options):
...
asyncio.run(main())
posthog.shutdown()
`}
/>
```
How can I resolve this? If you propose a fix, please make it concise.| title: 'Tool usage and multi-turn conversations', | ||
| badge: 'optional', | ||
| content: ( | ||
| <> | ||
| <Markdown> | ||
| PostHog captures the full trace hierarchy for multi-turn agent conversations with tool calls. | ||
| Each tool use is captured as an `$ai_span` event linked to its parent generation. | ||
| </Markdown> | ||
|
|
||
| <CodeBlock | ||
| language="python" | ||
| code={dedent` | ||
| from claude_agent_sdk import ClaudeAgentOptions, AssistantMessage, TextBlock, ToolUseBlock | ||
|
|
||
| options = ClaudeAgentOptions( | ||
| max_turns=10, | ||
| allowed_tools=["Read", "Glob", "Grep", "Bash"], | ||
| permission_mode="bypassPermissions", | ||
| cwd="/path/to/your/project", | ||
| ) | ||
|
|
||
| async for message in query( | ||
| prompt="Read the README and summarize this project", | ||
| options=options, | ||
| posthog_client=posthog, | ||
| posthog_distinct_id="user_123", | ||
| ): | ||
| if isinstance(message, AssistantMessage): | ||
| for block in message.content: | ||
| if isinstance(block, TextBlock): | ||
| print(block.text) | ||
| elif isinstance(block, ToolUseBlock): | ||
| print(f"Tool: {block.name}") | ||
| `} | ||
| /> | ||
|
|
||
| <Markdown> | ||
| {dedent` | ||
| This captures: | ||
| - \`$ai_generation\` events for each LLM turn (with token counts, cost, and cache metrics) | ||
| - \`$ai_span\` events for each tool use (Read, Glob, Grep, Bash, etc.) | ||
| - An \`$ai_trace\` event grouping the entire conversation with total cost and latency |
There was a problem hiding this comment.
Incomplete imports and missing
asyncio wrapper in tool-usage example
This code block has three problems that would prevent it from working as a standalone script:
queryis used but never imported in this block (it was imported in step 3, but each code block should be self-contained or clearly note the dependency).posthogis referenced but not initialized anywhere in this block.async forat module level is aSyntaxErroroutside anasync def/asyncio.run().
A self-contained, runnable snippet would look like:
import asyncio
from posthog import Posthog
from posthog.ai.claude_agent_sdk import query
from claude_agent_sdk import ClaudeAgentOptions, AssistantMessage, TextBlock, ToolUseBlock
posthog = Posthog("<ph_project_token>", host="<ph_client_api_host>")
options = ClaudeAgentOptions(
max_turns=10,
allowed_tools=["Read", "Glob", "Grep", "Bash"],
permission_mode="bypassPermissions",
cwd="/path/to/your/project",
)
async def main():
async for message in query(
prompt="Read the README and summarize this project",
options=options,
posthog_client=posthog,
posthog_distinct_id="user_123",
):
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, TextBlock):
print(block.text)
elif isinstance(block, ToolUseBlock):
print(f"Tool: {block.name}")
asyncio.run(main())
posthog.shutdown()Prompt To Fix With AI
This is a comment left during a code review.
Path: docs/onboarding/llm-analytics/claude-agent-sdk.tsx
Line: 181-222
Comment:
**Incomplete imports and missing `asyncio` wrapper in tool-usage example**
This code block has three problems that would prevent it from working as a standalone script:
1. `query` is used but never imported in this block (it was imported in step 3, but each code block should be self-contained or clearly note the dependency).
2. `posthog` is referenced but not initialized anywhere in this block.
3. `async for` at module level is a `SyntaxError` outside an `async def`/`asyncio.run()`.
A self-contained, runnable snippet would look like:
```python
import asyncio
from posthog import Posthog
from posthog.ai.claude_agent_sdk import query
from claude_agent_sdk import ClaudeAgentOptions, AssistantMessage, TextBlock, ToolUseBlock
posthog = Posthog("<ph_project_token>", host="<ph_client_api_host>")
options = ClaudeAgentOptions(
max_turns=10,
allowed_tools=["Read", "Glob", "Grep", "Bash"],
permission_mode="bypassPermissions",
cwd="/path/to/your/project",
)
async def main():
async for message in query(
prompt="Read the README and summarize this project",
options=options,
posthog_client=posthog,
posthog_distinct_id="user_123",
):
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, TextBlock):
print(block.text)
elif isinstance(block, ToolUseBlock):
print(f"Tool: {block.name}")
asyncio.run(main())
posthog.shutdown()
```
How can I resolve this? If you propose a fix, please make it concise.|
This PR hasn't seen activity in a week! Should it be merged, closed, or further worked on? If you want to keep it open, please remove the |
There was a problem hiding this comment.
The documentation code snippets contain real Python errors that bot reviewers flagged and remain unaddressed: the "Reusable configuration with instrument()" step has async for loops at module level (a SyntaxError in Python), and the "Tool usage and multi-turn conversations" step uses query and posthog without importing or initializing them. These are legitimate issues in user-facing documentation that would mislead developers.
|
Docs from this PR will be published at posthog.com
Preview will be ready in ~10 minutes. Click Preview link above to access docs at |
There was a problem hiding this comment.
The previously bot-flagged Python syntax errors in Steps 4 and 5 were fixed, but the new Step 6 ("Multi-turn conversations with history") introduces the same class of error: async with, await, and async for are used at module level without an enclosing async def / asyncio.run() wrapper, which is a SyntaxError in any standard Python script and directly misleads users copying this user-facing documentation snippet.
| asyncio.run(main()) | ||
| `} |
There was a problem hiding this comment.
Consistency issue: Missing posthog.shutdown() call after asyncio.run(main()). This is inconsistent with the examples in steps 3 and 5 (lines 100 and 229) which both include the shutdown call. Without it, the PostHog client may not flush pending events before the program exits.
asyncio.run(main())
posthog.shutdown()| asyncio.run(main()) | |
| `} | |
| asyncio.run(main()) | |
| posthog.shutdown() | |
| `} |
Spotted by Graphite
Is this helpful? React 👍 or 👎 to let us know.
|
This PR hasn't seen activity in a week! Should it be merged, closed, or further worked on? If you want to keep it open, please remove the |
There was a problem hiding this comment.
Step 6 of the documentation contains a genuine Python SyntaxError: async with, await, and async for are used at module level without an enclosing async def / asyncio.run() wrapper. This directly misleads users copying the snippet. The bot reviewer flagged this on the current head and it remains unaddressed.
Companion page for PostHog/posthog#53011 which added the shared onboarding content at docs/onboarding/llm-analytics/claude-agent-sdk.tsx. Follows the same pattern as openai-agents.mdx — imports the shared component and wraps it with OnboardingContentWrapper and addNextStepsStep.
Companion page for PostHog/posthog#53011 which added the shared onboarding content at docs/onboarding/llm-analytics/claude-agent-sdk.tsx. Follows the same pattern as openai-agents.mdx — imports the shared component and wraps it with OnboardingContentWrapper and addNextStepsStep. Co-authored-by: inkeep[bot] <257615677+inkeep[bot]@users.noreply.github.com>
Problem
No installation docs for the new Claude Agent SDK integration in LLM Analytics.
Changes
Adds
docs/onboarding/llm-analytics/claude-agent-sdk.tsx— shared onboarding content covering:query()drop-in replacementinstrument()reusable configurationCompanion PR in posthog.com adds the MDX wrapper and nav entry.
Part of #53010. Depends on PostHog/posthog-python#477.
How did you test this code?
Content follows the exact same pattern as
openai-agents.tsx. Will render once both this PR and the posthog.com PR are merged.Publish to changelog?
No