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
5 changes: 5 additions & 0 deletions .sampo/changesets/claude-agent-sdk-graceful-fallback.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
pypi/posthog: patch
---

fix: graceful fallback in claude_agent_sdk query wrapper when PostHog is not configured
32 changes: 25 additions & 7 deletions posthog/ai/claude_agent_sdk/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import logging
from typing import TYPE_CHECKING, Any, Callable, Dict, Optional, Union

if TYPE_CHECKING:
Expand All @@ -17,6 +18,8 @@
from posthog.ai.claude_agent_sdk.client import PostHogClaudeSDKClient
from posthog.ai.claude_agent_sdk.processor import PostHogClaudeAgentProcessor

log = logging.getLogger("posthog")

__all__ = [
"PostHogClaudeAgentProcessor",
"PostHogClaudeSDKClient",
Expand Down Expand Up @@ -113,13 +116,28 @@ async def query(
print(message)
```
"""
processor = PostHogClaudeAgentProcessor(
client=posthog_client,
distinct_id=posthog_distinct_id,
privacy_mode=posthog_privacy_mode,
groups=posthog_groups,
properties={},
)
from claude_agent_sdk import query as original_query

try:
processor = PostHogClaudeAgentProcessor(
client=posthog_client,
distinct_id=posthog_distinct_id,
privacy_mode=posthog_privacy_mode,
groups=posthog_groups,
properties={},
)
except ValueError as e:
# PostHog is not configured (missing API key); fall back to the
# plain SDK so callers are never broken by missing instrumentation.
log.warning(
"PostHog instrumentation disabled: %s — falling back to plain claude_agent_sdk.query()",
e,
)
async for message in original_query(
prompt=prompt, options=options, transport=transport
):
yield message
return

async for message in processor.query(
prompt=prompt,
Expand Down
39 changes: 39 additions & 0 deletions posthog/test/ai/claude_agent_sdk/test_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from posthog.ai.claude_agent_sdk import (
PostHogClaudeAgentProcessor,
instrument,
query as posthog_query,
)

CLAUDE_AGENT_SDK_AVAILABLE = True
Expand Down Expand Up @@ -580,3 +581,41 @@ async def fake_query_capture(**kwargs):
pass

assert captured_options.get("options").include_partial_messages is True


class TestQueryGracefulFallback:
@pytest.mark.asyncio
async def test_falls_back_to_original_query_when_posthog_not_configured(self):
result_msg = _make_result_message()
messages_from_sdk = [result_msg]

with (
patch(
"posthog.ai.claude_agent_sdk.PostHogClaudeAgentProcessor",
side_effect=ValueError("API key is required"),
),
patch(
"claude_agent_sdk.query",
side_effect=lambda **kwargs: _fake_query(messages_from_sdk),
),
):
collected = []
async for msg in posthog_query(
prompt="Hello", options=ClaudeAgentOptions()
):
collected.append(msg)

assert len(collected) == 1
assert collected[0] is result_msg

@pytest.mark.asyncio
async def test_non_config_errors_propagate(self):
with patch(
"posthog.ai.claude_agent_sdk.PostHogClaudeAgentProcessor",
side_effect=RuntimeError("unexpected init bug"),
):
with pytest.raises(RuntimeError, match="unexpected init bug"):
async for _ in posthog_query(
prompt="Hello", options=ClaudeAgentOptions()
):
pass
Loading