mirror of
https://github.com/hanzoai/insights.git
synced 2026-10-30 16:15:31 +00:00
Mechanically re-derived: bin/debrand runs over a fresh import of posthog/posthog@fa1a9a60d4 (2026-08-06). The pipeline (.hanzo/workflows), the Dockerfiles and bin/docker* entrypoints, compose files, LLM.md and the squashed initial migration are restored from the shipped tree, same as the previous import. The prune's import-closure guard kept 21 products the new core now imports; dropping them is a refactor now, recorded rather than forced.
296 lines
14 KiB
TypeScript
296 lines
14 KiB
TypeScript
import { OnboardingComponentsContext, createInstallation } from 'scenes/onboarding/shared/OnboardingDocsContentWrapper'
|
|
|
|
import { StepDefinition } from '../steps'
|
|
|
|
export const getClaudeAgentSDKSteps = (ctx: OnboardingComponentsContext): StepDefinition[] => {
|
|
const { CodeBlock, CalloutBox, Markdown, Blockquote, dedent, snippets } = ctx
|
|
|
|
const NotableGenerationProperties = snippets?.NotableGenerationProperties
|
|
|
|
return [
|
|
{
|
|
title: 'Install the Insights SDK',
|
|
badge: 'required',
|
|
content: (
|
|
<>
|
|
<Markdown>Setting up analytics starts with installing the Insights Python SDK.</Markdown>
|
|
|
|
<CodeBlock
|
|
language="bash"
|
|
code={dedent`
|
|
pip install insights
|
|
`}
|
|
/>
|
|
</>
|
|
),
|
|
},
|
|
{
|
|
title: 'Install the Claude Agent SDK',
|
|
badge: 'required',
|
|
content: (
|
|
<>
|
|
<Markdown>
|
|
Install the Claude Agent SDK. Insights instruments your agent queries by wrapping the `query()`
|
|
function. The Insights SDK **does not** proxy your calls.
|
|
</Markdown>
|
|
|
|
<CodeBlock
|
|
language="bash"
|
|
code={dedent`
|
|
pip install claude-agent-sdk
|
|
`}
|
|
/>
|
|
|
|
<CalloutBox type="fyi" icon="IconInfo" title="Proxy note">
|
|
<Markdown>
|
|
These SDKs **do not** proxy your calls. They only fire off an async call to Insights in the
|
|
background to send the data. You can also use AI observability with other SDKs or our API,
|
|
but you will need to capture the data in the right format. See the schema in the [manual
|
|
capture section](https://hanzo.ai/docs/ai-observability/installation/manual-capture) for
|
|
more details.
|
|
</Markdown>
|
|
</CalloutBox>
|
|
</>
|
|
),
|
|
},
|
|
{
|
|
title: 'Initialize Insights and run a query',
|
|
badge: 'required',
|
|
content: (
|
|
<>
|
|
<Markdown>
|
|
{dedent`
|
|
Initialize Insights with your project token and host from [your project
|
|
settings](https://app.hanzo.ai/settings/project). Then use the Insights \`query()\`
|
|
wrapper as a drop-in replacement for \`claude_agent_sdk.query()\`. This automatically
|
|
captures \`$ai_generation\`, \`$ai_span\`, and \`$ai_trace\` events, including a span
|
|
for each tool the agent uses, such as \`Bash\` below.
|
|
`}
|
|
</Markdown>
|
|
|
|
<CodeBlock
|
|
language="python"
|
|
code={dedent`
|
|
import asyncio
|
|
from insights import Insights
|
|
from insights.ai.claude_agent_sdk import query
|
|
from claude_agent_sdk import ClaudeAgentOptions
|
|
|
|
insights = Insights(
|
|
"<ph_project_token>",
|
|
host="<ph_client_api_host>"
|
|
)
|
|
|
|
async def main():
|
|
options = ClaudeAgentOptions(
|
|
max_turns=5,
|
|
allowed_tools=["Read", "Glob", "Grep"],
|
|
cwd="/path/to/your/project",
|
|
)
|
|
|
|
async for message in query(
|
|
prompt="Read the README and summarize this project",
|
|
options=options,
|
|
insights_client=insights,
|
|
insights_distinct_id="user_123", # optional
|
|
insights_trace_id="trace_123", # optional
|
|
insights_properties={"conversation_id": "abc123", "$ai_session_id": "conversation-abc"}, # optional
|
|
insights_groups={"company": "company_id_in_your_db"}, # optional
|
|
insights_privacy_mode=False, # optional
|
|
):
|
|
print(message)
|
|
|
|
asyncio.run(main())
|
|
insights.shutdown()
|
|
`}
|
|
/>
|
|
|
|
<Blockquote>
|
|
<Markdown>
|
|
{dedent`
|
|
**Notes:**
|
|
- The wrapper yields all original messages unchanged, fully transparent to your code.
|
|
- Pass \`$ai_session_id\` in \`insights_properties\` to group every generation, span, and trace from a call into one Insights session.
|
|
- If you want to capture LLM events anonymously, **do not** pass a distinct ID. See our docs on [anonymous vs identified events](https://hanzo.ai/docs/data/anonymous-vs-identified-events) to learn more.
|
|
`}
|
|
</Markdown>
|
|
</Blockquote>
|
|
|
|
<Markdown>
|
|
{dedent`
|
|
You can expect captured \`$ai_generation\` events to have the following properties:
|
|
`}
|
|
</Markdown>
|
|
|
|
{NotableGenerationProperties && <NotableGenerationProperties />}
|
|
</>
|
|
),
|
|
},
|
|
{
|
|
title: 'Reusable configuration with instrument()',
|
|
badge: 'optional',
|
|
content: (
|
|
<>
|
|
<Markdown>
|
|
If you make multiple `query()` calls with the same Insights configuration, use `instrument()` to
|
|
configure once and reuse across queries.
|
|
</Markdown>
|
|
|
|
<CodeBlock
|
|
language="python"
|
|
code={dedent`
|
|
import asyncio
|
|
from insights import Insights
|
|
from insights.ai.claude_agent_sdk import instrument
|
|
from claude_agent_sdk import ClaudeAgentOptions
|
|
|
|
insights = Insights(
|
|
"<ph_project_token>",
|
|
host="<ph_client_api_host>"
|
|
)
|
|
|
|
ph = instrument(
|
|
client=insights,
|
|
distinct_id="user_123",
|
|
properties={"app": "my-agent"},
|
|
)
|
|
|
|
options = ClaudeAgentOptions(max_turns=10)
|
|
|
|
async def main():
|
|
# All queries share the same Insights 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())
|
|
`}
|
|
/>
|
|
|
|
<Markdown>You can override any Insights parameter per-query:</Markdown>
|
|
|
|
<CodeBlock
|
|
language="python"
|
|
code={dedent`
|
|
async for msg in ph.query(
|
|
prompt="...",
|
|
options=options,
|
|
insights_distinct_id="different_user",
|
|
insights_properties={"extra": "data"},
|
|
):
|
|
...
|
|
`}
|
|
/>
|
|
</>
|
|
),
|
|
},
|
|
{
|
|
title: 'Tool usage and multi-turn conversations',
|
|
badge: 'optional',
|
|
content: (
|
|
<>
|
|
<Markdown>
|
|
{dedent`
|
|
The recommended example above already runs a tool-using query, and \`max_turns\` lets
|
|
Claude take more than one internal turn before it replies. Inspect the message stream
|
|
to see each turn and tool call as they happen.
|
|
`}
|
|
</Markdown>
|
|
|
|
<CodeBlock
|
|
language="python"
|
|
code={dedent`
|
|
from claude_agent_sdk import AssistantMessage, TextBlock, ToolUseBlock
|
|
|
|
async for message in query(
|
|
prompt="Read the README and summarize this project",
|
|
options=options,
|
|
insights_client=insights,
|
|
insights_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`
|
|
Across the whole query, Insights 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
|
|
`}
|
|
</Markdown>
|
|
</>
|
|
),
|
|
},
|
|
{
|
|
title: 'Multi-turn conversations with history',
|
|
badge: 'optional',
|
|
content: (
|
|
<>
|
|
<Markdown>
|
|
For stateful, multi-turn conversations where each follow-up has full context from previous
|
|
turns, use `InsightsClaudeSDKClient`. This wraps the Claude Agent SDK's `ClaudeSDKClient` and
|
|
instruments each turn automatically. All turns share a single trace.
|
|
</Markdown>
|
|
|
|
<CodeBlock
|
|
language="python"
|
|
code={dedent`
|
|
from insights import Insights
|
|
from insights.ai.claude_agent_sdk import InsightsClaudeSDKClient
|
|
from claude_agent_sdk import ClaudeAgentOptions, AssistantMessage
|
|
from claude_agent_sdk.types import TextBlock
|
|
|
|
insights = Insights(
|
|
"<ph_project_token>",
|
|
host="<ph_client_api_host>"
|
|
)
|
|
|
|
options = ClaudeAgentOptions(max_turns=5)
|
|
|
|
async with InsightsClaudeSDKClient(
|
|
options,
|
|
insights_client=insights,
|
|
insights_distinct_id="user_123",
|
|
insights_properties={"app": "my-agent"},
|
|
) as client:
|
|
# Turn 1
|
|
await client.query("What is the capital of France?")
|
|
async for msg in client.receive_response():
|
|
if isinstance(msg, AssistantMessage):
|
|
for block in msg.content:
|
|
if isinstance(block, TextBlock):
|
|
print(block.text)
|
|
|
|
# Turn 2 — has full conversation history
|
|
await client.query("What language do they speak there?")
|
|
async for msg in client.receive_response():
|
|
if isinstance(msg, AssistantMessage):
|
|
for block in msg.content:
|
|
if isinstance(block, TextBlock):
|
|
print(block.text)
|
|
`}
|
|
/>
|
|
|
|
<Markdown>
|
|
{dedent`
|
|
Each \`receive_response()\` cycle emits \`$ai_generation\` events for that turn. When the
|
|
client disconnects, exiting the \`async with\` block, it emits a single \`$ai_trace\`
|
|
event covering the entire session with aggregate latency.
|
|
`}
|
|
</Markdown>
|
|
</>
|
|
),
|
|
},
|
|
]
|
|
}
|
|
|
|
export const ClaudeAgentSDKInstallation = createInstallation(getClaudeAgentSDKSteps)
|