rebrand: remove all compat aliases, zero posthog refs
- Remove Posthog = Insights alias from __init__.py - Remove PosthogContextMiddleware alias from django.py - Remove PostHogTracingProcessor alias from processor.py - Change $lib from "posthog-python" to "insights-python" - Change ingestion URLs from posthog.com to insights.hanzo.ai - Rename all posthog_* kwargs to insights_* across AI wrappers - Rename __posthog_exception_captured to __insights_exception_captured - Rename posthog_context_stack contextvar to insights_context_stack - Rename posthog🎏 Redis prefix to insights🎏 - Rename $$_posthog_redacted_* sentinels to $$_insights_redacted_* - Remove POSTHOG_MW_* Django settings fallback, X-POSTHOG-* headers - Rename Prompts(posthog=) param to Prompts(client=) - Update APP_ENDPOINT to us.insights.hanzo.ai - Update all tests, examples, docs, mypy config
This commit is contained in:
@@ -17,7 +17,7 @@ uv run pytest
|
||||
```
|
||||
posthog-python/
|
||||
hanzo_insights/ # Main package
|
||||
__init__.py # Module-level API, Insights class (alias: Posthog)
|
||||
__init__.py # Module-level API, Insights class
|
||||
client.py # Client class
|
||||
ai/ # AI provider integrations (OpenAI, Anthropic, Gemini, LangChain)
|
||||
integrations/ # Framework integrations (Django middleware)
|
||||
@@ -34,8 +34,15 @@ posthog-python/
|
||||
- `hanzo_insights/client.py` -- Client implementation
|
||||
|
||||
## Rebrand Notes
|
||||
- Main class: `Insights` (backward compat alias: `Posthog = Insights`)
|
||||
- Django middleware: `InsightsContextMiddleware` (alias: `PosthogContextMiddleware`)
|
||||
- OpenAI Agents: `InsightsTracingProcessor` (alias: `PostHogTracingProcessor`)
|
||||
- Internal protocol values (`$lib`, `posthog.com` ingestion URLs) kept for server compat
|
||||
- `posthog_*` parameter names in AI wrappers kept for API compat
|
||||
- Main class: `Insights` (no backward compat aliases)
|
||||
- Django middleware: `InsightsContextMiddleware` (no backward compat aliases)
|
||||
- OpenAI Agents: `InsightsTracingProcessor` (no backward compat aliases)
|
||||
- `$lib` protocol value: `insights-python`
|
||||
- Ingestion URLs: `us.i.insights.hanzo.ai` / `eu.i.insights.hanzo.ai`
|
||||
- AI wrapper kwargs: `insights_*` (e.g. `insights_distinct_id`, `insights_trace_id`)
|
||||
- Exception attrs: `__insights_exception_captured`, `__insights_exception_uuid`
|
||||
- Context var: `insights_context_stack`
|
||||
- Redis prefix: `insights:flags:`
|
||||
- Redaction sentinels: `$$_insights_redacted_*`, `$$_insights_value_too_long_*`
|
||||
- Django settings: `INSIGHTS_MW_*` only (no `POSTHOG_MW_*` fallback)
|
||||
- Django headers: `X-INSIGHTS-SESSION-ID`, `X-INSIGHTS-DISTINCT-ID` only
|
||||
|
||||
@@ -65,15 +65,6 @@ make test
|
||||
pytest -k test_no_api_key
|
||||
```
|
||||
|
||||
## Backward Compatibility
|
||||
|
||||
For users migrating from `posthog` or `posthoganalytics`, the `Posthog` class name is
|
||||
available as an alias for `Insights`:
|
||||
|
||||
```python
|
||||
from hanzo_insights import Posthog # works, same as Insights
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
@@ -4,5 +4,5 @@
|
||||
source bin/helpers/_utils.sh
|
||||
set_source_and_root_dir
|
||||
|
||||
flake8 posthog --ignore E501,W503
|
||||
flake8 hanzo_insights --ignore E501,W503
|
||||
mypy --no-site-packages --config-file mypy.ini . | mypy-baseline filter
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
#/ Usage: bin/docs
|
||||
#/ Description: Generate documentation for the PostHog Python SDK
|
||||
#/ Description: Generate documentation for the Insights Python SDK
|
||||
source bin/helpers/_utils.sh
|
||||
set_source_and_root_dir
|
||||
ensure_virtual_env
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
Constants for PostHog Python SDK documentation generation.
|
||||
Constants for Insights Python SDK documentation generation.
|
||||
"""
|
||||
|
||||
from typing import Dict, Union
|
||||
@@ -8,8 +8,8 @@ from hanzo_insights.version import VERSION
|
||||
# Documentation generation metadata
|
||||
DOCUMENTATION_METADATA = {
|
||||
"hogRef": "0.3",
|
||||
"slugPrefix": "posthog-python",
|
||||
"specUrl": "https://github.com/PostHog/posthog-python",
|
||||
"slugPrefix": "insights-python",
|
||||
"specUrl": "https://github.com/Insights/insights-python",
|
||||
}
|
||||
|
||||
# Docstring parsing patterns for new format
|
||||
@@ -29,8 +29,8 @@ DOCSTRING_PATTERNS = {
|
||||
# Output file configuration
|
||||
OUTPUT_CONFIG: Dict[str, Union[str, int]] = {
|
||||
"output_dir": "./references",
|
||||
"filename": f"posthog-python-references-{VERSION}.json",
|
||||
"filename_latest": "posthog-python-references-latest.json",
|
||||
"filename": f"insights-python-references-{VERSION}.json",
|
||||
"filename_latest": "insights-python-references-latest.json",
|
||||
"indent": 2,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Generate comprehensive SDK documentation JSON from PostHog Python SDK.
|
||||
Generate comprehensive SDK documentation JSON from Insights Python SDK.
|
||||
This script inspects the code and docstrings to create documentation in the specified format.
|
||||
"""
|
||||
|
||||
@@ -337,8 +337,8 @@ def analyze_type(cls) -> dict:
|
||||
def generate_sdk_documentation():
|
||||
"""Generate complete SDK documentation in the requested format."""
|
||||
|
||||
# Import PostHog components
|
||||
import posthog
|
||||
# Import Insights components
|
||||
import hanzo_insights
|
||||
from hanzo_insights.client import Client
|
||||
import hanzo_insights.types as types_module
|
||||
import hanzo_insights.args as args_module
|
||||
@@ -347,9 +347,9 @@ def generate_sdk_documentation():
|
||||
# Main SDK info
|
||||
sdk_info = {
|
||||
"version": VERSION,
|
||||
"id": "posthog-python",
|
||||
"title": "PostHog Python SDK",
|
||||
"description": "Integrate PostHog into any python application.",
|
||||
"id": "insights-python",
|
||||
"title": "Insights Python SDK",
|
||||
"description": "Integrate Insights into any python application.",
|
||||
"slugPrefix": DOCUMENTATION_METADATA["slugPrefix"],
|
||||
"specUrl": DOCUMENTATION_METADATA["specUrl"],
|
||||
}
|
||||
@@ -388,24 +388,24 @@ def generate_sdk_documentation():
|
||||
# Collect classes
|
||||
classes_list = []
|
||||
|
||||
# Main PostHog class (renamed from Client)
|
||||
# Main Insights class (renamed from Client)
|
||||
client_class = analyze_class(Client)
|
||||
client_class["id"] = "PostHog"
|
||||
client_class["title"] = "PostHog"
|
||||
client_class["id"] = "Insights"
|
||||
client_class["title"] = "Insights"
|
||||
classes_list.append(client_class)
|
||||
|
||||
# Global module functions (functions callable as hanzo_insights.function_name)
|
||||
global_functions = []
|
||||
for func_name in dir(posthog):
|
||||
for func_name in dir(hanzo_insights):
|
||||
# Skip private functions and non-callables
|
||||
if func_name.startswith("_") or not callable(getattr(posthog, func_name)):
|
||||
if func_name.startswith("_") or not callable(getattr(hanzo_insights, func_name)):
|
||||
continue
|
||||
|
||||
func = getattr(posthog, func_name)
|
||||
# Only include functions actually defined in the posthog module (not imported)
|
||||
func = getattr(hanzo_insights, func_name)
|
||||
# Only include functions actually defined in the hanzo_insights module (not imported)
|
||||
# and exclude class references
|
||||
if (
|
||||
func_name not in ["Client", "Posthog"]
|
||||
func_name not in ["Client", "Insights"]
|
||||
and hasattr(func, "__module__")
|
||||
and func.__module__ == "hanzo_insights"
|
||||
):
|
||||
@@ -421,8 +421,8 @@ def generate_sdk_documentation():
|
||||
classes_list.append(
|
||||
{
|
||||
"id": "PostHogModule",
|
||||
"title": "PostHog Module Functions",
|
||||
"description": "Global functions available in the PostHog module",
|
||||
"title": "Insights Module Functions",
|
||||
"description": "Global functions available in the Insights module",
|
||||
"functions": global_functions,
|
||||
}
|
||||
)
|
||||
@@ -443,7 +443,7 @@ def generate_sdk_documentation():
|
||||
|
||||
# Create the final structure
|
||||
result = {
|
||||
"id": "posthog-python",
|
||||
"id": "insights-python",
|
||||
"hogRef": DOCUMENTATION_METADATA["hogRef"],
|
||||
"info": sdk_info,
|
||||
"types": types_list,
|
||||
@@ -455,7 +455,7 @@ def generate_sdk_documentation():
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("Generating PostHog Python SDK documentation...")
|
||||
print("Generating Insights Python SDK documentation...")
|
||||
|
||||
try:
|
||||
documentation = generate_sdk_documentation()
|
||||
|
||||
@@ -6,7 +6,7 @@ using Redis for multi-instance deployments (leader election pattern).
|
||||
|
||||
Usage:
|
||||
import redis
|
||||
from hanzo_insights import Posthog
|
||||
from hanzo_insights import Insights
|
||||
|
||||
redis_client = redis.Redis(host='localhost', port=6379, decode_responses=True)
|
||||
cache = RedisFlagCache(redis_client, service_key="my-service")
|
||||
@@ -83,8 +83,8 @@ class RedisFlagCache(FlagDefinitionCacheProvider):
|
||||
Examples: "my-api-prod", "checkout-service", "staging".
|
||||
|
||||
Redis Keys Created:
|
||||
- posthog:flags:{service_key} - Cached flag definitions (JSON)
|
||||
- posthog:flags:{service_key}:lock - Leader election lock
|
||||
- insights:flags:{service_key} - Cached flag definitions (JSON)
|
||||
- insights:flags:{service_key}:lock - Leader election lock
|
||||
|
||||
Example:
|
||||
redis_client = redis.Redis(
|
||||
@@ -95,8 +95,8 @@ class RedisFlagCache(FlagDefinitionCacheProvider):
|
||||
cache = RedisFlagCache(redis_client, service_key="my-api-prod")
|
||||
"""
|
||||
self._redis = redis
|
||||
self._cache_key = f"posthog:flags:{service_key}"
|
||||
self._lock_key = f"posthog:flags:{service_key}:lock"
|
||||
self._cache_key = f"insights:flags:{service_key}"
|
||||
self._lock_key = f"insights:flags:{service_key}:lock"
|
||||
self._instance_id = str(uuid.uuid4())
|
||||
self._try_lead = self._redis.register_script(self._LUA_TRY_LEAD)
|
||||
self._stop_lead = self._redis.register_script(self._LUA_STOP_LEAD)
|
||||
|
||||
@@ -8,7 +8,7 @@ import hanzo_insights
|
||||
# Initialize Insights client
|
||||
hanzo_insights.api_key = "phc_..."
|
||||
hanzo_insights.personal_api_key = "phs_..." # or "phx_..."
|
||||
hanzo_insights.host = "http://localhost:8000" # or "https://us.posthog.com"
|
||||
hanzo_insights.host = "http://localhost:8000" # or "https://us.insights.hanzo.ai"
|
||||
hanzo_insights.debug = True
|
||||
|
||||
|
||||
|
||||
@@ -892,7 +892,3 @@ class Insights(Client):
|
||||
"""Hanzo Insights client for product analytics."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
# Backward compatibility alias
|
||||
Posthog = Insights
|
||||
|
||||
@@ -34,14 +34,14 @@ class Anthropic(anthropic.Anthropic):
|
||||
|
||||
_ph_client: InsightsClient
|
||||
|
||||
def __init__(self, posthog_client: Optional[InsightsClient] = None, **kwargs):
|
||||
def __init__(self, insights_client: Optional[InsightsClient] = None, **kwargs):
|
||||
"""
|
||||
Args:
|
||||
posthog_client: Insights client for tracking usage
|
||||
insights_client: Insights client for tracking usage
|
||||
**kwargs: Additional arguments passed to the Anthropic client
|
||||
"""
|
||||
super().__init__(**kwargs)
|
||||
self._ph_client = posthog_client or setup()
|
||||
self._ph_client = insights_client or setup()
|
||||
self.messages = WrappedMessages(self)
|
||||
|
||||
|
||||
@@ -50,46 +50,46 @@ class WrappedMessages(Messages):
|
||||
|
||||
def create(
|
||||
self,
|
||||
posthog_distinct_id: Optional[str] = None,
|
||||
posthog_trace_id: Optional[str] = None,
|
||||
posthog_properties: Optional[Dict[str, Any]] = None,
|
||||
posthog_privacy_mode: bool = False,
|
||||
posthog_groups: Optional[Dict[str, Any]] = None,
|
||||
insights_distinct_id: Optional[str] = None,
|
||||
insights_trace_id: Optional[str] = None,
|
||||
insights_properties: Optional[Dict[str, Any]] = None,
|
||||
insights_privacy_mode: bool = False,
|
||||
insights_groups: Optional[Dict[str, Any]] = None,
|
||||
**kwargs: Any,
|
||||
):
|
||||
"""
|
||||
Create a message using Anthropic's API while tracking usage in Insights.
|
||||
|
||||
Args:
|
||||
posthog_distinct_id: Optional ID to associate with the usage event
|
||||
posthog_trace_id: Optional trace UUID for linking events
|
||||
posthog_properties: Optional dictionary of extra properties to include in the event
|
||||
posthog_privacy_mode: Whether to redact sensitive information in tracking
|
||||
posthog_groups: Optional group analytics properties
|
||||
insights_distinct_id: Optional ID to associate with the usage event
|
||||
insights_trace_id: Optional trace UUID for linking events
|
||||
insights_properties: Optional dictionary of extra properties to include in the event
|
||||
insights_privacy_mode: Whether to redact sensitive information in tracking
|
||||
insights_groups: Optional group analytics properties
|
||||
**kwargs: Arguments passed to Anthropic's messages.create
|
||||
"""
|
||||
|
||||
if posthog_trace_id is None:
|
||||
posthog_trace_id = str(uuid.uuid4())
|
||||
if insights_trace_id is None:
|
||||
insights_trace_id = str(uuid.uuid4())
|
||||
|
||||
if kwargs.get("stream", False):
|
||||
return self._create_streaming(
|
||||
posthog_distinct_id,
|
||||
posthog_trace_id,
|
||||
posthog_properties,
|
||||
posthog_privacy_mode,
|
||||
posthog_groups,
|
||||
insights_distinct_id,
|
||||
insights_trace_id,
|
||||
insights_properties,
|
||||
insights_privacy_mode,
|
||||
insights_groups,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
return call_llm_and_track_usage(
|
||||
posthog_distinct_id,
|
||||
insights_distinct_id,
|
||||
self._client._ph_client,
|
||||
"anthropic",
|
||||
posthog_trace_id,
|
||||
posthog_properties,
|
||||
posthog_privacy_mode,
|
||||
posthog_groups,
|
||||
insights_trace_id,
|
||||
insights_properties,
|
||||
insights_privacy_mode,
|
||||
insights_groups,
|
||||
self._client.base_url,
|
||||
super().create,
|
||||
**kwargs,
|
||||
@@ -97,32 +97,32 @@ class WrappedMessages(Messages):
|
||||
|
||||
def stream(
|
||||
self,
|
||||
posthog_distinct_id: Optional[str] = None,
|
||||
posthog_trace_id: Optional[str] = None,
|
||||
posthog_properties: Optional[Dict[str, Any]] = None,
|
||||
posthog_privacy_mode: bool = False,
|
||||
posthog_groups: Optional[Dict[str, Any]] = None,
|
||||
insights_distinct_id: Optional[str] = None,
|
||||
insights_trace_id: Optional[str] = None,
|
||||
insights_properties: Optional[Dict[str, Any]] = None,
|
||||
insights_privacy_mode: bool = False,
|
||||
insights_groups: Optional[Dict[str, Any]] = None,
|
||||
**kwargs: Any,
|
||||
):
|
||||
if posthog_trace_id is None:
|
||||
posthog_trace_id = str(uuid.uuid4())
|
||||
if insights_trace_id is None:
|
||||
insights_trace_id = str(uuid.uuid4())
|
||||
|
||||
return self._create_streaming(
|
||||
posthog_distinct_id,
|
||||
posthog_trace_id,
|
||||
posthog_properties,
|
||||
posthog_privacy_mode,
|
||||
posthog_groups,
|
||||
insights_distinct_id,
|
||||
insights_trace_id,
|
||||
insights_properties,
|
||||
insights_privacy_mode,
|
||||
insights_groups,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
def _create_streaming(
|
||||
self,
|
||||
posthog_distinct_id: Optional[str],
|
||||
posthog_trace_id: Optional[str],
|
||||
posthog_properties: Optional[Dict[str, Any]],
|
||||
posthog_privacy_mode: bool,
|
||||
posthog_groups: Optional[Dict[str, Any]],
|
||||
insights_distinct_id: Optional[str],
|
||||
insights_trace_id: Optional[str],
|
||||
insights_properties: Optional[Dict[str, Any]],
|
||||
insights_privacy_mode: bool,
|
||||
insights_groups: Optional[Dict[str, Any]],
|
||||
**kwargs: Any,
|
||||
):
|
||||
start_time = time.time()
|
||||
@@ -188,11 +188,11 @@ class WrappedMessages(Messages):
|
||||
latency = end_time - start_time
|
||||
|
||||
self._capture_streaming_event(
|
||||
posthog_distinct_id,
|
||||
posthog_trace_id,
|
||||
posthog_properties,
|
||||
posthog_privacy_mode,
|
||||
posthog_groups,
|
||||
insights_distinct_id,
|
||||
insights_trace_id,
|
||||
insights_properties,
|
||||
insights_privacy_mode,
|
||||
insights_groups,
|
||||
kwargs,
|
||||
usage_stats,
|
||||
latency,
|
||||
@@ -204,11 +204,11 @@ class WrappedMessages(Messages):
|
||||
|
||||
def _capture_streaming_event(
|
||||
self,
|
||||
posthog_distinct_id: Optional[str],
|
||||
posthog_trace_id: Optional[str],
|
||||
posthog_properties: Optional[Dict[str, Any]],
|
||||
posthog_privacy_mode: bool,
|
||||
posthog_groups: Optional[Dict[str, Any]],
|
||||
insights_distinct_id: Optional[str],
|
||||
insights_trace_id: Optional[str],
|
||||
insights_properties: Optional[Dict[str, Any]],
|
||||
insights_privacy_mode: bool,
|
||||
insights_groups: Optional[Dict[str, Any]],
|
||||
kwargs: Dict[str, Any],
|
||||
usage_stats: TokenUsage,
|
||||
latency: float,
|
||||
@@ -237,11 +237,11 @@ class WrappedMessages(Messages):
|
||||
),
|
||||
usage_stats=usage_stats,
|
||||
latency=latency,
|
||||
distinct_id=posthog_distinct_id,
|
||||
trace_id=posthog_trace_id,
|
||||
properties=posthog_properties,
|
||||
privacy_mode=posthog_privacy_mode,
|
||||
groups=posthog_groups,
|
||||
distinct_id=insights_distinct_id,
|
||||
trace_id=insights_trace_id,
|
||||
properties=insights_properties,
|
||||
privacy_mode=insights_privacy_mode,
|
||||
groups=insights_groups,
|
||||
)
|
||||
|
||||
# Use the common capture function
|
||||
|
||||
@@ -34,14 +34,14 @@ class AsyncAnthropic(anthropic.AsyncAnthropic):
|
||||
|
||||
_ph_client: InsightsClient
|
||||
|
||||
def __init__(self, posthog_client: Optional[InsightsClient] = None, **kwargs):
|
||||
def __init__(self, insights_client: Optional[InsightsClient] = None, **kwargs):
|
||||
"""
|
||||
Args:
|
||||
posthog_client: Insights client for tracking usage
|
||||
insights_client: Insights client for tracking usage
|
||||
**kwargs: Additional arguments passed to the Anthropic client
|
||||
"""
|
||||
super().__init__(**kwargs)
|
||||
self._ph_client = posthog_client or setup()
|
||||
self._ph_client = insights_client or setup()
|
||||
self.messages = AsyncWrappedMessages(self)
|
||||
|
||||
|
||||
@@ -50,46 +50,46 @@ class AsyncWrappedMessages(AsyncMessages):
|
||||
|
||||
async def create(
|
||||
self,
|
||||
posthog_distinct_id: Optional[str] = None,
|
||||
posthog_trace_id: Optional[str] = None,
|
||||
posthog_properties: Optional[Dict[str, Any]] = None,
|
||||
posthog_privacy_mode: bool = False,
|
||||
posthog_groups: Optional[Dict[str, Any]] = None,
|
||||
insights_distinct_id: Optional[str] = None,
|
||||
insights_trace_id: Optional[str] = None,
|
||||
insights_properties: Optional[Dict[str, Any]] = None,
|
||||
insights_privacy_mode: bool = False,
|
||||
insights_groups: Optional[Dict[str, Any]] = None,
|
||||
**kwargs: Any,
|
||||
):
|
||||
"""
|
||||
Create a message using Anthropic's API while tracking usage in Insights.
|
||||
|
||||
Args:
|
||||
posthog_distinct_id: Optional ID to associate with the usage event
|
||||
posthog_trace_id: Optional trace UUID for linking events
|
||||
posthog_properties: Optional dictionary of extra properties to include in the event
|
||||
posthog_privacy_mode: Whether to redact sensitive information in tracking
|
||||
posthog_groups: Optional group analytics properties
|
||||
insights_distinct_id: Optional ID to associate with the usage event
|
||||
insights_trace_id: Optional trace UUID for linking events
|
||||
insights_properties: Optional dictionary of extra properties to include in the event
|
||||
insights_privacy_mode: Whether to redact sensitive information in tracking
|
||||
insights_groups: Optional group analytics properties
|
||||
**kwargs: Arguments passed to Anthropic's messages.create
|
||||
"""
|
||||
|
||||
if posthog_trace_id is None:
|
||||
posthog_trace_id = str(uuid.uuid4())
|
||||
if insights_trace_id is None:
|
||||
insights_trace_id = str(uuid.uuid4())
|
||||
|
||||
if kwargs.get("stream", False):
|
||||
return await self._create_streaming(
|
||||
posthog_distinct_id,
|
||||
posthog_trace_id,
|
||||
posthog_properties,
|
||||
posthog_privacy_mode,
|
||||
posthog_groups,
|
||||
insights_distinct_id,
|
||||
insights_trace_id,
|
||||
insights_properties,
|
||||
insights_privacy_mode,
|
||||
insights_groups,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
return await call_llm_and_track_usage_async(
|
||||
posthog_distinct_id,
|
||||
insights_distinct_id,
|
||||
self._client._ph_client,
|
||||
"anthropic",
|
||||
posthog_trace_id,
|
||||
posthog_properties,
|
||||
posthog_privacy_mode,
|
||||
posthog_groups,
|
||||
insights_trace_id,
|
||||
insights_properties,
|
||||
insights_privacy_mode,
|
||||
insights_groups,
|
||||
self._client.base_url,
|
||||
super().create,
|
||||
**kwargs,
|
||||
@@ -97,32 +97,32 @@ class AsyncWrappedMessages(AsyncMessages):
|
||||
|
||||
async def stream(
|
||||
self,
|
||||
posthog_distinct_id: Optional[str] = None,
|
||||
posthog_trace_id: Optional[str] = None,
|
||||
posthog_properties: Optional[Dict[str, Any]] = None,
|
||||
posthog_privacy_mode: bool = False,
|
||||
posthog_groups: Optional[Dict[str, Any]] = None,
|
||||
insights_distinct_id: Optional[str] = None,
|
||||
insights_trace_id: Optional[str] = None,
|
||||
insights_properties: Optional[Dict[str, Any]] = None,
|
||||
insights_privacy_mode: bool = False,
|
||||
insights_groups: Optional[Dict[str, Any]] = None,
|
||||
**kwargs: Any,
|
||||
):
|
||||
if posthog_trace_id is None:
|
||||
posthog_trace_id = str(uuid.uuid4())
|
||||
if insights_trace_id is None:
|
||||
insights_trace_id = str(uuid.uuid4())
|
||||
|
||||
return await self._create_streaming(
|
||||
posthog_distinct_id,
|
||||
posthog_trace_id,
|
||||
posthog_properties,
|
||||
posthog_privacy_mode,
|
||||
posthog_groups,
|
||||
insights_distinct_id,
|
||||
insights_trace_id,
|
||||
insights_properties,
|
||||
insights_privacy_mode,
|
||||
insights_groups,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
async def _create_streaming(
|
||||
self,
|
||||
posthog_distinct_id: Optional[str],
|
||||
posthog_trace_id: Optional[str],
|
||||
posthog_properties: Optional[Dict[str, Any]],
|
||||
posthog_privacy_mode: bool,
|
||||
posthog_groups: Optional[Dict[str, Any]],
|
||||
insights_distinct_id: Optional[str],
|
||||
insights_trace_id: Optional[str],
|
||||
insights_properties: Optional[Dict[str, Any]],
|
||||
insights_privacy_mode: bool,
|
||||
insights_groups: Optional[Dict[str, Any]],
|
||||
**kwargs: Any,
|
||||
):
|
||||
start_time = time.time()
|
||||
@@ -188,11 +188,11 @@ class AsyncWrappedMessages(AsyncMessages):
|
||||
latency = end_time - start_time
|
||||
|
||||
await self._capture_streaming_event(
|
||||
posthog_distinct_id,
|
||||
posthog_trace_id,
|
||||
posthog_properties,
|
||||
posthog_privacy_mode,
|
||||
posthog_groups,
|
||||
insights_distinct_id,
|
||||
insights_trace_id,
|
||||
insights_properties,
|
||||
insights_privacy_mode,
|
||||
insights_groups,
|
||||
kwargs,
|
||||
usage_stats,
|
||||
latency,
|
||||
@@ -204,11 +204,11 @@ class AsyncWrappedMessages(AsyncMessages):
|
||||
|
||||
async def _capture_streaming_event(
|
||||
self,
|
||||
posthog_distinct_id: Optional[str],
|
||||
posthog_trace_id: Optional[str],
|
||||
posthog_properties: Optional[Dict[str, Any]],
|
||||
posthog_privacy_mode: bool,
|
||||
posthog_groups: Optional[Dict[str, Any]],
|
||||
insights_distinct_id: Optional[str],
|
||||
insights_trace_id: Optional[str],
|
||||
insights_properties: Optional[Dict[str, Any]],
|
||||
insights_privacy_mode: bool,
|
||||
insights_groups: Optional[Dict[str, Any]],
|
||||
kwargs: Dict[str, Any],
|
||||
usage_stats: TokenUsage,
|
||||
latency: float,
|
||||
@@ -237,11 +237,11 @@ class AsyncWrappedMessages(AsyncMessages):
|
||||
),
|
||||
usage_stats=usage_stats,
|
||||
latency=latency,
|
||||
distinct_id=posthog_distinct_id,
|
||||
trace_id=posthog_trace_id,
|
||||
properties=posthog_properties,
|
||||
privacy_mode=posthog_privacy_mode,
|
||||
groups=posthog_groups,
|
||||
distinct_id=insights_distinct_id,
|
||||
trace_id=insights_trace_id,
|
||||
properties=insights_properties,
|
||||
privacy_mode=insights_privacy_mode,
|
||||
groups=insights_groups,
|
||||
)
|
||||
|
||||
# Use the common capture function
|
||||
|
||||
@@ -20,9 +20,9 @@ class AnthropicBedrock(anthropic.AnthropicBedrock):
|
||||
|
||||
_ph_client: InsightsClient
|
||||
|
||||
def __init__(self, posthog_client: Optional[InsightsClient] = None, **kwargs):
|
||||
def __init__(self, insights_client: Optional[InsightsClient] = None, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self._ph_client = posthog_client or setup()
|
||||
self._ph_client = insights_client or setup()
|
||||
self.messages = WrappedMessages(self)
|
||||
|
||||
|
||||
@@ -33,9 +33,9 @@ class AsyncAnthropicBedrock(anthropic.AsyncAnthropicBedrock):
|
||||
|
||||
_ph_client: InsightsClient
|
||||
|
||||
def __init__(self, posthog_client: Optional[InsightsClient] = None, **kwargs):
|
||||
def __init__(self, insights_client: Optional[InsightsClient] = None, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self._ph_client = posthog_client or setup()
|
||||
self._ph_client = insights_client or setup()
|
||||
self.messages = AsyncWrappedMessages(self)
|
||||
|
||||
|
||||
@@ -46,9 +46,9 @@ class AnthropicVertex(anthropic.AnthropicVertex):
|
||||
|
||||
_ph_client: InsightsClient
|
||||
|
||||
def __init__(self, posthog_client: Optional[InsightsClient] = None, **kwargs):
|
||||
def __init__(self, insights_client: Optional[InsightsClient] = None, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self._ph_client = posthog_client or setup()
|
||||
self._ph_client = insights_client or setup()
|
||||
self.messages = WrappedMessages(self)
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ class AsyncAnthropicVertex(anthropic.AsyncAnthropicVertex):
|
||||
|
||||
_ph_client: InsightsClient
|
||||
|
||||
def __init__(self, posthog_client: Optional[InsightsClient] = None, **kwargs):
|
||||
def __init__(self, insights_client: Optional[InsightsClient] = None, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self._ph_client = posthog_client or setup()
|
||||
self._ph_client = insights_client or setup()
|
||||
self.messages = AsyncWrappedMessages(self)
|
||||
|
||||
@@ -35,14 +35,14 @@ class Client:
|
||||
Usage:
|
||||
client = Client(
|
||||
api_key="your_api_key",
|
||||
posthog_client=posthog_client,
|
||||
posthog_distinct_id="default_user", # Optional defaults
|
||||
posthog_properties={"team": "ai"} # Optional defaults
|
||||
insights_client=insights_client,
|
||||
insights_distinct_id="default_user", # Optional defaults
|
||||
insights_properties={"team": "ai"} # Optional defaults
|
||||
)
|
||||
response = client.models.generate_content(
|
||||
model="gemini-2.0-flash",
|
||||
contents=["Hello world"],
|
||||
posthog_distinct_id="specific_user" # Override default
|
||||
insights_distinct_id="specific_user" # Override default
|
||||
)
|
||||
"""
|
||||
|
||||
@@ -57,11 +57,11 @@ class Client:
|
||||
location: Optional[str] = None,
|
||||
debug_config: Optional[Any] = None,
|
||||
http_options: Optional[Any] = None,
|
||||
posthog_client: Optional[InsightsClient] = None,
|
||||
posthog_distinct_id: Optional[str] = None,
|
||||
posthog_properties: Optional[Dict[str, Any]] = None,
|
||||
posthog_privacy_mode: bool = False,
|
||||
posthog_groups: Optional[Dict[str, Any]] = None,
|
||||
insights_client: Optional[InsightsClient] = None,
|
||||
insights_distinct_id: Optional[str] = None,
|
||||
insights_properties: Optional[Dict[str, Any]] = None,
|
||||
insights_privacy_mode: bool = False,
|
||||
insights_groups: Optional[Dict[str, Any]] = None,
|
||||
**kwargs,
|
||||
):
|
||||
"""
|
||||
@@ -73,18 +73,18 @@ class Client:
|
||||
location: GCP location for Vertex AI
|
||||
debug_config: Debug configuration for the client
|
||||
http_options: HTTP options for the client
|
||||
posthog_client: Insights client for tracking usage
|
||||
posthog_distinct_id: Default distinct ID for all calls (can be overridden per call)
|
||||
posthog_properties: Default properties for all calls (can be overridden per call)
|
||||
posthog_privacy_mode: Default privacy mode for all calls (can be overridden per call)
|
||||
posthog_groups: Default groups for all calls (can be overridden per call)
|
||||
insights_client: Insights client for tracking usage
|
||||
insights_distinct_id: Default distinct ID for all calls (can be overridden per call)
|
||||
insights_properties: Default properties for all calls (can be overridden per call)
|
||||
insights_privacy_mode: Default privacy mode for all calls (can be overridden per call)
|
||||
insights_groups: Default groups for all calls (can be overridden per call)
|
||||
**kwargs: Additional arguments (for future compatibility)
|
||||
"""
|
||||
|
||||
self._ph_client = posthog_client or setup()
|
||||
self._ph_client = insights_client or setup()
|
||||
|
||||
if self._ph_client is None:
|
||||
raise ValueError("posthog_client is required for Insights tracking")
|
||||
raise ValueError("insights_client is required for Insights tracking")
|
||||
|
||||
self.models = Models(
|
||||
api_key=api_key,
|
||||
@@ -94,11 +94,11 @@ class Client:
|
||||
location=location,
|
||||
debug_config=debug_config,
|
||||
http_options=http_options,
|
||||
posthog_client=self._ph_client,
|
||||
posthog_distinct_id=posthog_distinct_id,
|
||||
posthog_properties=posthog_properties,
|
||||
posthog_privacy_mode=posthog_privacy_mode,
|
||||
posthog_groups=posthog_groups,
|
||||
insights_client=self._ph_client,
|
||||
insights_distinct_id=insights_distinct_id,
|
||||
insights_properties=insights_properties,
|
||||
insights_privacy_mode=insights_privacy_mode,
|
||||
insights_groups=insights_groups,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
@@ -119,11 +119,11 @@ class Models:
|
||||
location: Optional[str] = None,
|
||||
debug_config: Optional[Any] = None,
|
||||
http_options: Optional[Any] = None,
|
||||
posthog_client: Optional[InsightsClient] = None,
|
||||
posthog_distinct_id: Optional[str] = None,
|
||||
posthog_properties: Optional[Dict[str, Any]] = None,
|
||||
posthog_privacy_mode: bool = False,
|
||||
posthog_groups: Optional[Dict[str, Any]] = None,
|
||||
insights_client: Optional[InsightsClient] = None,
|
||||
insights_distinct_id: Optional[str] = None,
|
||||
insights_properties: Optional[Dict[str, Any]] = None,
|
||||
insights_privacy_mode: bool = False,
|
||||
insights_groups: Optional[Dict[str, Any]] = None,
|
||||
**kwargs,
|
||||
):
|
||||
"""
|
||||
@@ -135,24 +135,24 @@ class Models:
|
||||
location: GCP location for Vertex AI
|
||||
debug_config: Debug configuration for the client
|
||||
http_options: HTTP options for the client
|
||||
posthog_client: Insights client for tracking usage
|
||||
posthog_distinct_id: Default distinct ID for all calls
|
||||
posthog_properties: Default properties for all calls
|
||||
posthog_privacy_mode: Default privacy mode for all calls
|
||||
posthog_groups: Default groups for all calls
|
||||
insights_client: Insights client for tracking usage
|
||||
insights_distinct_id: Default distinct ID for all calls
|
||||
insights_properties: Default properties for all calls
|
||||
insights_privacy_mode: Default privacy mode for all calls
|
||||
insights_groups: Default groups for all calls
|
||||
**kwargs: Additional arguments (for future compatibility)
|
||||
"""
|
||||
|
||||
self._ph_client = posthog_client or setup()
|
||||
self._ph_client = insights_client or setup()
|
||||
|
||||
if self._ph_client is None:
|
||||
raise ValueError("posthog_client is required for Insights tracking")
|
||||
raise ValueError("insights_client is required for Insights tracking")
|
||||
|
||||
# Store default Insights settings
|
||||
self._default_distinct_id = posthog_distinct_id
|
||||
self._default_properties = posthog_properties or {}
|
||||
self._default_privacy_mode = posthog_privacy_mode
|
||||
self._default_groups = posthog_groups
|
||||
self._default_distinct_id = insights_distinct_id
|
||||
self._default_properties = insights_properties or {}
|
||||
self._default_privacy_mode = insights_privacy_mode
|
||||
self._default_groups = insights_groups
|
||||
|
||||
# Build genai.Client arguments
|
||||
client_args: Dict[str, Any] = {}
|
||||
@@ -196,7 +196,7 @@ class Models:
|
||||
self._client = genai.Client(**client_args)
|
||||
self._base_url = "https://generativelanguage.googleapis.com"
|
||||
|
||||
def _merge_posthog_params(
|
||||
def _merge_insights_params(
|
||||
self,
|
||||
call_distinct_id: Optional[str],
|
||||
call_trace_id: Optional[str],
|
||||
@@ -234,11 +234,11 @@ class Models:
|
||||
self,
|
||||
model: str,
|
||||
contents,
|
||||
posthog_distinct_id: Optional[str] = None,
|
||||
posthog_trace_id: Optional[str] = None,
|
||||
posthog_properties: Optional[Dict[str, Any]] = None,
|
||||
posthog_privacy_mode: Optional[bool] = None,
|
||||
posthog_groups: Optional[Dict[str, Any]] = None,
|
||||
insights_distinct_id: Optional[str] = None,
|
||||
insights_trace_id: Optional[str] = None,
|
||||
insights_properties: Optional[Dict[str, Any]] = None,
|
||||
insights_privacy_mode: Optional[bool] = None,
|
||||
insights_groups: Optional[Dict[str, Any]] = None,
|
||||
**kwargs: Any,
|
||||
):
|
||||
"""
|
||||
@@ -250,22 +250,22 @@ class Models:
|
||||
Args:
|
||||
model: The model to use (e.g., 'gemini-2.0-flash')
|
||||
contents: The input content for generation
|
||||
posthog_distinct_id: ID to associate with the usage event (overrides client default)
|
||||
posthog_trace_id: Trace UUID for linking events (auto-generated if not provided)
|
||||
posthog_properties: Extra properties to include in the event (merged with client defaults)
|
||||
posthog_privacy_mode: Whether to redact sensitive information (overrides client default)
|
||||
posthog_groups: Group analytics properties (overrides client default)
|
||||
insights_distinct_id: ID to associate with the usage event (overrides client default)
|
||||
insights_trace_id: Trace UUID for linking events (auto-generated if not provided)
|
||||
insights_properties: Extra properties to include in the event (merged with client defaults)
|
||||
insights_privacy_mode: Whether to redact sensitive information (overrides client default)
|
||||
insights_groups: Group analytics properties (overrides client default)
|
||||
**kwargs: Arguments passed to Gemini's generate_content
|
||||
"""
|
||||
|
||||
# Merge Insights parameters
|
||||
distinct_id, trace_id, properties, privacy_mode, groups = (
|
||||
self._merge_posthog_params(
|
||||
posthog_distinct_id,
|
||||
posthog_trace_id,
|
||||
posthog_properties,
|
||||
posthog_privacy_mode,
|
||||
posthog_groups,
|
||||
self._merge_insights_params(
|
||||
insights_distinct_id,
|
||||
insights_trace_id,
|
||||
insights_properties,
|
||||
insights_privacy_mode,
|
||||
insights_groups,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -390,21 +390,21 @@ class Models:
|
||||
self,
|
||||
model: str,
|
||||
contents,
|
||||
posthog_distinct_id: Optional[str] = None,
|
||||
posthog_trace_id: Optional[str] = None,
|
||||
posthog_properties: Optional[Dict[str, Any]] = None,
|
||||
posthog_privacy_mode: Optional[bool] = None,
|
||||
posthog_groups: Optional[Dict[str, Any]] = None,
|
||||
insights_distinct_id: Optional[str] = None,
|
||||
insights_trace_id: Optional[str] = None,
|
||||
insights_properties: Optional[Dict[str, Any]] = None,
|
||||
insights_privacy_mode: Optional[bool] = None,
|
||||
insights_groups: Optional[Dict[str, Any]] = None,
|
||||
**kwargs: Any,
|
||||
):
|
||||
# Merge Insights parameters
|
||||
distinct_id, trace_id, properties, privacy_mode, groups = (
|
||||
self._merge_posthog_params(
|
||||
posthog_distinct_id,
|
||||
posthog_trace_id,
|
||||
posthog_properties,
|
||||
posthog_privacy_mode,
|
||||
posthog_groups,
|
||||
self._merge_insights_params(
|
||||
insights_distinct_id,
|
||||
insights_trace_id,
|
||||
insights_properties,
|
||||
insights_privacy_mode,
|
||||
insights_groups,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -35,14 +35,14 @@ class AsyncClient:
|
||||
Usage:
|
||||
client = AsyncClient(
|
||||
api_key="your_api_key",
|
||||
posthog_client=posthog_client,
|
||||
posthog_distinct_id="default_user", # Optional defaults
|
||||
posthog_properties={"team": "ai"} # Optional defaults
|
||||
insights_client=insights_client,
|
||||
insights_distinct_id="default_user", # Optional defaults
|
||||
insights_properties={"team": "ai"} # Optional defaults
|
||||
)
|
||||
response = await client.models.generate_content(
|
||||
model="gemini-2.0-flash",
|
||||
contents=["Hello world"],
|
||||
posthog_distinct_id="specific_user" # Override default
|
||||
insights_distinct_id="specific_user" # Override default
|
||||
)
|
||||
"""
|
||||
|
||||
@@ -57,11 +57,11 @@ class AsyncClient:
|
||||
location: Optional[str] = None,
|
||||
debug_config: Optional[Any] = None,
|
||||
http_options: Optional[Any] = None,
|
||||
posthog_client: Optional[InsightsClient] = None,
|
||||
posthog_distinct_id: Optional[str] = None,
|
||||
posthog_properties: Optional[Dict[str, Any]] = None,
|
||||
posthog_privacy_mode: bool = False,
|
||||
posthog_groups: Optional[Dict[str, Any]] = None,
|
||||
insights_client: Optional[InsightsClient] = None,
|
||||
insights_distinct_id: Optional[str] = None,
|
||||
insights_properties: Optional[Dict[str, Any]] = None,
|
||||
insights_privacy_mode: bool = False,
|
||||
insights_groups: Optional[Dict[str, Any]] = None,
|
||||
**kwargs,
|
||||
):
|
||||
"""
|
||||
@@ -73,18 +73,18 @@ class AsyncClient:
|
||||
location: GCP location for Vertex AI
|
||||
debug_config: Debug configuration for the client
|
||||
http_options: HTTP options for the client
|
||||
posthog_client: Insights client for tracking usage
|
||||
posthog_distinct_id: Default distinct ID for all calls (can be overridden per call)
|
||||
posthog_properties: Default properties for all calls (can be overridden per call)
|
||||
posthog_privacy_mode: Default privacy mode for all calls (can be overridden per call)
|
||||
posthog_groups: Default groups for all calls (can be overridden per call)
|
||||
insights_client: Insights client for tracking usage
|
||||
insights_distinct_id: Default distinct ID for all calls (can be overridden per call)
|
||||
insights_properties: Default properties for all calls (can be overridden per call)
|
||||
insights_privacy_mode: Default privacy mode for all calls (can be overridden per call)
|
||||
insights_groups: Default groups for all calls (can be overridden per call)
|
||||
**kwargs: Additional arguments (for future compatibility)
|
||||
"""
|
||||
|
||||
self._ph_client = posthog_client or setup()
|
||||
self._ph_client = insights_client or setup()
|
||||
|
||||
if self._ph_client is None:
|
||||
raise ValueError("posthog_client is required for Insights tracking")
|
||||
raise ValueError("insights_client is required for Insights tracking")
|
||||
|
||||
self.models = AsyncModels(
|
||||
api_key=api_key,
|
||||
@@ -94,11 +94,11 @@ class AsyncClient:
|
||||
location=location,
|
||||
debug_config=debug_config,
|
||||
http_options=http_options,
|
||||
posthog_client=self._ph_client,
|
||||
posthog_distinct_id=posthog_distinct_id,
|
||||
posthog_properties=posthog_properties,
|
||||
posthog_privacy_mode=posthog_privacy_mode,
|
||||
posthog_groups=posthog_groups,
|
||||
insights_client=self._ph_client,
|
||||
insights_distinct_id=insights_distinct_id,
|
||||
insights_properties=insights_properties,
|
||||
insights_privacy_mode=insights_privacy_mode,
|
||||
insights_groups=insights_groups,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
@@ -119,11 +119,11 @@ class AsyncModels:
|
||||
location: Optional[str] = None,
|
||||
debug_config: Optional[Any] = None,
|
||||
http_options: Optional[Any] = None,
|
||||
posthog_client: Optional[InsightsClient] = None,
|
||||
posthog_distinct_id: Optional[str] = None,
|
||||
posthog_properties: Optional[Dict[str, Any]] = None,
|
||||
posthog_privacy_mode: bool = False,
|
||||
posthog_groups: Optional[Dict[str, Any]] = None,
|
||||
insights_client: Optional[InsightsClient] = None,
|
||||
insights_distinct_id: Optional[str] = None,
|
||||
insights_properties: Optional[Dict[str, Any]] = None,
|
||||
insights_privacy_mode: bool = False,
|
||||
insights_groups: Optional[Dict[str, Any]] = None,
|
||||
**kwargs,
|
||||
):
|
||||
"""
|
||||
@@ -135,24 +135,24 @@ class AsyncModels:
|
||||
location: GCP location for Vertex AI
|
||||
debug_config: Debug configuration for the client
|
||||
http_options: HTTP options for the client
|
||||
posthog_client: Insights client for tracking usage
|
||||
posthog_distinct_id: Default distinct ID for all calls
|
||||
posthog_properties: Default properties for all calls
|
||||
posthog_privacy_mode: Default privacy mode for all calls
|
||||
posthog_groups: Default groups for all calls
|
||||
insights_client: Insights client for tracking usage
|
||||
insights_distinct_id: Default distinct ID for all calls
|
||||
insights_properties: Default properties for all calls
|
||||
insights_privacy_mode: Default privacy mode for all calls
|
||||
insights_groups: Default groups for all calls
|
||||
**kwargs: Additional arguments (for future compatibility)
|
||||
"""
|
||||
|
||||
self._ph_client = posthog_client or setup()
|
||||
self._ph_client = insights_client or setup()
|
||||
|
||||
if self._ph_client is None:
|
||||
raise ValueError("posthog_client is required for Insights tracking")
|
||||
raise ValueError("insights_client is required for Insights tracking")
|
||||
|
||||
# Store default Insights settings
|
||||
self._default_distinct_id = posthog_distinct_id
|
||||
self._default_properties = posthog_properties or {}
|
||||
self._default_privacy_mode = posthog_privacy_mode
|
||||
self._default_groups = posthog_groups
|
||||
self._default_distinct_id = insights_distinct_id
|
||||
self._default_properties = insights_properties or {}
|
||||
self._default_privacy_mode = insights_privacy_mode
|
||||
self._default_groups = insights_groups
|
||||
|
||||
# Build genai.Client arguments
|
||||
client_args: Dict[str, Any] = {}
|
||||
@@ -196,7 +196,7 @@ class AsyncModels:
|
||||
self._client = genai.Client(**client_args)
|
||||
self._base_url = "https://generativelanguage.googleapis.com"
|
||||
|
||||
def _merge_posthog_params(
|
||||
def _merge_insights_params(
|
||||
self,
|
||||
call_distinct_id: Optional[str],
|
||||
call_trace_id: Optional[str],
|
||||
@@ -234,11 +234,11 @@ class AsyncModels:
|
||||
self,
|
||||
model: str,
|
||||
contents,
|
||||
posthog_distinct_id: Optional[str] = None,
|
||||
posthog_trace_id: Optional[str] = None,
|
||||
posthog_properties: Optional[Dict[str, Any]] = None,
|
||||
posthog_privacy_mode: Optional[bool] = None,
|
||||
posthog_groups: Optional[Dict[str, Any]] = None,
|
||||
insights_distinct_id: Optional[str] = None,
|
||||
insights_trace_id: Optional[str] = None,
|
||||
insights_properties: Optional[Dict[str, Any]] = None,
|
||||
insights_privacy_mode: Optional[bool] = None,
|
||||
insights_groups: Optional[Dict[str, Any]] = None,
|
||||
**kwargs: Any,
|
||||
):
|
||||
"""
|
||||
@@ -250,22 +250,22 @@ class AsyncModels:
|
||||
Args:
|
||||
model: The model to use (e.g., 'gemini-2.0-flash')
|
||||
contents: The input content for generation
|
||||
posthog_distinct_id: ID to associate with the usage event (overrides client default)
|
||||
posthog_trace_id: Trace UUID for linking events (auto-generated if not provided)
|
||||
posthog_properties: Extra properties to include in the event (merged with client defaults)
|
||||
posthog_privacy_mode: Whether to redact sensitive information (overrides client default)
|
||||
posthog_groups: Group analytics properties (overrides client default)
|
||||
insights_distinct_id: ID to associate with the usage event (overrides client default)
|
||||
insights_trace_id: Trace UUID for linking events (auto-generated if not provided)
|
||||
insights_properties: Extra properties to include in the event (merged with client defaults)
|
||||
insights_privacy_mode: Whether to redact sensitive information (overrides client default)
|
||||
insights_groups: Group analytics properties (overrides client default)
|
||||
**kwargs: Arguments passed to Gemini's generate_content
|
||||
"""
|
||||
|
||||
# Merge Insights parameters
|
||||
distinct_id, trace_id, properties, privacy_mode, groups = (
|
||||
self._merge_posthog_params(
|
||||
posthog_distinct_id,
|
||||
posthog_trace_id,
|
||||
posthog_properties,
|
||||
posthog_privacy_mode,
|
||||
posthog_groups,
|
||||
self._merge_insights_params(
|
||||
insights_distinct_id,
|
||||
insights_trace_id,
|
||||
insights_properties,
|
||||
insights_privacy_mode,
|
||||
insights_groups,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -393,21 +393,21 @@ class AsyncModels:
|
||||
self,
|
||||
model: str,
|
||||
contents,
|
||||
posthog_distinct_id: Optional[str] = None,
|
||||
posthog_trace_id: Optional[str] = None,
|
||||
posthog_properties: Optional[Dict[str, Any]] = None,
|
||||
posthog_privacy_mode: Optional[bool] = None,
|
||||
posthog_groups: Optional[Dict[str, Any]] = None,
|
||||
insights_distinct_id: Optional[str] = None,
|
||||
insights_trace_id: Optional[str] = None,
|
||||
insights_properties: Optional[Dict[str, Any]] = None,
|
||||
insights_privacy_mode: Optional[bool] = None,
|
||||
insights_groups: Optional[Dict[str, Any]] = None,
|
||||
**kwargs: Any,
|
||||
):
|
||||
# Merge Insights parameters
|
||||
distinct_id, trace_id, properties, privacy_mode, groups = (
|
||||
self._merge_posthog_params(
|
||||
posthog_distinct_id,
|
||||
posthog_trace_id,
|
||||
posthog_properties,
|
||||
posthog_privacy_mode,
|
||||
posthog_groups,
|
||||
self._merge_insights_params(
|
||||
insights_distinct_id,
|
||||
insights_trace_id,
|
||||
insights_properties,
|
||||
insights_privacy_mode,
|
||||
insights_groups,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ class GenerationMetadata(SpanMetadata):
|
||||
"""Base URL of the provider's API used in the run."""
|
||||
tools: Optional[List[Dict[str, Any]]] = None
|
||||
"""Tools provided to the model."""
|
||||
posthog_properties: Optional[Dict[str, Any]] = None
|
||||
insights_properties: Optional[Dict[str, Any]] = None
|
||||
"""Insights properties of the run."""
|
||||
|
||||
|
||||
@@ -423,7 +423,7 @@ class CallbackHandler(BaseCallbackHandler):
|
||||
if provider := metadata.get("ls_provider"):
|
||||
generation.provider = provider
|
||||
|
||||
generation.posthog_properties = metadata.get("posthog_properties")
|
||||
generation.insights_properties = metadata.get("insights_properties")
|
||||
try:
|
||||
base_url = serialized["kwargs"]["openai_api_base"]
|
||||
if base_url is not None:
|
||||
@@ -578,8 +578,8 @@ class CallbackHandler(BaseCallbackHandler):
|
||||
"$ai_framework": "langchain",
|
||||
}
|
||||
|
||||
if isinstance(run.posthog_properties, dict):
|
||||
event_properties.update(run.posthog_properties)
|
||||
if isinstance(run.insights_properties, dict):
|
||||
event_properties.update(run.insights_properties)
|
||||
|
||||
if run.tools:
|
||||
event_properties["$ai_tools"] = run.tools
|
||||
|
||||
+120
-120
@@ -35,16 +35,16 @@ class OpenAI(openai.OpenAI):
|
||||
|
||||
_ph_client: InsightsClient
|
||||
|
||||
def __init__(self, posthog_client: Optional[InsightsClient] = None, **kwargs):
|
||||
def __init__(self, insights_client: Optional[InsightsClient] = None, **kwargs):
|
||||
"""
|
||||
Args:
|
||||
api_key: OpenAI API key.
|
||||
posthog_client: If provided, events will be captured via this client instead of the global `posthog`.
|
||||
insights_client: If provided, events will be captured via this client instead of the global client.
|
||||
**openai_config: Any additional keyword args to set on openai (e.g. organization="xxx").
|
||||
"""
|
||||
|
||||
super().__init__(**kwargs)
|
||||
self._ph_client = posthog_client or setup()
|
||||
self._ph_client = insights_client or setup()
|
||||
|
||||
# Store original objects after parent initialization (only if they exist)
|
||||
self._original_chat = getattr(self, "chat", None)
|
||||
@@ -79,34 +79,34 @@ class WrappedResponses:
|
||||
|
||||
def create(
|
||||
self,
|
||||
posthog_distinct_id: Optional[str] = None,
|
||||
posthog_trace_id: Optional[str] = None,
|
||||
posthog_properties: Optional[Dict[str, Any]] = None,
|
||||
posthog_privacy_mode: bool = False,
|
||||
posthog_groups: Optional[Dict[str, Any]] = None,
|
||||
insights_distinct_id: Optional[str] = None,
|
||||
insights_trace_id: Optional[str] = None,
|
||||
insights_properties: Optional[Dict[str, Any]] = None,
|
||||
insights_privacy_mode: bool = False,
|
||||
insights_groups: Optional[Dict[str, Any]] = None,
|
||||
**kwargs: Any,
|
||||
):
|
||||
if posthog_trace_id is None:
|
||||
posthog_trace_id = str(uuid.uuid4())
|
||||
if insights_trace_id is None:
|
||||
insights_trace_id = str(uuid.uuid4())
|
||||
|
||||
if kwargs.get("stream", False):
|
||||
return self._create_streaming(
|
||||
posthog_distinct_id,
|
||||
posthog_trace_id,
|
||||
posthog_properties,
|
||||
posthog_privacy_mode,
|
||||
posthog_groups,
|
||||
insights_distinct_id,
|
||||
insights_trace_id,
|
||||
insights_properties,
|
||||
insights_privacy_mode,
|
||||
insights_groups,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
return call_llm_and_track_usage(
|
||||
posthog_distinct_id,
|
||||
insights_distinct_id,
|
||||
self._client._ph_client,
|
||||
"openai",
|
||||
posthog_trace_id,
|
||||
posthog_properties,
|
||||
posthog_privacy_mode,
|
||||
posthog_groups,
|
||||
insights_trace_id,
|
||||
insights_properties,
|
||||
insights_privacy_mode,
|
||||
insights_groups,
|
||||
self._client.base_url,
|
||||
self._original.create,
|
||||
**kwargs,
|
||||
@@ -114,11 +114,11 @@ class WrappedResponses:
|
||||
|
||||
def _create_streaming(
|
||||
self,
|
||||
posthog_distinct_id: Optional[str],
|
||||
posthog_trace_id: Optional[str],
|
||||
posthog_properties: Optional[Dict[str, Any]],
|
||||
posthog_privacy_mode: bool,
|
||||
posthog_groups: Optional[Dict[str, Any]],
|
||||
insights_distinct_id: Optional[str],
|
||||
insights_trace_id: Optional[str],
|
||||
insights_properties: Optional[Dict[str, Any]],
|
||||
insights_privacy_mode: bool,
|
||||
insights_groups: Optional[Dict[str, Any]],
|
||||
**kwargs: Any,
|
||||
):
|
||||
start_time = time.time()
|
||||
@@ -160,11 +160,11 @@ class WrappedResponses:
|
||||
latency = end_time - start_time
|
||||
output = final_content
|
||||
self._capture_streaming_event(
|
||||
posthog_distinct_id,
|
||||
posthog_trace_id,
|
||||
posthog_properties,
|
||||
posthog_privacy_mode,
|
||||
posthog_groups,
|
||||
insights_distinct_id,
|
||||
insights_trace_id,
|
||||
insights_properties,
|
||||
insights_privacy_mode,
|
||||
insights_groups,
|
||||
kwargs,
|
||||
usage_stats,
|
||||
latency,
|
||||
@@ -177,11 +177,11 @@ class WrappedResponses:
|
||||
|
||||
def _capture_streaming_event(
|
||||
self,
|
||||
posthog_distinct_id: Optional[str],
|
||||
posthog_trace_id: Optional[str],
|
||||
posthog_properties: Optional[Dict[str, Any]],
|
||||
posthog_privacy_mode: bool,
|
||||
posthog_groups: Optional[Dict[str, Any]],
|
||||
insights_distinct_id: Optional[str],
|
||||
insights_trace_id: Optional[str],
|
||||
insights_properties: Optional[Dict[str, Any]],
|
||||
insights_privacy_mode: bool,
|
||||
insights_groups: Optional[Dict[str, Any]],
|
||||
kwargs: Dict[str, Any],
|
||||
usage_stats: TokenUsage,
|
||||
latency: float,
|
||||
@@ -212,11 +212,11 @@ class WrappedResponses:
|
||||
formatted_output=format_openai_streaming_output(output, "responses"),
|
||||
usage_stats=usage_stats,
|
||||
latency=latency,
|
||||
distinct_id=posthog_distinct_id,
|
||||
trace_id=posthog_trace_id,
|
||||
properties=posthog_properties,
|
||||
privacy_mode=posthog_privacy_mode,
|
||||
groups=posthog_groups,
|
||||
distinct_id=insights_distinct_id,
|
||||
trace_id=insights_trace_id,
|
||||
properties=insights_properties,
|
||||
privacy_mode=insights_privacy_mode,
|
||||
groups=insights_groups,
|
||||
)
|
||||
|
||||
# Use the common capture function
|
||||
@@ -224,35 +224,35 @@ class WrappedResponses:
|
||||
|
||||
def parse(
|
||||
self,
|
||||
posthog_distinct_id: Optional[str] = None,
|
||||
posthog_trace_id: Optional[str] = None,
|
||||
posthog_properties: Optional[Dict[str, Any]] = None,
|
||||
posthog_privacy_mode: bool = False,
|
||||
posthog_groups: Optional[Dict[str, Any]] = None,
|
||||
insights_distinct_id: Optional[str] = None,
|
||||
insights_trace_id: Optional[str] = None,
|
||||
insights_properties: Optional[Dict[str, Any]] = None,
|
||||
insights_privacy_mode: bool = False,
|
||||
insights_groups: Optional[Dict[str, Any]] = None,
|
||||
**kwargs: Any,
|
||||
):
|
||||
"""
|
||||
Parse structured output using OpenAI's 'responses.parse' method, but also track usage in Insights.
|
||||
|
||||
Args:
|
||||
posthog_distinct_id: Optional ID to associate with the usage event.
|
||||
posthog_trace_id: Optional trace UUID for linking events.
|
||||
posthog_properties: Optional dictionary of extra properties to include in the event.
|
||||
posthog_privacy_mode: Whether to anonymize the input and output.
|
||||
posthog_groups: Optional dictionary of groups to associate with the event.
|
||||
insights_distinct_id: Optional ID to associate with the usage event.
|
||||
insights_trace_id: Optional trace UUID for linking events.
|
||||
insights_properties: Optional dictionary of extra properties to include in the event.
|
||||
insights_privacy_mode: Whether to anonymize the input and output.
|
||||
insights_groups: Optional dictionary of groups to associate with the event.
|
||||
**kwargs: Any additional parameters for the OpenAI Responses Parse API.
|
||||
|
||||
Returns:
|
||||
The response from OpenAI's responses.parse call.
|
||||
"""
|
||||
return call_llm_and_track_usage(
|
||||
posthog_distinct_id,
|
||||
insights_distinct_id,
|
||||
self._client._ph_client,
|
||||
"openai",
|
||||
posthog_trace_id,
|
||||
posthog_properties,
|
||||
posthog_privacy_mode,
|
||||
posthog_groups,
|
||||
insights_trace_id,
|
||||
insights_properties,
|
||||
insights_privacy_mode,
|
||||
insights_groups,
|
||||
self._client.base_url,
|
||||
self._original.parse,
|
||||
**kwargs,
|
||||
@@ -288,34 +288,34 @@ class WrappedCompletions:
|
||||
|
||||
def create(
|
||||
self,
|
||||
posthog_distinct_id: Optional[str] = None,
|
||||
posthog_trace_id: Optional[str] = None,
|
||||
posthog_properties: Optional[Dict[str, Any]] = None,
|
||||
posthog_privacy_mode: bool = False,
|
||||
posthog_groups: Optional[Dict[str, Any]] = None,
|
||||
insights_distinct_id: Optional[str] = None,
|
||||
insights_trace_id: Optional[str] = None,
|
||||
insights_properties: Optional[Dict[str, Any]] = None,
|
||||
insights_privacy_mode: bool = False,
|
||||
insights_groups: Optional[Dict[str, Any]] = None,
|
||||
**kwargs: Any,
|
||||
):
|
||||
if posthog_trace_id is None:
|
||||
posthog_trace_id = str(uuid.uuid4())
|
||||
if insights_trace_id is None:
|
||||
insights_trace_id = str(uuid.uuid4())
|
||||
|
||||
if kwargs.get("stream", False):
|
||||
return self._create_streaming(
|
||||
posthog_distinct_id,
|
||||
posthog_trace_id,
|
||||
posthog_properties,
|
||||
posthog_privacy_mode,
|
||||
posthog_groups,
|
||||
insights_distinct_id,
|
||||
insights_trace_id,
|
||||
insights_properties,
|
||||
insights_privacy_mode,
|
||||
insights_groups,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
return call_llm_and_track_usage(
|
||||
posthog_distinct_id,
|
||||
insights_distinct_id,
|
||||
self._client._ph_client,
|
||||
"openai",
|
||||
posthog_trace_id,
|
||||
posthog_properties,
|
||||
posthog_privacy_mode,
|
||||
posthog_groups,
|
||||
insights_trace_id,
|
||||
insights_properties,
|
||||
insights_privacy_mode,
|
||||
insights_groups,
|
||||
self._client.base_url,
|
||||
self._original.create,
|
||||
**kwargs,
|
||||
@@ -323,11 +323,11 @@ class WrappedCompletions:
|
||||
|
||||
def _create_streaming(
|
||||
self,
|
||||
posthog_distinct_id: Optional[str],
|
||||
posthog_trace_id: Optional[str],
|
||||
posthog_properties: Optional[Dict[str, Any]],
|
||||
posthog_privacy_mode: bool,
|
||||
posthog_groups: Optional[Dict[str, Any]],
|
||||
insights_distinct_id: Optional[str],
|
||||
insights_trace_id: Optional[str],
|
||||
insights_properties: Optional[Dict[str, Any]],
|
||||
insights_privacy_mode: bool,
|
||||
insights_groups: Optional[Dict[str, Any]],
|
||||
**kwargs: Any,
|
||||
):
|
||||
start_time = time.time()
|
||||
@@ -385,11 +385,11 @@ class WrappedCompletions:
|
||||
)
|
||||
|
||||
self._capture_streaming_event(
|
||||
posthog_distinct_id,
|
||||
posthog_trace_id,
|
||||
posthog_properties,
|
||||
posthog_privacy_mode,
|
||||
posthog_groups,
|
||||
insights_distinct_id,
|
||||
insights_trace_id,
|
||||
insights_properties,
|
||||
insights_privacy_mode,
|
||||
insights_groups,
|
||||
kwargs,
|
||||
usage_stats,
|
||||
latency,
|
||||
@@ -403,11 +403,11 @@ class WrappedCompletions:
|
||||
|
||||
def _capture_streaming_event(
|
||||
self,
|
||||
posthog_distinct_id: Optional[str],
|
||||
posthog_trace_id: Optional[str],
|
||||
posthog_properties: Optional[Dict[str, Any]],
|
||||
posthog_privacy_mode: bool,
|
||||
posthog_groups: Optional[Dict[str, Any]],
|
||||
insights_distinct_id: Optional[str],
|
||||
insights_trace_id: Optional[str],
|
||||
insights_properties: Optional[Dict[str, Any]],
|
||||
insights_privacy_mode: bool,
|
||||
insights_groups: Optional[Dict[str, Any]],
|
||||
kwargs: Dict[str, Any],
|
||||
usage_stats: TokenUsage,
|
||||
latency: float,
|
||||
@@ -439,11 +439,11 @@ class WrappedCompletions:
|
||||
formatted_output=format_openai_streaming_output(output, "chat", tool_calls),
|
||||
usage_stats=usage_stats,
|
||||
latency=latency,
|
||||
distinct_id=posthog_distinct_id,
|
||||
trace_id=posthog_trace_id,
|
||||
properties=posthog_properties,
|
||||
privacy_mode=posthog_privacy_mode,
|
||||
groups=posthog_groups,
|
||||
distinct_id=insights_distinct_id,
|
||||
trace_id=insights_trace_id,
|
||||
properties=insights_properties,
|
||||
privacy_mode=insights_privacy_mode,
|
||||
groups=insights_groups,
|
||||
)
|
||||
|
||||
# Use the common capture function
|
||||
@@ -463,30 +463,30 @@ class WrappedEmbeddings:
|
||||
|
||||
def create(
|
||||
self,
|
||||
posthog_distinct_id: Optional[str] = None,
|
||||
posthog_trace_id: Optional[str] = None,
|
||||
posthog_properties: Optional[Dict[str, Any]] = None,
|
||||
posthog_privacy_mode: bool = False,
|
||||
posthog_groups: Optional[Dict[str, Any]] = None,
|
||||
insights_distinct_id: Optional[str] = None,
|
||||
insights_trace_id: Optional[str] = None,
|
||||
insights_properties: Optional[Dict[str, Any]] = None,
|
||||
insights_privacy_mode: bool = False,
|
||||
insights_groups: Optional[Dict[str, Any]] = None,
|
||||
**kwargs: Any,
|
||||
):
|
||||
"""
|
||||
Create an embedding using OpenAI's 'embeddings.create' method, but also track usage in Insights.
|
||||
|
||||
Args:
|
||||
posthog_distinct_id: Optional ID to associate with the usage event.
|
||||
posthog_trace_id: Optional trace UUID for linking events.
|
||||
posthog_properties: Optional dictionary of extra properties to include in the event.
|
||||
posthog_privacy_mode: Whether to anonymize the input and output.
|
||||
posthog_groups: Optional dictionary of groups to associate with the event.
|
||||
insights_distinct_id: Optional ID to associate with the usage event.
|
||||
insights_trace_id: Optional trace UUID for linking events.
|
||||
insights_properties: Optional dictionary of extra properties to include in the event.
|
||||
insights_privacy_mode: Whether to anonymize the input and output.
|
||||
insights_groups: Optional dictionary of groups to associate with the event.
|
||||
**kwargs: Any additional parameters for the OpenAI Embeddings API.
|
||||
|
||||
Returns:
|
||||
The response from OpenAI's embeddings.create call.
|
||||
"""
|
||||
|
||||
if posthog_trace_id is None:
|
||||
posthog_trace_id = str(uuid.uuid4())
|
||||
if insights_trace_id is None:
|
||||
insights_trace_id = str(uuid.uuid4())
|
||||
|
||||
start_time = time.time()
|
||||
response = self._original.create(**kwargs)
|
||||
@@ -508,27 +508,27 @@ class WrappedEmbeddings:
|
||||
"$ai_model": kwargs.get("model"),
|
||||
"$ai_input": with_privacy_mode(
|
||||
self._client._ph_client,
|
||||
posthog_privacy_mode,
|
||||
insights_privacy_mode,
|
||||
sanitize_openai_response(kwargs.get("input")),
|
||||
),
|
||||
"$ai_http_status": 200,
|
||||
"$ai_input_tokens": usage_stats.get("prompt_tokens", 0),
|
||||
"$ai_latency": latency,
|
||||
"$ai_trace_id": posthog_trace_id,
|
||||
"$ai_trace_id": insights_trace_id,
|
||||
"$ai_base_url": str(self._client.base_url),
|
||||
**(posthog_properties or {}),
|
||||
**(insights_properties or {}),
|
||||
}
|
||||
|
||||
if posthog_distinct_id is None:
|
||||
if insights_distinct_id is None:
|
||||
event_properties["$process_person_profile"] = False
|
||||
|
||||
# Send capture event for embeddings
|
||||
if hasattr(self._client._ph_client, "capture"):
|
||||
self._client._ph_client.capture(
|
||||
distinct_id=posthog_distinct_id or posthog_trace_id,
|
||||
distinct_id=insights_distinct_id or insights_trace_id,
|
||||
event="$ai_embedding",
|
||||
properties=event_properties,
|
||||
groups=posthog_groups,
|
||||
groups=insights_groups,
|
||||
)
|
||||
|
||||
return response
|
||||
@@ -579,21 +579,21 @@ class WrappedBetaCompletions:
|
||||
|
||||
def parse(
|
||||
self,
|
||||
posthog_distinct_id: Optional[str] = None,
|
||||
posthog_trace_id: Optional[str] = None,
|
||||
posthog_properties: Optional[Dict[str, Any]] = None,
|
||||
posthog_privacy_mode: bool = False,
|
||||
posthog_groups: Optional[Dict[str, Any]] = None,
|
||||
insights_distinct_id: Optional[str] = None,
|
||||
insights_trace_id: Optional[str] = None,
|
||||
insights_properties: Optional[Dict[str, Any]] = None,
|
||||
insights_privacy_mode: bool = False,
|
||||
insights_groups: Optional[Dict[str, Any]] = None,
|
||||
**kwargs: Any,
|
||||
):
|
||||
return call_llm_and_track_usage(
|
||||
posthog_distinct_id,
|
||||
insights_distinct_id,
|
||||
self._client._ph_client,
|
||||
"openai",
|
||||
posthog_trace_id,
|
||||
posthog_properties,
|
||||
posthog_privacy_mode,
|
||||
posthog_groups,
|
||||
insights_trace_id,
|
||||
insights_properties,
|
||||
insights_privacy_mode,
|
||||
insights_groups,
|
||||
self._client.base_url,
|
||||
self._original.parse,
|
||||
**kwargs,
|
||||
|
||||
@@ -37,17 +37,17 @@ class AsyncOpenAI(openai.AsyncOpenAI):
|
||||
|
||||
_ph_client: InsightsClient
|
||||
|
||||
def __init__(self, posthog_client: Optional[InsightsClient] = None, **kwargs):
|
||||
def __init__(self, insights_client: Optional[InsightsClient] = None, **kwargs):
|
||||
"""
|
||||
Args:
|
||||
api_key: OpenAI API key.
|
||||
posthog_client: If provided, events will be captured via this client instead
|
||||
insights_client: If provided, events will be captured via this client instead
|
||||
of the global hanzo_insights.
|
||||
**openai_config: Any additional keyword args to set on openai (e.g. organization="xxx").
|
||||
"""
|
||||
|
||||
super().__init__(**kwargs)
|
||||
self._ph_client = posthog_client or setup()
|
||||
self._ph_client = insights_client or setup()
|
||||
|
||||
# Store original objects after parent initialization (only if they exist)
|
||||
self._original_chat = getattr(self, "chat", None)
|
||||
@@ -83,34 +83,34 @@ class WrappedResponses:
|
||||
|
||||
async def create(
|
||||
self,
|
||||
posthog_distinct_id: Optional[str] = None,
|
||||
posthog_trace_id: Optional[str] = None,
|
||||
posthog_properties: Optional[Dict[str, Any]] = None,
|
||||
posthog_privacy_mode: bool = False,
|
||||
posthog_groups: Optional[Dict[str, Any]] = None,
|
||||
insights_distinct_id: Optional[str] = None,
|
||||
insights_trace_id: Optional[str] = None,
|
||||
insights_properties: Optional[Dict[str, Any]] = None,
|
||||
insights_privacy_mode: bool = False,
|
||||
insights_groups: Optional[Dict[str, Any]] = None,
|
||||
**kwargs: Any,
|
||||
):
|
||||
if posthog_trace_id is None:
|
||||
posthog_trace_id = str(uuid.uuid4())
|
||||
if insights_trace_id is None:
|
||||
insights_trace_id = str(uuid.uuid4())
|
||||
|
||||
if kwargs.get("stream", False):
|
||||
return await self._create_streaming(
|
||||
posthog_distinct_id,
|
||||
posthog_trace_id,
|
||||
posthog_properties,
|
||||
posthog_privacy_mode,
|
||||
posthog_groups,
|
||||
insights_distinct_id,
|
||||
insights_trace_id,
|
||||
insights_properties,
|
||||
insights_privacy_mode,
|
||||
insights_groups,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
return await call_llm_and_track_usage_async(
|
||||
posthog_distinct_id,
|
||||
insights_distinct_id,
|
||||
self._client._ph_client,
|
||||
"openai",
|
||||
posthog_trace_id,
|
||||
posthog_properties,
|
||||
posthog_privacy_mode,
|
||||
posthog_groups,
|
||||
insights_trace_id,
|
||||
insights_properties,
|
||||
insights_privacy_mode,
|
||||
insights_groups,
|
||||
self._client.base_url,
|
||||
self._original.create,
|
||||
**kwargs,
|
||||
@@ -118,11 +118,11 @@ class WrappedResponses:
|
||||
|
||||
async def _create_streaming(
|
||||
self,
|
||||
posthog_distinct_id: Optional[str],
|
||||
posthog_trace_id: Optional[str],
|
||||
posthog_properties: Optional[Dict[str, Any]],
|
||||
posthog_privacy_mode: bool,
|
||||
posthog_groups: Optional[Dict[str, Any]],
|
||||
insights_distinct_id: Optional[str],
|
||||
insights_trace_id: Optional[str],
|
||||
insights_properties: Optional[Dict[str, Any]],
|
||||
insights_privacy_mode: bool,
|
||||
insights_groups: Optional[Dict[str, Any]],
|
||||
**kwargs: Any,
|
||||
):
|
||||
start_time = time.time()
|
||||
@@ -165,11 +165,11 @@ class WrappedResponses:
|
||||
output = final_content
|
||||
|
||||
await self._capture_streaming_event(
|
||||
posthog_distinct_id,
|
||||
posthog_trace_id,
|
||||
posthog_properties,
|
||||
posthog_privacy_mode,
|
||||
posthog_groups,
|
||||
insights_distinct_id,
|
||||
insights_trace_id,
|
||||
insights_properties,
|
||||
insights_privacy_mode,
|
||||
insights_groups,
|
||||
kwargs,
|
||||
usage_stats,
|
||||
latency,
|
||||
@@ -182,11 +182,11 @@ class WrappedResponses:
|
||||
|
||||
async def _capture_streaming_event(
|
||||
self,
|
||||
posthog_distinct_id: Optional[str],
|
||||
posthog_trace_id: Optional[str],
|
||||
posthog_properties: Optional[Dict[str, Any]],
|
||||
posthog_privacy_mode: bool,
|
||||
posthog_groups: Optional[Dict[str, Any]],
|
||||
insights_distinct_id: Optional[str],
|
||||
insights_trace_id: Optional[str],
|
||||
insights_properties: Optional[Dict[str, Any]],
|
||||
insights_privacy_mode: bool,
|
||||
insights_groups: Optional[Dict[str, Any]],
|
||||
kwargs: Dict[str, Any],
|
||||
usage_stats: TokenUsage,
|
||||
latency: float,
|
||||
@@ -194,8 +194,8 @@ class WrappedResponses:
|
||||
available_tool_calls: Optional[List[Dict[str, Any]]] = None,
|
||||
model_from_response: Optional[str] = None,
|
||||
):
|
||||
if posthog_trace_id is None:
|
||||
posthog_trace_id = str(uuid.uuid4())
|
||||
if insights_trace_id is None:
|
||||
insights_trace_id = str(uuid.uuid4())
|
||||
|
||||
# Use model from kwargs, fallback to model from response
|
||||
model = kwargs.get("model") or model_from_response or "unknown"
|
||||
@@ -206,12 +206,12 @@ class WrappedResponses:
|
||||
"$ai_model_parameters": get_model_params(kwargs),
|
||||
"$ai_input": with_privacy_mode(
|
||||
self._client._ph_client,
|
||||
posthog_privacy_mode,
|
||||
insights_privacy_mode,
|
||||
sanitize_openai_response(kwargs.get("input")),
|
||||
),
|
||||
"$ai_output_choices": with_privacy_mode(
|
||||
self._client._ph_client,
|
||||
posthog_privacy_mode,
|
||||
insights_privacy_mode,
|
||||
format_openai_streaming_output(output, "responses"),
|
||||
),
|
||||
"$ai_http_status": 200,
|
||||
@@ -222,9 +222,9 @@ class WrappedResponses:
|
||||
),
|
||||
"$ai_reasoning_tokens": usage_stats.get("reasoning_tokens", 0),
|
||||
"$ai_latency": latency,
|
||||
"$ai_trace_id": posthog_trace_id,
|
||||
"$ai_trace_id": insights_trace_id,
|
||||
"$ai_base_url": str(self._client.base_url),
|
||||
**(posthog_properties or {}),
|
||||
**(insights_properties or {}),
|
||||
}
|
||||
|
||||
# Add web search count if present
|
||||
@@ -239,48 +239,48 @@ class WrappedResponses:
|
||||
if available_tool_calls:
|
||||
event_properties["$ai_tools"] = available_tool_calls
|
||||
|
||||
if posthog_distinct_id is None:
|
||||
if insights_distinct_id is None:
|
||||
event_properties["$process_person_profile"] = False
|
||||
|
||||
if hasattr(self._client._ph_client, "capture"):
|
||||
self._client._ph_client.capture(
|
||||
distinct_id=posthog_distinct_id or posthog_trace_id,
|
||||
distinct_id=insights_distinct_id or insights_trace_id,
|
||||
event="$ai_generation",
|
||||
properties=event_properties,
|
||||
groups=posthog_groups,
|
||||
groups=insights_groups,
|
||||
)
|
||||
|
||||
async def parse(
|
||||
self,
|
||||
posthog_distinct_id: Optional[str] = None,
|
||||
posthog_trace_id: Optional[str] = None,
|
||||
posthog_properties: Optional[Dict[str, Any]] = None,
|
||||
posthog_privacy_mode: bool = False,
|
||||
posthog_groups: Optional[Dict[str, Any]] = None,
|
||||
insights_distinct_id: Optional[str] = None,
|
||||
insights_trace_id: Optional[str] = None,
|
||||
insights_properties: Optional[Dict[str, Any]] = None,
|
||||
insights_privacy_mode: bool = False,
|
||||
insights_groups: Optional[Dict[str, Any]] = None,
|
||||
**kwargs: Any,
|
||||
):
|
||||
"""
|
||||
Parse structured output using OpenAI's 'responses.parse' method, but also track usage in Insights.
|
||||
|
||||
Args:
|
||||
posthog_distinct_id: Optional ID to associate with the usage event.
|
||||
posthog_trace_id: Optional trace UUID for linking events.
|
||||
posthog_properties: Optional dictionary of extra properties to include in the event.
|
||||
posthog_privacy_mode: Whether to anonymize the input and output.
|
||||
posthog_groups: Optional dictionary of groups to associate with the event.
|
||||
insights_distinct_id: Optional ID to associate with the usage event.
|
||||
insights_trace_id: Optional trace UUID for linking events.
|
||||
insights_properties: Optional dictionary of extra properties to include in the event.
|
||||
insights_privacy_mode: Whether to anonymize the input and output.
|
||||
insights_groups: Optional dictionary of groups to associate with the event.
|
||||
**kwargs: Any additional parameters for the OpenAI Responses Parse API.
|
||||
|
||||
Returns:
|
||||
The response from OpenAI's responses.parse call.
|
||||
"""
|
||||
return await call_llm_and_track_usage_async(
|
||||
posthog_distinct_id,
|
||||
insights_distinct_id,
|
||||
self._client._ph_client,
|
||||
"openai",
|
||||
posthog_trace_id,
|
||||
posthog_properties,
|
||||
posthog_privacy_mode,
|
||||
posthog_groups,
|
||||
insights_trace_id,
|
||||
insights_properties,
|
||||
insights_privacy_mode,
|
||||
insights_groups,
|
||||
self._client.base_url,
|
||||
self._original.parse,
|
||||
**kwargs,
|
||||
@@ -316,35 +316,35 @@ class WrappedCompletions:
|
||||
|
||||
async def create(
|
||||
self,
|
||||
posthog_distinct_id: Optional[str] = None,
|
||||
posthog_trace_id: Optional[str] = None,
|
||||
posthog_properties: Optional[Dict[str, Any]] = None,
|
||||
posthog_privacy_mode: bool = False,
|
||||
posthog_groups: Optional[Dict[str, Any]] = None,
|
||||
insights_distinct_id: Optional[str] = None,
|
||||
insights_trace_id: Optional[str] = None,
|
||||
insights_properties: Optional[Dict[str, Any]] = None,
|
||||
insights_privacy_mode: bool = False,
|
||||
insights_groups: Optional[Dict[str, Any]] = None,
|
||||
**kwargs: Any,
|
||||
):
|
||||
if posthog_trace_id is None:
|
||||
posthog_trace_id = str(uuid.uuid4())
|
||||
if insights_trace_id is None:
|
||||
insights_trace_id = str(uuid.uuid4())
|
||||
|
||||
# If streaming, handle streaming specifically
|
||||
if kwargs.get("stream", False):
|
||||
return await self._create_streaming(
|
||||
posthog_distinct_id,
|
||||
posthog_trace_id,
|
||||
posthog_properties,
|
||||
posthog_privacy_mode,
|
||||
posthog_groups,
|
||||
insights_distinct_id,
|
||||
insights_trace_id,
|
||||
insights_properties,
|
||||
insights_privacy_mode,
|
||||
insights_groups,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
response = await call_llm_and_track_usage_async(
|
||||
posthog_distinct_id,
|
||||
insights_distinct_id,
|
||||
self._client._ph_client,
|
||||
"openai",
|
||||
posthog_trace_id,
|
||||
posthog_properties,
|
||||
posthog_privacy_mode,
|
||||
posthog_groups,
|
||||
insights_trace_id,
|
||||
insights_properties,
|
||||
insights_privacy_mode,
|
||||
insights_groups,
|
||||
self._client.base_url,
|
||||
self._original.create,
|
||||
**kwargs,
|
||||
@@ -353,11 +353,11 @@ class WrappedCompletions:
|
||||
|
||||
async def _create_streaming(
|
||||
self,
|
||||
posthog_distinct_id: Optional[str],
|
||||
posthog_trace_id: Optional[str],
|
||||
posthog_properties: Optional[Dict[str, Any]],
|
||||
posthog_privacy_mode: bool,
|
||||
posthog_groups: Optional[Dict[str, Any]],
|
||||
insights_distinct_id: Optional[str],
|
||||
insights_trace_id: Optional[str],
|
||||
insights_properties: Optional[Dict[str, Any]],
|
||||
insights_privacy_mode: bool,
|
||||
insights_groups: Optional[Dict[str, Any]],
|
||||
**kwargs: Any,
|
||||
):
|
||||
start_time = time.time()
|
||||
@@ -414,11 +414,11 @@ class WrappedCompletions:
|
||||
)
|
||||
|
||||
await self._capture_streaming_event(
|
||||
posthog_distinct_id,
|
||||
posthog_trace_id,
|
||||
posthog_properties,
|
||||
posthog_privacy_mode,
|
||||
posthog_groups,
|
||||
insights_distinct_id,
|
||||
insights_trace_id,
|
||||
insights_properties,
|
||||
insights_privacy_mode,
|
||||
insights_groups,
|
||||
kwargs,
|
||||
usage_stats,
|
||||
latency,
|
||||
@@ -432,11 +432,11 @@ class WrappedCompletions:
|
||||
|
||||
async def _capture_streaming_event(
|
||||
self,
|
||||
posthog_distinct_id: Optional[str],
|
||||
posthog_trace_id: Optional[str],
|
||||
posthog_properties: Optional[Dict[str, Any]],
|
||||
posthog_privacy_mode: bool,
|
||||
posthog_groups: Optional[Dict[str, Any]],
|
||||
insights_distinct_id: Optional[str],
|
||||
insights_trace_id: Optional[str],
|
||||
insights_properties: Optional[Dict[str, Any]],
|
||||
insights_privacy_mode: bool,
|
||||
insights_groups: Optional[Dict[str, Any]],
|
||||
kwargs: Dict[str, Any],
|
||||
usage_stats: TokenUsage,
|
||||
latency: float,
|
||||
@@ -445,8 +445,8 @@ class WrappedCompletions:
|
||||
available_tool_calls: Optional[List[Dict[str, Any]]] = None,
|
||||
model_from_response: Optional[str] = None,
|
||||
):
|
||||
if posthog_trace_id is None:
|
||||
posthog_trace_id = str(uuid.uuid4())
|
||||
if insights_trace_id is None:
|
||||
insights_trace_id = str(uuid.uuid4())
|
||||
|
||||
# Use model from kwargs, fallback to model from response
|
||||
model = kwargs.get("model") or model_from_response or "unknown"
|
||||
@@ -457,12 +457,12 @@ class WrappedCompletions:
|
||||
"$ai_model_parameters": get_model_params(kwargs),
|
||||
"$ai_input": with_privacy_mode(
|
||||
self._client._ph_client,
|
||||
posthog_privacy_mode,
|
||||
insights_privacy_mode,
|
||||
sanitize_openai(kwargs.get("messages")),
|
||||
),
|
||||
"$ai_output_choices": with_privacy_mode(
|
||||
self._client._ph_client,
|
||||
posthog_privacy_mode,
|
||||
insights_privacy_mode,
|
||||
format_openai_streaming_output(output, "chat", tool_calls),
|
||||
),
|
||||
"$ai_http_status": 200,
|
||||
@@ -473,9 +473,9 @@ class WrappedCompletions:
|
||||
),
|
||||
"$ai_reasoning_tokens": usage_stats.get("reasoning_tokens", 0),
|
||||
"$ai_latency": latency,
|
||||
"$ai_trace_id": posthog_trace_id,
|
||||
"$ai_trace_id": insights_trace_id,
|
||||
"$ai_base_url": str(self._client.base_url),
|
||||
**(posthog_properties or {}),
|
||||
**(insights_properties or {}),
|
||||
}
|
||||
|
||||
# Add web search count if present
|
||||
@@ -491,15 +491,15 @@ class WrappedCompletions:
|
||||
if available_tool_calls:
|
||||
event_properties["$ai_tools"] = available_tool_calls
|
||||
|
||||
if posthog_distinct_id is None:
|
||||
if insights_distinct_id is None:
|
||||
event_properties["$process_person_profile"] = False
|
||||
|
||||
if hasattr(self._client._ph_client, "capture"):
|
||||
self._client._ph_client.capture(
|
||||
distinct_id=posthog_distinct_id or posthog_trace_id,
|
||||
distinct_id=insights_distinct_id or insights_trace_id,
|
||||
event="$ai_generation",
|
||||
properties=event_properties,
|
||||
groups=posthog_groups,
|
||||
groups=insights_groups,
|
||||
)
|
||||
|
||||
|
||||
@@ -517,30 +517,30 @@ class WrappedEmbeddings:
|
||||
|
||||
async def create(
|
||||
self,
|
||||
posthog_distinct_id: Optional[str] = None,
|
||||
posthog_trace_id: Optional[str] = None,
|
||||
posthog_properties: Optional[Dict[str, Any]] = None,
|
||||
posthog_privacy_mode: bool = False,
|
||||
posthog_groups: Optional[Dict[str, Any]] = None,
|
||||
insights_distinct_id: Optional[str] = None,
|
||||
insights_trace_id: Optional[str] = None,
|
||||
insights_properties: Optional[Dict[str, Any]] = None,
|
||||
insights_privacy_mode: bool = False,
|
||||
insights_groups: Optional[Dict[str, Any]] = None,
|
||||
**kwargs: Any,
|
||||
):
|
||||
"""
|
||||
Create an embedding using OpenAI's 'embeddings.create' method, but also track usage in Insights.
|
||||
|
||||
Args:
|
||||
posthog_distinct_id: Optional ID to associate with the usage event.
|
||||
posthog_trace_id: Optional trace UUID for linking events.
|
||||
posthog_properties: Optional dictionary of extra properties to include in the event.
|
||||
posthog_privacy_mode: Whether to anonymize the input and output.
|
||||
posthog_groups: Optional dictionary of groups to associate with the event.
|
||||
insights_distinct_id: Optional ID to associate with the usage event.
|
||||
insights_trace_id: Optional trace UUID for linking events.
|
||||
insights_properties: Optional dictionary of extra properties to include in the event.
|
||||
insights_privacy_mode: Whether to anonymize the input and output.
|
||||
insights_groups: Optional dictionary of groups to associate with the event.
|
||||
**kwargs: Any additional parameters for the OpenAI Embeddings API.
|
||||
|
||||
Returns:
|
||||
The response from OpenAI's embeddings.create call.
|
||||
"""
|
||||
|
||||
if posthog_trace_id is None:
|
||||
posthog_trace_id = str(uuid.uuid4())
|
||||
if insights_trace_id is None:
|
||||
insights_trace_id = str(uuid.uuid4())
|
||||
|
||||
start_time = time.time()
|
||||
response = await self._original.create(**kwargs)
|
||||
@@ -563,27 +563,27 @@ class WrappedEmbeddings:
|
||||
"$ai_model": kwargs.get("model"),
|
||||
"$ai_input": with_privacy_mode(
|
||||
self._client._ph_client,
|
||||
posthog_privacy_mode,
|
||||
insights_privacy_mode,
|
||||
sanitize_openai_response(kwargs.get("input")),
|
||||
),
|
||||
"$ai_http_status": 200,
|
||||
"$ai_input_tokens": usage_stats.get("input_tokens", 0),
|
||||
"$ai_latency": latency,
|
||||
"$ai_trace_id": posthog_trace_id,
|
||||
"$ai_trace_id": insights_trace_id,
|
||||
"$ai_base_url": str(self._client.base_url),
|
||||
**(posthog_properties or {}),
|
||||
**(insights_properties or {}),
|
||||
}
|
||||
|
||||
if posthog_distinct_id is None:
|
||||
if insights_distinct_id is None:
|
||||
event_properties["$process_person_profile"] = False
|
||||
|
||||
# Send capture event for embeddings
|
||||
if hasattr(self._client._ph_client, "capture"):
|
||||
self._client._ph_client.capture(
|
||||
distinct_id=posthog_distinct_id or posthog_trace_id,
|
||||
distinct_id=insights_distinct_id or insights_trace_id,
|
||||
event="$ai_embedding",
|
||||
properties=event_properties,
|
||||
groups=posthog_groups,
|
||||
groups=insights_groups,
|
||||
)
|
||||
|
||||
return response
|
||||
@@ -637,21 +637,21 @@ class WrappedBetaCompletions:
|
||||
|
||||
async def parse(
|
||||
self,
|
||||
posthog_distinct_id: Optional[str] = None,
|
||||
posthog_trace_id: Optional[str] = None,
|
||||
posthog_properties: Optional[Dict[str, Any]] = None,
|
||||
posthog_privacy_mode: bool = False,
|
||||
posthog_groups: Optional[Dict[str, Any]] = None,
|
||||
insights_distinct_id: Optional[str] = None,
|
||||
insights_trace_id: Optional[str] = None,
|
||||
insights_properties: Optional[Dict[str, Any]] = None,
|
||||
insights_privacy_mode: bool = False,
|
||||
insights_groups: Optional[Dict[str, Any]] = None,
|
||||
**kwargs: Any,
|
||||
):
|
||||
return await call_llm_and_track_usage_async(
|
||||
posthog_distinct_id,
|
||||
insights_distinct_id,
|
||||
self._client._ph_client,
|
||||
"openai",
|
||||
posthog_trace_id,
|
||||
posthog_properties,
|
||||
posthog_privacy_mode,
|
||||
posthog_groups,
|
||||
insights_trace_id,
|
||||
insights_properties,
|
||||
insights_privacy_mode,
|
||||
insights_groups,
|
||||
self._client.base_url,
|
||||
self._original.parse,
|
||||
**kwargs,
|
||||
|
||||
@@ -28,16 +28,16 @@ class AzureOpenAI(openai.AzureOpenAI):
|
||||
|
||||
_ph_client: InsightsClient
|
||||
|
||||
def __init__(self, posthog_client: Optional[InsightsClient] = None, **kwargs):
|
||||
def __init__(self, insights_client: Optional[InsightsClient] = None, **kwargs):
|
||||
"""
|
||||
Args:
|
||||
api_key: Azure OpenAI API key.
|
||||
posthog_client: If provided, events will be captured via this client instead
|
||||
insights_client: If provided, events will be captured via this client instead
|
||||
of the global hanzo_insights.
|
||||
**openai_config: Any additional keyword args to set on Azure OpenAI (e.g. azure_endpoint="xxx").
|
||||
"""
|
||||
super().__init__(**kwargs)
|
||||
self._ph_client = posthog_client or setup()
|
||||
self._ph_client = insights_client or setup()
|
||||
|
||||
# Store original objects after parent initialization (only if they exist)
|
||||
self._original_chat = getattr(self, "chat", None)
|
||||
@@ -66,16 +66,16 @@ class AsyncAzureOpenAI(openai.AsyncAzureOpenAI):
|
||||
|
||||
_ph_client: InsightsClient
|
||||
|
||||
def __init__(self, posthog_client: Optional[InsightsClient] = None, **kwargs):
|
||||
def __init__(self, insights_client: Optional[InsightsClient] = None, **kwargs):
|
||||
"""
|
||||
Args:
|
||||
api_key: Azure OpenAI API key.
|
||||
posthog_client: If provided, events will be captured via this client instead
|
||||
insights_client: If provided, events will be captured via this client instead
|
||||
of the global hanzo_insights.
|
||||
**openai_config: Any additional keyword args to set on Azure OpenAI (e.g. azure_endpoint="xxx").
|
||||
"""
|
||||
super().__init__(**kwargs)
|
||||
self._ph_client = posthog_client or setup()
|
||||
self._ph_client = insights_client or setup()
|
||||
|
||||
# Store original objects after parent initialization (only if they exist)
|
||||
self._original_chat = getattr(self, "chat", None)
|
||||
|
||||
@@ -14,9 +14,9 @@ except ImportError:
|
||||
"Please install the OpenAI Agents SDK to use this feature: 'pip install openai-agents'"
|
||||
)
|
||||
|
||||
from hanzo_insights.ai.openai_agents.processor import InsightsTracingProcessor, PostHogTracingProcessor
|
||||
from hanzo_insights.ai.openai_agents.processor import InsightsTracingProcessor
|
||||
|
||||
__all__ = ["InsightsTracingProcessor", "PostHogTracingProcessor", "instrument"]
|
||||
__all__ = ["InsightsTracingProcessor", "instrument"]
|
||||
|
||||
|
||||
def instrument(
|
||||
|
||||
@@ -861,7 +861,3 @@ class InsightsTracingProcessor(TracingProcessor):
|
||||
self._client.flush()
|
||||
except Exception as e:
|
||||
log.debug(f"Error in force_flush: {e}")
|
||||
|
||||
|
||||
# Backward compatibility alias
|
||||
PostHogTracingProcessor = InsightsTracingProcessor
|
||||
|
||||
@@ -15,7 +15,7 @@ from hanzo_insights.utils import remove_trailing_slash
|
||||
|
||||
log = logging.getLogger("hanzo_insights")
|
||||
|
||||
APP_ENDPOINT = "https://us.posthog.com"
|
||||
APP_ENDPOINT = "https://us.insights.hanzo.ai"
|
||||
DEFAULT_CACHE_TTL_SECONDS = 300 # 5 minutes
|
||||
|
||||
PromptVariables = Dict[str, Union[str, int, float, bool]]
|
||||
@@ -64,14 +64,14 @@ class Prompts:
|
||||
from hanzo_insights.ai.prompts import Prompts
|
||||
|
||||
# With Insights client
|
||||
client = Insights('phc_xxx', host='https://us.posthog.com', personal_api_key='phx_xxx')
|
||||
client = Insights('phc_xxx', host='https://us.insights.hanzo.ai', personal_api_key='phx_xxx')
|
||||
prompts = Prompts(client)
|
||||
|
||||
# Or with direct options (no Insights client needed)
|
||||
prompts = Prompts(
|
||||
personal_api_key='phx_xxx',
|
||||
project_api_key='phc_xxx',
|
||||
host='https://us.posthog.com',
|
||||
host='https://us.insights.hanzo.ai',
|
||||
)
|
||||
|
||||
# Fetch with caching and fallback
|
||||
@@ -90,7 +90,7 @@ class Prompts:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
posthog: Optional[Any] = None,
|
||||
client: Optional[Any] = None,
|
||||
*,
|
||||
personal_api_key: Optional[str] = None,
|
||||
project_api_key: Optional[str] = None,
|
||||
@@ -101,9 +101,9 @@ class Prompts:
|
||||
Initialize Prompts.
|
||||
|
||||
Args:
|
||||
posthog: Insights client instance (optional if personal_api_key provided)
|
||||
personal_api_key: Direct personal API key (optional if posthog provided)
|
||||
project_api_key: Direct project API key (optional if posthog provided)
|
||||
client: Insights client instance (optional if personal_api_key provided)
|
||||
personal_api_key: Direct personal API key (optional if client provided)
|
||||
project_api_key: Direct project API key (optional if client provided)
|
||||
host: Insights host (defaults to app endpoint)
|
||||
default_cache_ttl_seconds: Default cache TTL (defaults to 300)
|
||||
"""
|
||||
@@ -112,11 +112,11 @@ class Prompts:
|
||||
)
|
||||
self._cache: Dict[PromptCacheKey, CachedPrompt] = {}
|
||||
|
||||
if posthog is not None:
|
||||
self._personal_api_key = getattr(posthog, "personal_api_key", None) or ""
|
||||
self._project_api_key = getattr(posthog, "api_key", None) or ""
|
||||
if client is not None:
|
||||
self._personal_api_key = getattr(client, "personal_api_key", None) or ""
|
||||
self._project_api_key = getattr(client, "api_key", None) or ""
|
||||
self._host = remove_trailing_slash(
|
||||
getattr(posthog, "raw_host", None) or APP_ENDPOINT
|
||||
getattr(client, "raw_host", None) or APP_ENDPOINT
|
||||
)
|
||||
else:
|
||||
self._personal_api_key = personal_api_key or ""
|
||||
|
||||
+41
-41
@@ -26,10 +26,10 @@ _TOKEN_PROPERTY_KEYS = frozenset(
|
||||
|
||||
|
||||
def _get_tokens_source(
|
||||
sdk_tags: Dict[str, Any], posthog_properties: Optional[Dict[str, Any]]
|
||||
sdk_tags: Dict[str, Any], insights_properties: Optional[Dict[str, Any]]
|
||||
) -> str:
|
||||
if posthog_properties and any(
|
||||
key in posthog_properties for key in _TOKEN_PROPERTY_KEYS
|
||||
if insights_properties and any(
|
||||
key in insights_properties for key in _TOKEN_PROPERTY_KEYS
|
||||
):
|
||||
return "passthrough"
|
||||
return "sdk"
|
||||
@@ -319,13 +319,13 @@ def merge_system_prompt(
|
||||
|
||||
|
||||
def call_llm_and_track_usage(
|
||||
posthog_distinct_id: Optional[str],
|
||||
insights_distinct_id: Optional[str],
|
||||
ph_client: InsightsClient,
|
||||
provider: str,
|
||||
posthog_trace_id: Optional[str],
|
||||
posthog_properties: Optional[Dict[str, Any]],
|
||||
posthog_privacy_mode: bool,
|
||||
posthog_groups: Optional[Dict[str, Any]],
|
||||
insights_trace_id: Optional[str],
|
||||
insights_properties: Optional[Dict[str, Any]],
|
||||
insights_privacy_mode: bool,
|
||||
insights_groups: Optional[Dict[str, Any]],
|
||||
base_url: str,
|
||||
call_method: Callable[..., Any],
|
||||
**kwargs: Any,
|
||||
@@ -342,8 +342,8 @@ def call_llm_and_track_usage(
|
||||
error_params: Dict[str, Any] = {}
|
||||
|
||||
with new_context(client=ph_client, capture_exceptions=False):
|
||||
if posthog_distinct_id:
|
||||
identify_context(posthog_distinct_id)
|
||||
if insights_distinct_id:
|
||||
identify_context(insights_distinct_id)
|
||||
|
||||
try:
|
||||
response = call_method(**kwargs)
|
||||
@@ -363,18 +363,18 @@ def call_llm_and_track_usage(
|
||||
end_time = time.time()
|
||||
latency = end_time - start_time
|
||||
|
||||
if posthog_trace_id is None:
|
||||
posthog_trace_id = str(uuid.uuid4())
|
||||
if insights_trace_id is None:
|
||||
insights_trace_id = str(uuid.uuid4())
|
||||
|
||||
# Check if we have a real user distinct_id (from param or outer context)
|
||||
has_person_distinct_id = (
|
||||
posthog_distinct_id is not None
|
||||
insights_distinct_id is not None
|
||||
or contexts.get_context_distinct_id() is not None
|
||||
)
|
||||
|
||||
if not has_person_distinct_id:
|
||||
# Fall back to trace_id as distinct_id when no real user id is available.
|
||||
identify_context(posthog_trace_id)
|
||||
identify_context(insights_trace_id)
|
||||
|
||||
if response and (
|
||||
hasattr(response, "usage")
|
||||
@@ -390,19 +390,19 @@ def call_llm_and_track_usage(
|
||||
tag("$ai_model_parameters", get_model_params(kwargs))
|
||||
tag(
|
||||
"$ai_input",
|
||||
with_privacy_mode(ph_client, posthog_privacy_mode, sanitized_messages),
|
||||
with_privacy_mode(ph_client, insights_privacy_mode, sanitized_messages),
|
||||
)
|
||||
tag(
|
||||
"$ai_output_choices",
|
||||
with_privacy_mode(
|
||||
ph_client, posthog_privacy_mode, format_response(response, provider)
|
||||
ph_client, insights_privacy_mode, format_response(response, provider)
|
||||
),
|
||||
)
|
||||
tag("$ai_http_status", http_status)
|
||||
tag("$ai_input_tokens", usage.get("input_tokens", 0))
|
||||
tag("$ai_output_tokens", usage.get("output_tokens", 0))
|
||||
tag("$ai_latency", latency)
|
||||
tag("$ai_trace_id", posthog_trace_id)
|
||||
tag("$ai_trace_id", insights_trace_id)
|
||||
tag("$ai_base_url", str(base_url))
|
||||
|
||||
available_tool_calls = extract_available_tool_calls(provider, kwargs)
|
||||
@@ -439,26 +439,26 @@ def call_llm_and_track_usage(
|
||||
tag(
|
||||
"$ai_instructions",
|
||||
with_privacy_mode(
|
||||
ph_client, posthog_privacy_mode, kwargs.get("instructions")
|
||||
ph_client, insights_privacy_mode, kwargs.get("instructions")
|
||||
),
|
||||
)
|
||||
|
||||
# send the event to posthog
|
||||
# send the event to Insights
|
||||
if hasattr(ph_client, "capture") and callable(ph_client.capture):
|
||||
sdk_tags = get_tags()
|
||||
merged_properties = {
|
||||
**sdk_tags,
|
||||
**(posthog_properties or {}),
|
||||
**(insights_properties or {}),
|
||||
**(error_params or {}),
|
||||
}
|
||||
merged_properties["$ai_tokens_source"] = _get_tokens_source(
|
||||
sdk_tags, posthog_properties
|
||||
sdk_tags, insights_properties
|
||||
)
|
||||
ph_client.capture(
|
||||
distinct_id=contexts.get_context_distinct_id(),
|
||||
event="$ai_generation",
|
||||
properties=merged_properties,
|
||||
groups=posthog_groups,
|
||||
groups=insights_groups,
|
||||
)
|
||||
|
||||
if error:
|
||||
@@ -468,13 +468,13 @@ def call_llm_and_track_usage(
|
||||
|
||||
|
||||
async def call_llm_and_track_usage_async(
|
||||
posthog_distinct_id: Optional[str],
|
||||
insights_distinct_id: Optional[str],
|
||||
ph_client: InsightsClient,
|
||||
provider: str,
|
||||
posthog_trace_id: Optional[str],
|
||||
posthog_properties: Optional[Dict[str, Any]],
|
||||
posthog_privacy_mode: bool,
|
||||
posthog_groups: Optional[Dict[str, Any]],
|
||||
insights_trace_id: Optional[str],
|
||||
insights_properties: Optional[Dict[str, Any]],
|
||||
insights_privacy_mode: bool,
|
||||
insights_groups: Optional[Dict[str, Any]],
|
||||
base_url: str,
|
||||
call_async_method: Callable[..., Any],
|
||||
**kwargs: Any,
|
||||
@@ -487,8 +487,8 @@ async def call_llm_and_track_usage_async(
|
||||
error_params: Dict[str, Any] = {}
|
||||
|
||||
with new_context(client=ph_client, capture_exceptions=False):
|
||||
if posthog_distinct_id:
|
||||
identify_context(posthog_distinct_id)
|
||||
if insights_distinct_id:
|
||||
identify_context(insights_distinct_id)
|
||||
|
||||
try:
|
||||
response = await call_async_method(**kwargs)
|
||||
@@ -508,18 +508,18 @@ async def call_llm_and_track_usage_async(
|
||||
end_time = time.time()
|
||||
latency = end_time - start_time
|
||||
|
||||
if posthog_trace_id is None:
|
||||
posthog_trace_id = str(uuid.uuid4())
|
||||
if insights_trace_id is None:
|
||||
insights_trace_id = str(uuid.uuid4())
|
||||
|
||||
# Check if we have a real user distinct_id (from param or outer context)
|
||||
has_person_distinct_id = (
|
||||
posthog_distinct_id is not None
|
||||
insights_distinct_id is not None
|
||||
or contexts.get_context_distinct_id() is not None
|
||||
)
|
||||
|
||||
if not has_person_distinct_id:
|
||||
# Fall back to trace_id as distinct_id when no real user id is available.
|
||||
identify_context(posthog_trace_id)
|
||||
identify_context(insights_trace_id)
|
||||
|
||||
if response and (
|
||||
hasattr(response, "usage")
|
||||
@@ -535,19 +535,19 @@ async def call_llm_and_track_usage_async(
|
||||
tag("$ai_model_parameters", get_model_params(kwargs))
|
||||
tag(
|
||||
"$ai_input",
|
||||
with_privacy_mode(ph_client, posthog_privacy_mode, sanitized_messages),
|
||||
with_privacy_mode(ph_client, insights_privacy_mode, sanitized_messages),
|
||||
)
|
||||
tag(
|
||||
"$ai_output_choices",
|
||||
with_privacy_mode(
|
||||
ph_client, posthog_privacy_mode, format_response(response, provider)
|
||||
ph_client, insights_privacy_mode, format_response(response, provider)
|
||||
),
|
||||
)
|
||||
tag("$ai_http_status", http_status)
|
||||
tag("$ai_input_tokens", usage.get("input_tokens", 0))
|
||||
tag("$ai_output_tokens", usage.get("output_tokens", 0))
|
||||
tag("$ai_latency", latency)
|
||||
tag("$ai_trace_id", posthog_trace_id)
|
||||
tag("$ai_trace_id", insights_trace_id)
|
||||
tag("$ai_base_url", str(base_url))
|
||||
|
||||
available_tool_calls = extract_available_tool_calls(provider, kwargs)
|
||||
@@ -584,26 +584,26 @@ async def call_llm_and_track_usage_async(
|
||||
tag(
|
||||
"$ai_instructions",
|
||||
with_privacy_mode(
|
||||
ph_client, posthog_privacy_mode, kwargs.get("instructions")
|
||||
ph_client, insights_privacy_mode, kwargs.get("instructions")
|
||||
),
|
||||
)
|
||||
|
||||
# send the event to posthog
|
||||
# send the event to Insights
|
||||
if hasattr(ph_client, "capture") and callable(ph_client.capture):
|
||||
sdk_tags = get_tags()
|
||||
merged_properties = {
|
||||
**sdk_tags,
|
||||
**(posthog_properties or {}),
|
||||
**(insights_properties or {}),
|
||||
**(error_params or {}),
|
||||
}
|
||||
merged_properties["$ai_tokens_source"] = _get_tokens_source(
|
||||
sdk_tags, posthog_properties
|
||||
sdk_tags, insights_properties
|
||||
)
|
||||
ph_client.capture(
|
||||
distinct_id=contexts.get_context_distinct_id(),
|
||||
event="$ai_generation",
|
||||
properties=merged_properties,
|
||||
groups=posthog_groups,
|
||||
groups=insights_groups,
|
||||
)
|
||||
|
||||
if error:
|
||||
|
||||
@@ -1108,7 +1108,7 @@ class Client(object):
|
||||
|
||||
if not msg.get("properties"):
|
||||
msg["properties"] = {}
|
||||
msg["properties"]["$lib"] = "posthog-python"
|
||||
msg["properties"]["$lib"] = "insights-python"
|
||||
msg["properties"]["$lib_version"] = VERSION
|
||||
|
||||
if disable_geoip is None:
|
||||
|
||||
@@ -104,7 +104,7 @@ class ContextScope:
|
||||
|
||||
|
||||
_context_stack: contextvars.ContextVar[Optional[ContextScope]] = contextvars.ContextVar(
|
||||
"posthog_context_stack", default=None
|
||||
"insights_context_stack", default=None
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -62,8 +62,8 @@ DEFAULT_CODE_VARIABLES_MASK_PATTERNS = [
|
||||
|
||||
DEFAULT_CODE_VARIABLES_IGNORE_PATTERNS = [r"^__.*"]
|
||||
|
||||
CODE_VARIABLES_REDACTED_VALUE = "$$_posthog_redacted_based_on_masking_rules_$$"
|
||||
CODE_VARIABLES_TOO_LONG_VALUE = "$$_posthog_value_too_long_$$"
|
||||
CODE_VARIABLES_REDACTED_VALUE = "$$_insights_redacted_based_on_masking_rules_$$"
|
||||
CODE_VARIABLES_TOO_LONG_VALUE = "$$_insights_value_too_long_$$"
|
||||
|
||||
_MAX_VALUE_LENGTH_FOR_PATTERN_MATCH = 5_000
|
||||
_MAX_COLLECTION_ITEMS_TO_SCAN = 100
|
||||
@@ -768,12 +768,12 @@ def set_in_app_in_frames(frames, in_app_exclude, in_app_include, project_root=No
|
||||
def exception_is_already_captured(error):
|
||||
# type: (ExceptionArg) -> bool
|
||||
if isinstance(error, BaseException):
|
||||
return hasattr(error, "__posthog_exception_captured")
|
||||
return hasattr(error, "__insights_exception_captured")
|
||||
# Autocaptured exceptions are passed as a tuple from our system hooks,
|
||||
# the second item is the exception value (the first is the exception type)
|
||||
elif isinstance(error, tuple) and len(error) > 1:
|
||||
return error[1] is not None and hasattr(
|
||||
error[1], "__posthog_exception_captured"
|
||||
error[1], "__insights_exception_captured"
|
||||
)
|
||||
else:
|
||||
return False # type: ignore[unreachable]
|
||||
@@ -782,14 +782,14 @@ def exception_is_already_captured(error):
|
||||
def mark_exception_as_captured(error, uuid):
|
||||
# type: (ExceptionArg, str) -> None
|
||||
if isinstance(error, BaseException):
|
||||
setattr(error, "__posthog_exception_captured", True)
|
||||
setattr(error, "__posthog_exception_uuid", uuid)
|
||||
setattr(error, "__insights_exception_captured", True)
|
||||
setattr(error, "__insights_exception_uuid", uuid)
|
||||
# Autocaptured exceptions are passed as a tuple from our system hooks,
|
||||
# the second item is the exception value (the first is the exception type)
|
||||
elif isinstance(error, tuple) and len(error) > 1:
|
||||
if error[1] is not None:
|
||||
setattr(error[1], "__posthog_exception_captured", True)
|
||||
setattr(error[1], "__posthog_exception_uuid", uuid)
|
||||
setattr(error[1], "__insights_exception_captured", True)
|
||||
setattr(error[1], "__insights_exception_uuid", uuid)
|
||||
|
||||
|
||||
def exc_info_from_error(error):
|
||||
|
||||
@@ -25,20 +25,20 @@ class InsightsContextMiddleware:
|
||||
"""Middleware to automatically track Django requests.
|
||||
|
||||
This middleware wraps all calls with an Insights context. It attempts to extract the following from the request headers:
|
||||
- Session ID, (extracted from `X-INSIGHTS-SESSION-ID` or `X-POSTHOG-SESSION-ID`)
|
||||
- Distinct ID, (extracted from `X-INSIGHTS-DISTINCT-ID` or `X-POSTHOG-DISTINCT-ID`)
|
||||
- Session ID, (extracted from `X-INSIGHTS-SESSION-ID`)
|
||||
- Distinct ID, (extracted from `X-INSIGHTS-DISTINCT-ID`)
|
||||
- Request URL as $current_url
|
||||
- Request Method as $request_method
|
||||
|
||||
The context will also auto-capture exceptions and send them to Insights, unless you disable it by setting
|
||||
`INSIGHTS_MW_CAPTURE_EXCEPTIONS` (or `POSTHOG_MW_CAPTURE_EXCEPTIONS`) to `False` in your Django settings.
|
||||
`INSIGHTS_MW_CAPTURE_EXCEPTIONS` to `False` in your Django settings.
|
||||
The exceptions are captured using the global client, unless the setting `INSIGHTS_MW_CLIENT`
|
||||
(or `POSTHOG_MW_CLIENT`) is set to a custom client instance.
|
||||
is set to a custom client instance.
|
||||
|
||||
The middleware behaviour is customisable through 3 additional functions:
|
||||
- `INSIGHTS_MW_EXTRA_TAGS` (or `POSTHOG_MW_EXTRA_TAGS`), which is a Callable[[HttpRequest], Dict[str, Any]] expected to return a dictionary of additional tags to be added to the context.
|
||||
- `INSIGHTS_MW_REQUEST_FILTER` (or `POSTHOG_MW_REQUEST_FILTER`), which is a Callable[[HttpRequest], bool] expected to return `False` if the request should not be tracked.
|
||||
- `INSIGHTS_MW_TAG_MAP` (or `POSTHOG_MW_TAG_MAP`), which is a Callable[[Dict[str, Any]], Dict[str, Any]], which you can use to modify the tags before they're added to the context.
|
||||
- `INSIGHTS_MW_EXTRA_TAGS`, which is a Callable[[HttpRequest], Dict[str, Any]] expected to return a dictionary of additional tags to be added to the context.
|
||||
- `INSIGHTS_MW_REQUEST_FILTER`, which is a Callable[[HttpRequest], bool] expected to return `False` if the request should not be tracked.
|
||||
- `INSIGHTS_MW_TAG_MAP`, which is a Callable[[Dict[str, Any]], Dict[str, Any]], which you can use to modify the tags before they're added to the context.
|
||||
|
||||
You can use the `INSIGHTS_MW_TAG_MAP` function to remove any default tags you don't want to capture, or override them with your own values.
|
||||
|
||||
@@ -67,14 +67,10 @@ class InsightsContextMiddleware:
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
# Support both INSIGHTS_MW_* and legacy POSTHOG_MW_* setting names
|
||||
def _get_setting(name):
|
||||
insights_name = f"INSIGHTS_MW_{name}"
|
||||
posthog_name = f"POSTHOG_MW_{name}"
|
||||
if hasattr(settings, insights_name):
|
||||
return getattr(settings, insights_name)
|
||||
if hasattr(settings, posthog_name):
|
||||
return getattr(settings, posthog_name)
|
||||
return None
|
||||
|
||||
extra_tags = _get_setting("EXTRA_TAGS")
|
||||
@@ -131,13 +127,13 @@ class InsightsContextMiddleware:
|
||||
"""
|
||||
tags = {}
|
||||
|
||||
# Extract session ID from X-INSIGHTS-SESSION-ID or X-POSTHOG-SESSION-ID header
|
||||
session_id = request.headers.get("X-INSIGHTS-SESSION-ID") or request.headers.get("X-POSTHOG-SESSION-ID")
|
||||
# Extract session ID from X-INSIGHTS-SESSION-ID header
|
||||
session_id = request.headers.get("X-INSIGHTS-SESSION-ID")
|
||||
if session_id:
|
||||
contexts.set_context_session(session_id)
|
||||
|
||||
# Extract distinct ID from X-INSIGHTS-DISTINCT-ID or X-POSTHOG-DISTINCT-ID header or request user id
|
||||
distinct_id = request.headers.get("X-INSIGHTS-DISTINCT-ID") or request.headers.get("X-POSTHOG-DISTINCT-ID") or user_id
|
||||
# Extract distinct ID from X-INSIGHTS-DISTINCT-ID header or request user id
|
||||
distinct_id = request.headers.get("X-INSIGHTS-DISTINCT-ID") or user_id
|
||||
if distinct_id:
|
||||
contexts.identify_context(distinct_id)
|
||||
|
||||
@@ -323,7 +319,3 @@ class InsightsContextMiddleware:
|
||||
from hanzo_insights import capture_exception
|
||||
|
||||
capture_exception(exception)
|
||||
|
||||
|
||||
# Backward compatibility alias
|
||||
PosthogContextMiddleware = InsightsContextMiddleware
|
||||
|
||||
@@ -159,8 +159,8 @@ def disable_connection_reuse() -> None:
|
||||
_pooling_enabled = False
|
||||
|
||||
|
||||
US_INGESTION_ENDPOINT = "https://us.i.posthog.com"
|
||||
EU_INGESTION_ENDPOINT = "https://eu.i.posthog.com"
|
||||
US_INGESTION_ENDPOINT = "https://us.i.insights.hanzo.ai"
|
||||
EU_INGESTION_ENDPOINT = "https://eu.i.insights.hanzo.ai"
|
||||
DEFAULT_HOST = US_INGESTION_ENDPOINT
|
||||
USER_AGENT = "hanzo-insights-python/" + VERSION
|
||||
|
||||
@@ -169,9 +169,9 @@ def determine_server_host(host: Optional[str]) -> str:
|
||||
"""Determines the server host to use."""
|
||||
host_or_default = host or DEFAULT_HOST
|
||||
trimmed_host = remove_trailing_slash(host_or_default)
|
||||
if trimmed_host in ("https://app.posthog.com", "https://us.posthog.com", "https://insights.hanzo.ai"):
|
||||
if trimmed_host in ("https://app.posthog.com", "https://us.posthog.com", "https://insights.hanzo.ai", "https://us.insights.hanzo.ai"):
|
||||
return US_INGESTION_ENDPOINT
|
||||
elif trimmed_host == "https://eu.posthog.com":
|
||||
elif trimmed_host in ("https://eu.posthog.com", "https://eu.insights.hanzo.ai"):
|
||||
return EU_INGESTION_ENDPOINT
|
||||
else:
|
||||
return host_or_default
|
||||
|
||||
@@ -279,12 +279,12 @@ def test_basic_completion(mock_client, mock_anthropic_response):
|
||||
with patch(
|
||||
"anthropic.resources.Messages.create", return_value=mock_anthropic_response
|
||||
):
|
||||
client = Anthropic(api_key="test-key", posthog_client=mock_client)
|
||||
client = Anthropic(api_key="test-key", insights_client=mock_client)
|
||||
response = client.messages.create(
|
||||
model="claude-3-opus-20240229",
|
||||
messages=[{"role": "user", "content": "Hello"}],
|
||||
posthog_distinct_id="test-id",
|
||||
posthog_properties={"foo": "bar"},
|
||||
insights_distinct_id="test-id",
|
||||
insights_properties={"foo": "bar"},
|
||||
)
|
||||
|
||||
assert response == mock_anthropic_response
|
||||
@@ -325,12 +325,12 @@ def test_tokens_source_passthrough(mock_client, mock_anthropic_response):
|
||||
with patch(
|
||||
"anthropic.resources.Messages.create", return_value=mock_anthropic_response
|
||||
):
|
||||
client = Anthropic(api_key="test-key", posthog_client=mock_client)
|
||||
client = Anthropic(api_key="test-key", insights_client=mock_client)
|
||||
client.messages.create(
|
||||
model="claude-3-opus-20240229",
|
||||
messages=[{"role": "user", "content": "Hello"}],
|
||||
posthog_distinct_id="test-id",
|
||||
posthog_properties={"$ai_input_tokens": 99999},
|
||||
insights_distinct_id="test-id",
|
||||
insights_properties={"$ai_input_tokens": 99999},
|
||||
)
|
||||
|
||||
props = mock_client.capture.call_args[1]["properties"]
|
||||
@@ -342,12 +342,12 @@ def test_groups(mock_client, mock_anthropic_response):
|
||||
with patch(
|
||||
"anthropic.resources.Messages.create", return_value=mock_anthropic_response
|
||||
):
|
||||
client = Anthropic(api_key="test-key", posthog_client=mock_client)
|
||||
client = Anthropic(api_key="test-key", insights_client=mock_client)
|
||||
response = client.messages.create(
|
||||
model="claude-3-opus-20240229",
|
||||
messages=[{"role": "user", "content": "Hello"}],
|
||||
posthog_distinct_id="test-id",
|
||||
posthog_groups={"company": "test_company"},
|
||||
insights_distinct_id="test-id",
|
||||
insights_groups={"company": "test_company"},
|
||||
)
|
||||
|
||||
assert response == mock_anthropic_response
|
||||
@@ -361,12 +361,12 @@ def test_privacy_mode_local(mock_client, mock_anthropic_response):
|
||||
with patch(
|
||||
"anthropic.resources.Messages.create", return_value=mock_anthropic_response
|
||||
):
|
||||
client = Anthropic(api_key="test-key", posthog_client=mock_client)
|
||||
client = Anthropic(api_key="test-key", insights_client=mock_client)
|
||||
response = client.messages.create(
|
||||
model="claude-3-opus-20240229",
|
||||
messages=[{"role": "user", "content": "Hello"}],
|
||||
posthog_distinct_id="test-id",
|
||||
posthog_privacy_mode=True,
|
||||
insights_distinct_id="test-id",
|
||||
insights_privacy_mode=True,
|
||||
)
|
||||
|
||||
assert response == mock_anthropic_response
|
||||
@@ -383,12 +383,12 @@ def test_privacy_mode_global(mock_client, mock_anthropic_response):
|
||||
"anthropic.resources.Messages.create", return_value=mock_anthropic_response
|
||||
):
|
||||
mock_client.privacy_mode = True
|
||||
client = Anthropic(api_key="test-key", posthog_client=mock_client)
|
||||
client = Anthropic(api_key="test-key", insights_client=mock_client)
|
||||
response = client.messages.create(
|
||||
model="claude-3-opus-20240229",
|
||||
messages=[{"role": "user", "content": "Hello"}],
|
||||
posthog_distinct_id="test-id",
|
||||
posthog_privacy_mode=False,
|
||||
insights_distinct_id="test-id",
|
||||
insights_privacy_mode=False,
|
||||
)
|
||||
|
||||
assert response == mock_anthropic_response
|
||||
@@ -407,14 +407,14 @@ def test_basic_integration(mock_client):
|
||||
"anthropic.resources.Messages.create",
|
||||
return_value=create_mock_response(),
|
||||
):
|
||||
client = Anthropic(posthog_client=mock_client)
|
||||
client = Anthropic(insights_client=mock_client)
|
||||
client.messages.create(
|
||||
model="claude-3-opus-20240229",
|
||||
messages=[{"role": "user", "content": "Foo"}],
|
||||
max_tokens=1,
|
||||
temperature=0,
|
||||
posthog_distinct_id="test-id",
|
||||
posthog_properties={"foo": "bar"},
|
||||
insights_distinct_id="test-id",
|
||||
insights_properties={"foo": "bar"},
|
||||
system="You must always answer with 'Bar'.",
|
||||
)
|
||||
|
||||
@@ -452,7 +452,7 @@ async def test_basic_async_integration(mock_client):
|
||||
"anthropic.resources.messages.AsyncMessages.create",
|
||||
side_effect=mock_async_create,
|
||||
):
|
||||
client = AsyncAnthropic(posthog_client=mock_client)
|
||||
client = AsyncAnthropic(insights_client=mock_client)
|
||||
await client.messages.create(
|
||||
model="claude-3-opus-20240229",
|
||||
messages=[
|
||||
@@ -460,8 +460,8 @@ async def test_basic_async_integration(mock_client):
|
||||
],
|
||||
max_tokens=1,
|
||||
temperature=0,
|
||||
posthog_distinct_id="test-id",
|
||||
posthog_properties={"foo": "bar"},
|
||||
insights_distinct_id="test-id",
|
||||
insights_properties={"foo": "bar"},
|
||||
)
|
||||
|
||||
assert mock_client.capture.call_count == 1
|
||||
@@ -513,7 +513,7 @@ async def test_async_streaming_system_prompt(mock_client):
|
||||
"anthropic.resources.messages.AsyncMessages.create",
|
||||
side_effect=async_create_wrapper,
|
||||
):
|
||||
client = AsyncAnthropic(posthog_client=mock_client)
|
||||
client = AsyncAnthropic(insights_client=mock_client)
|
||||
response = await client.messages.create(
|
||||
model="claude-3-opus-20240229",
|
||||
system="You must always answer with 'Bar'.",
|
||||
@@ -541,7 +541,7 @@ def test_error(mock_client, mock_anthropic_response):
|
||||
with patch(
|
||||
"anthropic.resources.Messages.create", side_effect=Exception("Test error")
|
||||
):
|
||||
client = Anthropic(api_key="test-key", posthog_client=mock_client)
|
||||
client = Anthropic(api_key="test-key", insights_client=mock_client)
|
||||
with pytest.raises(Exception):
|
||||
client.messages.create(
|
||||
model="claude-3-opus-20240229",
|
||||
@@ -561,12 +561,12 @@ def test_cached_tokens(mock_client, mock_anthropic_response_with_cached_tokens):
|
||||
"anthropic.resources.Messages.create",
|
||||
return_value=mock_anthropic_response_with_cached_tokens,
|
||||
):
|
||||
client = Anthropic(api_key="test-key", posthog_client=mock_client)
|
||||
client = Anthropic(api_key="test-key", insights_client=mock_client)
|
||||
response = client.messages.create(
|
||||
model="claude-3-opus-20240229",
|
||||
messages=[{"role": "user", "content": "Hello"}],
|
||||
posthog_distinct_id="test-id",
|
||||
posthog_properties={"foo": "bar"},
|
||||
insights_distinct_id="test-id",
|
||||
insights_properties={"foo": "bar"},
|
||||
)
|
||||
|
||||
assert response == mock_anthropic_response_with_cached_tokens
|
||||
@@ -600,7 +600,7 @@ def test_tool_definition(mock_client, mock_anthropic_response):
|
||||
"anthropic.resources.Messages.create",
|
||||
return_value=mock_anthropic_response,
|
||||
):
|
||||
client = Anthropic(api_key="test-key", posthog_client=mock_client)
|
||||
client = Anthropic(api_key="test-key", insights_client=mock_client)
|
||||
|
||||
tools = [
|
||||
{
|
||||
@@ -625,8 +625,8 @@ def test_tool_definition(mock_client, mock_anthropic_response):
|
||||
temperature=0.7,
|
||||
tools=tools,
|
||||
messages=[{"role": "user", "content": "hey"}],
|
||||
posthog_distinct_id="test-id",
|
||||
posthog_properties={"foo": "bar"},
|
||||
insights_distinct_id="test-id",
|
||||
insights_properties={"foo": "bar"},
|
||||
)
|
||||
|
||||
assert response == mock_anthropic_response
|
||||
@@ -662,7 +662,7 @@ def test_tool_calls_in_output_choices(
|
||||
"anthropic.resources.Messages.create",
|
||||
return_value=mock_anthropic_response_with_tool_calls,
|
||||
):
|
||||
client = Anthropic(api_key="test-key", posthog_client=mock_client)
|
||||
client = Anthropic(api_key="test-key", insights_client=mock_client)
|
||||
response = client.messages.create(
|
||||
model="claude-3-5-sonnet-20241022",
|
||||
max_tokens=200,
|
||||
@@ -680,7 +680,7 @@ def test_tool_calls_in_output_choices(
|
||||
},
|
||||
}
|
||||
],
|
||||
posthog_distinct_id="test-id",
|
||||
insights_distinct_id="test-id",
|
||||
)
|
||||
|
||||
assert response == mock_anthropic_response_with_tool_calls
|
||||
@@ -724,7 +724,7 @@ def test_tool_calls_only_no_content(
|
||||
"anthropic.resources.Messages.create",
|
||||
return_value=mock_anthropic_response_tool_calls_only,
|
||||
):
|
||||
client = Anthropic(api_key="test-key", posthog_client=mock_client)
|
||||
client = Anthropic(api_key="test-key", insights_client=mock_client)
|
||||
response = client.messages.create(
|
||||
model="claude-3-5-sonnet-20241022",
|
||||
max_tokens=200,
|
||||
@@ -743,7 +743,7 @@ def test_tool_calls_only_no_content(
|
||||
},
|
||||
}
|
||||
],
|
||||
posthog_distinct_id="test-id",
|
||||
insights_distinct_id="test-id",
|
||||
)
|
||||
|
||||
assert response == mock_anthropic_response_tool_calls_only
|
||||
@@ -790,7 +790,7 @@ def test_async_tool_calls_in_output_choices(
|
||||
"anthropic.resources.AsyncMessages.create",
|
||||
side_effect=mock_async_create,
|
||||
):
|
||||
async_client = AsyncAnthropic(api_key="test-key", posthog_client=mock_client)
|
||||
async_client = AsyncAnthropic(api_key="test-key", insights_client=mock_client)
|
||||
|
||||
async def run_test():
|
||||
return await async_client.messages.create(
|
||||
@@ -810,7 +810,7 @@ def test_async_tool_calls_in_output_choices(
|
||||
},
|
||||
}
|
||||
],
|
||||
posthog_distinct_id="test-id",
|
||||
insights_distinct_id="test-id",
|
||||
)
|
||||
|
||||
response = asyncio.run(run_test())
|
||||
@@ -855,7 +855,7 @@ def test_streaming_with_tool_calls(mock_client, mock_anthropic_stream_with_tools
|
||||
"anthropic.resources.Messages.create",
|
||||
return_value=mock_anthropic_stream_with_tools,
|
||||
):
|
||||
client = Anthropic(api_key="test-key", posthog_client=mock_client)
|
||||
client = Anthropic(api_key="test-key", insights_client=mock_client)
|
||||
response = client.messages.create(
|
||||
model="claude-3-5-sonnet-20241022",
|
||||
system="You are a helpful weather assistant.",
|
||||
@@ -877,7 +877,7 @@ def test_streaming_with_tool_calls(mock_client, mock_anthropic_stream_with_tools
|
||||
}
|
||||
],
|
||||
stream=True,
|
||||
posthog_distinct_id="test-id",
|
||||
insights_distinct_id="test-id",
|
||||
)
|
||||
|
||||
# Consume the stream - this triggers the finally block synchronously
|
||||
@@ -977,7 +977,7 @@ def test_async_streaming_with_tool_calls(mock_client, mock_anthropic_stream_with
|
||||
"anthropic.resources.AsyncMessages.create",
|
||||
side_effect=mock_async_create,
|
||||
):
|
||||
async_client = AsyncAnthropic(api_key="test-key", posthog_client=mock_client)
|
||||
async_client = AsyncAnthropic(api_key="test-key", insights_client=mock_client)
|
||||
|
||||
async def run_test():
|
||||
response = await async_client.messages.create(
|
||||
@@ -1001,7 +1001,7 @@ def test_async_streaming_with_tool_calls(mock_client, mock_anthropic_stream_with
|
||||
}
|
||||
],
|
||||
stream=True,
|
||||
posthog_distinct_id="test-id",
|
||||
insights_distinct_id="test-id",
|
||||
)
|
||||
|
||||
# Consume the async stream
|
||||
@@ -1101,11 +1101,11 @@ def test_web_search_count(mock_client):
|
||||
mock_response = MockResponseWithWebSearch()
|
||||
|
||||
with patch("anthropic.resources.Messages.create", return_value=mock_response):
|
||||
client = Anthropic(api_key="test-key", posthog_client=mock_client)
|
||||
client = Anthropic(api_key="test-key", insights_client=mock_client)
|
||||
response = client.messages.create(
|
||||
model="claude-3-opus-20240229",
|
||||
messages=[{"role": "user", "content": "Search for recent news"}],
|
||||
posthog_distinct_id="test-id",
|
||||
insights_distinct_id="test-id",
|
||||
)
|
||||
|
||||
assert response == mock_response
|
||||
@@ -1179,12 +1179,12 @@ def test_streaming_with_web_search(mock_client, mock_anthropic_stream_with_web_s
|
||||
"anthropic.resources.Messages.create",
|
||||
return_value=mock_anthropic_stream_with_web_search,
|
||||
):
|
||||
client = Anthropic(api_key="test-key", posthog_client=mock_client)
|
||||
client = Anthropic(api_key="test-key", insights_client=mock_client)
|
||||
response = client.messages.create(
|
||||
model="claude-3-opus-20240229",
|
||||
messages=[{"role": "user", "content": "Search for recent news"}],
|
||||
stream=True,
|
||||
posthog_distinct_id="test-id",
|
||||
insights_distinct_id="test-id",
|
||||
)
|
||||
|
||||
# Consume the stream - this triggers the finally block synchronously
|
||||
@@ -1234,13 +1234,13 @@ def test_async_with_web_search(mock_client):
|
||||
"anthropic.resources.AsyncMessages.create",
|
||||
side_effect=mock_async_create,
|
||||
):
|
||||
async_client = AsyncAnthropic(api_key="test-key", posthog_client=mock_client)
|
||||
async_client = AsyncAnthropic(api_key="test-key", insights_client=mock_client)
|
||||
|
||||
async def run_test():
|
||||
response = await async_client.messages.create(
|
||||
model="claude-3-opus-20240229",
|
||||
messages=[{"role": "user", "content": "Search for recent news"}],
|
||||
posthog_distinct_id="test-id",
|
||||
insights_distinct_id="test-id",
|
||||
)
|
||||
return response
|
||||
|
||||
@@ -1278,14 +1278,14 @@ def test_async_streaming_with_web_search(
|
||||
"anthropic.resources.AsyncMessages.create",
|
||||
side_effect=mock_async_create,
|
||||
):
|
||||
async_client = AsyncAnthropic(api_key="test-key", posthog_client=mock_client)
|
||||
async_client = AsyncAnthropic(api_key="test-key", insights_client=mock_client)
|
||||
|
||||
async def run_test():
|
||||
response = await async_client.messages.create(
|
||||
model="claude-3-opus-20240229",
|
||||
messages=[{"role": "user", "content": "Search for recent news"}],
|
||||
stream=True,
|
||||
posthog_distinct_id="test-id",
|
||||
insights_distinct_id="test-id",
|
||||
)
|
||||
|
||||
# Consume the async stream
|
||||
@@ -1318,11 +1318,11 @@ def test_no_distinct_id_uses_trace_id_and_personless(
|
||||
with patch(
|
||||
"anthropic.resources.Messages.create", return_value=mock_anthropic_response
|
||||
):
|
||||
client = Anthropic(api_key="test-key", posthog_client=mock_client)
|
||||
client = Anthropic(api_key="test-key", insights_client=mock_client)
|
||||
client.messages.create(
|
||||
model="claude-3-opus-20240229",
|
||||
messages=[{"role": "user", "content": "Hello"}],
|
||||
posthog_trace_id="trace-123",
|
||||
insights_trace_id="trace-123",
|
||||
)
|
||||
|
||||
call_args = mock_client.capture.call_args[1]
|
||||
@@ -1335,16 +1335,16 @@ def test_no_distinct_id_uses_trace_id_and_personless(
|
||||
def test_explicit_distinct_id_creates_person_profile(
|
||||
mock_client, mock_anthropic_response
|
||||
):
|
||||
"""When posthog_distinct_id is explicitly passed, it is used and event is not personless."""
|
||||
"""When insights_distinct_id is explicitly passed, it is used and event is not personless."""
|
||||
with patch(
|
||||
"anthropic.resources.Messages.create", return_value=mock_anthropic_response
|
||||
):
|
||||
client = Anthropic(api_key="test-key", posthog_client=mock_client)
|
||||
client = Anthropic(api_key="test-key", insights_client=mock_client)
|
||||
client.messages.create(
|
||||
model="claude-3-opus-20240229",
|
||||
messages=[{"role": "user", "content": "Hello"}],
|
||||
posthog_distinct_id="user-123",
|
||||
posthog_trace_id="trace-123",
|
||||
insights_distinct_id="user-123",
|
||||
insights_trace_id="trace-123",
|
||||
)
|
||||
|
||||
call_args = mock_client.capture.call_args[1]
|
||||
@@ -1362,13 +1362,13 @@ def test_outer_context_distinct_id_is_used(mock_client, mock_anthropic_response)
|
||||
with patch(
|
||||
"anthropic.resources.Messages.create", return_value=mock_anthropic_response
|
||||
):
|
||||
client = Anthropic(api_key="test-key", posthog_client=mock_client)
|
||||
client = Anthropic(api_key="test-key", insights_client=mock_client)
|
||||
with new_context():
|
||||
identify_context("outer-user-456")
|
||||
client.messages.create(
|
||||
model="claude-3-opus-20240229",
|
||||
messages=[{"role": "user", "content": "Hello"}],
|
||||
posthog_trace_id="trace-123",
|
||||
insights_trace_id="trace-123",
|
||||
)
|
||||
|
||||
call_args = mock_client.capture.call_args[1]
|
||||
@@ -1384,18 +1384,18 @@ def test_outer_context_distinct_id_is_used(mock_client, mock_anthropic_response)
|
||||
def test_explicit_distinct_id_overrides_outer_context(
|
||||
mock_client, mock_anthropic_response
|
||||
):
|
||||
"""When both outer context and explicit posthog_distinct_id are set, explicit wins."""
|
||||
"""When both outer context and explicit insights_distinct_id are set, explicit wins."""
|
||||
with patch(
|
||||
"anthropic.resources.Messages.create", return_value=mock_anthropic_response
|
||||
):
|
||||
client = Anthropic(api_key="test-key", posthog_client=mock_client)
|
||||
client = Anthropic(api_key="test-key", insights_client=mock_client)
|
||||
with new_context():
|
||||
identify_context("outer-user-456")
|
||||
client.messages.create(
|
||||
model="claude-3-opus-20240229",
|
||||
messages=[{"role": "user", "content": "Hello"}],
|
||||
posthog_distinct_id="explicit-user-789",
|
||||
posthog_trace_id="trace-123",
|
||||
insights_distinct_id="explicit-user-789",
|
||||
insights_trace_id="trace-123",
|
||||
)
|
||||
|
||||
call_args = mock_client.capture.call_args[1]
|
||||
|
||||
@@ -172,13 +172,13 @@ def test_new_client_basic_generation(
|
||||
"""Test the new Client/Models API structure"""
|
||||
mock_google_genai_client.models.generate_content.return_value = mock_gemini_response
|
||||
|
||||
client = Client(api_key="test-key", posthog_client=mock_client)
|
||||
client = Client(api_key="test-key", insights_client=mock_client)
|
||||
|
||||
response = client.models.generate_content(
|
||||
model="gemini-2.0-flash",
|
||||
contents=["Tell me a fun fact about hedgehogs"],
|
||||
posthog_distinct_id="test-id",
|
||||
posthog_properties={"foo": "bar"},
|
||||
insights_distinct_id="test-id",
|
||||
insights_properties={"foo": "bar"},
|
||||
)
|
||||
|
||||
assert response == mock_gemini_response
|
||||
@@ -239,13 +239,13 @@ def test_new_client_streaming_with_generate_content_stream(
|
||||
mock_streaming_response()
|
||||
)
|
||||
|
||||
client = Client(api_key="test-key", posthog_client=mock_client)
|
||||
client = Client(api_key="test-key", insights_client=mock_client)
|
||||
|
||||
response = client.models.generate_content_stream(
|
||||
model="gemini-2.0-flash",
|
||||
contents=["Write a short story"],
|
||||
posthog_distinct_id="test-id",
|
||||
posthog_properties={"feature": "streaming"},
|
||||
insights_distinct_id="test-id",
|
||||
insights_properties={"feature": "streaming"},
|
||||
)
|
||||
|
||||
chunks = list(response)
|
||||
@@ -298,7 +298,7 @@ def test_new_client_streaming_with_tools(mock_client, mock_google_genai_client):
|
||||
mock_streaming_response()
|
||||
)
|
||||
|
||||
client = Client(api_key="test-key", posthog_client=mock_client)
|
||||
client = Client(api_key="test-key", insights_client=mock_client)
|
||||
|
||||
# Create mock tools configuration
|
||||
mock_tool = MagicMock()
|
||||
@@ -326,8 +326,8 @@ def test_new_client_streaming_with_tools(mock_client, mock_google_genai_client):
|
||||
model="gemini-2.0-flash",
|
||||
contents=["What's the weather in SF?"],
|
||||
config=mock_config,
|
||||
posthog_distinct_id="test-id",
|
||||
posthog_properties={"feature": "streaming_with_tools"},
|
||||
insights_distinct_id="test-id",
|
||||
insights_properties={"feature": "streaming_with_tools"},
|
||||
)
|
||||
|
||||
chunks = list(response)
|
||||
@@ -357,13 +357,13 @@ def test_new_client_groups(mock_client, mock_google_genai_client, mock_gemini_re
|
||||
"""Test groups functionality with new Client API"""
|
||||
mock_google_genai_client.models.generate_content.return_value = mock_gemini_response
|
||||
|
||||
client = Client(api_key="test-key", posthog_client=mock_client)
|
||||
client = Client(api_key="test-key", insights_client=mock_client)
|
||||
|
||||
client.models.generate_content(
|
||||
model="gemini-2.0-flash",
|
||||
contents=["Hello"],
|
||||
posthog_distinct_id="test-id",
|
||||
posthog_groups={"company": "company_123"},
|
||||
insights_distinct_id="test-id",
|
||||
insights_groups={"company": "company_123"},
|
||||
)
|
||||
|
||||
call_args = mock_client.capture.call_args[1]
|
||||
@@ -376,13 +376,13 @@ def test_new_client_privacy_mode_local(
|
||||
"""Test local privacy mode with new Client API"""
|
||||
mock_google_genai_client.models.generate_content.return_value = mock_gemini_response
|
||||
|
||||
client = Client(api_key="test-key", posthog_client=mock_client)
|
||||
client = Client(api_key="test-key", insights_client=mock_client)
|
||||
|
||||
client.models.generate_content(
|
||||
model="gemini-2.0-flash",
|
||||
contents=["Hello"],
|
||||
posthog_distinct_id="test-id",
|
||||
posthog_privacy_mode=True,
|
||||
insights_distinct_id="test-id",
|
||||
insights_privacy_mode=True,
|
||||
)
|
||||
|
||||
call_args = mock_client.capture.call_args[1]
|
||||
@@ -399,12 +399,12 @@ def test_new_client_privacy_mode_global(
|
||||
|
||||
mock_google_genai_client.models.generate_content.return_value = mock_gemini_response
|
||||
|
||||
client = Client(api_key="test-key", posthog_client=mock_client)
|
||||
client = Client(api_key="test-key", insights_client=mock_client)
|
||||
|
||||
client.models.generate_content(
|
||||
model="gemini-2.0-flash",
|
||||
contents=["Hello"],
|
||||
posthog_distinct_id="test-id",
|
||||
insights_distinct_id="test-id",
|
||||
)
|
||||
|
||||
call_args = mock_client.capture.call_args[1]
|
||||
@@ -419,11 +419,11 @@ def test_new_client_different_input_formats(
|
||||
"""Test different input formats with new Client API"""
|
||||
mock_google_genai_client.models.generate_content.return_value = mock_gemini_response
|
||||
|
||||
client = Client(api_key="test-key", posthog_client=mock_client)
|
||||
client = Client(api_key="test-key", insights_client=mock_client)
|
||||
|
||||
# Test string input
|
||||
client.models.generate_content(
|
||||
model="gemini-2.0-flash", contents="Hello", posthog_distinct_id="test-id"
|
||||
model="gemini-2.0-flash", contents="Hello", insights_distinct_id="test-id"
|
||||
)
|
||||
call_args = mock_client.capture.call_args[1]
|
||||
props = call_args["properties"]
|
||||
@@ -434,7 +434,7 @@ def test_new_client_different_input_formats(
|
||||
client.models.generate_content(
|
||||
model="gemini-2.0-flash",
|
||||
contents=[{"role": "user", "parts": [{"text": "hey"}]}],
|
||||
posthog_distinct_id="test-id",
|
||||
insights_distinct_id="test-id",
|
||||
)
|
||||
call_args = mock_client.capture.call_args[1]
|
||||
props = call_args["properties"]
|
||||
@@ -447,7 +447,7 @@ def test_new_client_different_input_formats(
|
||||
client.models.generate_content(
|
||||
model="gemini-2.0-flash",
|
||||
contents=[{"role": "user", "parts": [{"text": "Hello "}, {"text": "world"}]}],
|
||||
posthog_distinct_id="test-id",
|
||||
insights_distinct_id="test-id",
|
||||
)
|
||||
call_args = mock_client.capture.call_args[1]
|
||||
props = call_args["properties"]
|
||||
@@ -464,7 +464,7 @@ def test_new_client_different_input_formats(
|
||||
# Test list input with string
|
||||
mock_client.capture.reset_mock()
|
||||
client.models.generate_content(
|
||||
model="gemini-2.0-flash", contents=["List item"], posthog_distinct_id="test-id"
|
||||
model="gemini-2.0-flash", contents=["List item"], insights_distinct_id="test-id"
|
||||
)
|
||||
call_args = mock_client.capture.call_args[1]
|
||||
props = call_args["properties"]
|
||||
@@ -477,12 +477,12 @@ def test_new_client_model_parameters(
|
||||
"""Test model parameters with new Client API"""
|
||||
mock_google_genai_client.models.generate_content.return_value = mock_gemini_response
|
||||
|
||||
client = Client(api_key="test-key", posthog_client=mock_client)
|
||||
client = Client(api_key="test-key", insights_client=mock_client)
|
||||
|
||||
client.models.generate_content(
|
||||
model="gemini-2.0-flash",
|
||||
contents=["Hello"],
|
||||
posthog_distinct_id="test-id",
|
||||
insights_distinct_id="test-id",
|
||||
temperature=0.7,
|
||||
max_tokens=100,
|
||||
)
|
||||
@@ -496,16 +496,16 @@ def test_new_client_model_parameters(
|
||||
def test_new_client_default_settings(
|
||||
mock_client, mock_google_genai_client, mock_gemini_response
|
||||
):
|
||||
"""Test client with default PostHog settings"""
|
||||
"""Test client with default Insights settings"""
|
||||
mock_google_genai_client.models.generate_content.return_value = mock_gemini_response
|
||||
|
||||
client = Client(
|
||||
api_key="test-key",
|
||||
posthog_client=mock_client,
|
||||
posthog_distinct_id="default_user",
|
||||
posthog_properties={"team": "ai"},
|
||||
posthog_privacy_mode=False,
|
||||
posthog_groups={"company": "acme_corp"},
|
||||
insights_client=mock_client,
|
||||
insights_distinct_id="default_user",
|
||||
insights_properties={"team": "ai"},
|
||||
insights_privacy_mode=False,
|
||||
insights_groups={"company": "acme_corp"},
|
||||
)
|
||||
|
||||
# Call without overriding defaults
|
||||
@@ -527,21 +527,21 @@ def test_new_client_override_defaults(
|
||||
|
||||
client = Client(
|
||||
api_key="test-key",
|
||||
posthog_client=mock_client,
|
||||
posthog_distinct_id="default_user",
|
||||
posthog_properties={"team": "ai"},
|
||||
posthog_privacy_mode=False,
|
||||
posthog_groups={"company": "acme_corp"},
|
||||
insights_client=mock_client,
|
||||
insights_distinct_id="default_user",
|
||||
insights_properties={"team": "ai"},
|
||||
insights_privacy_mode=False,
|
||||
insights_groups={"company": "acme_corp"},
|
||||
)
|
||||
|
||||
# Override defaults in call
|
||||
client.models.generate_content(
|
||||
model="gemini-2.0-flash",
|
||||
contents=["Hello"],
|
||||
posthog_distinct_id="specific_user",
|
||||
posthog_properties={"feature": "chat", "urgent": True},
|
||||
posthog_privacy_mode=True,
|
||||
posthog_groups={"organization": "special_org"},
|
||||
insights_distinct_id="specific_user",
|
||||
insights_properties={"feature": "chat", "urgent": True},
|
||||
insights_privacy_mode=True,
|
||||
insights_groups={"organization": "special_org"},
|
||||
)
|
||||
|
||||
call_args = mock_client.capture.call_args[1]
|
||||
@@ -577,7 +577,7 @@ def test_vertex_ai_parameters_passed_through(
|
||||
location="us-central1",
|
||||
debug_config=mock_debug_config,
|
||||
http_options=mock_http_options,
|
||||
posthog_client=mock_client,
|
||||
insights_client=mock_client,
|
||||
)
|
||||
|
||||
# Verify genai.Client was called with correct parameters
|
||||
@@ -597,7 +597,7 @@ def test_api_key_mode(mock_client, mock_google_genai_client):
|
||||
# Create client with just API key (traditional mode)
|
||||
Client(
|
||||
api_key="test-api-key",
|
||||
posthog_client=mock_client,
|
||||
insights_client=mock_client,
|
||||
)
|
||||
|
||||
# Verify genai.Client was called with only api_key
|
||||
@@ -618,7 +618,7 @@ def test_vertex_ai_mode_with_optional_api_key(
|
||||
api_key="test-api-key",
|
||||
credentials=mock_credentials,
|
||||
project="test-project",
|
||||
posthog_client=mock_client,
|
||||
insights_client=mock_client,
|
||||
)
|
||||
|
||||
# Verify genai.Client was called with both Vertex AI params and API key
|
||||
@@ -634,7 +634,7 @@ def test_tool_use_response(mock_client, mock_google_genai_client, mock_gemini_re
|
||||
"""Test that tools defined in config are captured in $ai_tools property"""
|
||||
mock_google_genai_client.models.generate_content.return_value = mock_gemini_response
|
||||
|
||||
client = Client(api_key="test-key", posthog_client=mock_client)
|
||||
client = Client(api_key="test-key", insights_client=mock_client)
|
||||
|
||||
# Create mock tools configuration
|
||||
mock_tool = MagicMock()
|
||||
@@ -664,8 +664,8 @@ def test_tool_use_response(mock_client, mock_google_genai_client, mock_gemini_re
|
||||
model="gemini-2.5-flash",
|
||||
contents=["hey"],
|
||||
config=mock_config,
|
||||
posthog_distinct_id="test-id",
|
||||
posthog_properties={"foo": "bar"},
|
||||
insights_distinct_id="test-id",
|
||||
insights_properties={"foo": "bar"},
|
||||
)
|
||||
|
||||
assert response == mock_gemini_response
|
||||
@@ -702,12 +702,12 @@ def test_function_calls_in_output_choices(
|
||||
mock_gemini_response_with_function_calls
|
||||
)
|
||||
|
||||
client = Client(api_key="test-key", posthog_client=mock_client)
|
||||
client = Client(api_key="test-key", insights_client=mock_client)
|
||||
|
||||
response = client.models.generate_content(
|
||||
model="gemini-2.5-flash",
|
||||
contents=["What's the weather in San Francisco?"],
|
||||
posthog_distinct_id="test-id",
|
||||
insights_distinct_id="test-id",
|
||||
)
|
||||
|
||||
assert response == mock_gemini_response_with_function_calls
|
||||
@@ -751,12 +751,12 @@ def test_function_calls_only_no_content(
|
||||
mock_gemini_response_function_calls_only
|
||||
)
|
||||
|
||||
client = Client(api_key="test-key", posthog_client=mock_client)
|
||||
client = Client(api_key="test-key", insights_client=mock_client)
|
||||
|
||||
response = client.models.generate_content(
|
||||
model="gemini-2.5-flash",
|
||||
contents=["Get weather for New York"],
|
||||
posthog_distinct_id="test-id",
|
||||
insights_distinct_id="test-id",
|
||||
)
|
||||
|
||||
assert response == mock_gemini_response_function_calls_only
|
||||
@@ -810,12 +810,12 @@ def test_cache_and_reasoning_tokens(mock_client, mock_google_genai_client):
|
||||
|
||||
mock_google_genai_client.models.generate_content.return_value = mock_response
|
||||
|
||||
client = Client(api_key="test-key", posthog_client=mock_client)
|
||||
client = Client(api_key="test-key", insights_client=mock_client)
|
||||
|
||||
response = client.models.generate_content(
|
||||
model="gemini-2.5-pro",
|
||||
contents="Test with cache",
|
||||
posthog_distinct_id="test-id",
|
||||
insights_distinct_id="test-id",
|
||||
)
|
||||
|
||||
assert response == mock_response
|
||||
@@ -869,19 +869,19 @@ def test_streaming_cache_and_reasoning_tokens(mock_client, mock_google_genai_cli
|
||||
mock_stream = iter([chunk1, chunk2])
|
||||
mock_google_genai_client.models.generate_content_stream.return_value = mock_stream
|
||||
|
||||
client = Client(api_key="test-key", posthog_client=mock_client)
|
||||
client = Client(api_key="test-key", insights_client=mock_client)
|
||||
|
||||
response = client.models.generate_content_stream(
|
||||
model="gemini-2.5-pro",
|
||||
contents="Test streaming with cache",
|
||||
posthog_distinct_id="test-id",
|
||||
insights_distinct_id="test-id",
|
||||
)
|
||||
|
||||
# Consume the stream
|
||||
result = list(response)
|
||||
assert len(result) == 2
|
||||
|
||||
# Check PostHog capture was called
|
||||
# Check Insights capture was called
|
||||
assert mock_client.capture.call_count == 1
|
||||
|
||||
call_args = mock_client.capture.call_args[1]
|
||||
@@ -946,11 +946,11 @@ def test_web_search_grounding(mock_client, mock_google_genai_client):
|
||||
# Mock the generate_content method
|
||||
mock_google_genai_client.models.generate_content.return_value = mock_response
|
||||
|
||||
client = Client(api_key="test-key", posthog_client=mock_client)
|
||||
client = Client(api_key="test-key", insights_client=mock_client)
|
||||
response = client.models.generate_content(
|
||||
model="gemini-2.5-flash",
|
||||
contents="What's the latest news?",
|
||||
posthog_distinct_id="test-id",
|
||||
insights_distinct_id="test-id",
|
||||
)
|
||||
|
||||
assert response == mock_response
|
||||
@@ -1015,12 +1015,12 @@ def test_streaming_with_web_search(mock_client, mock_google_genai_client):
|
||||
mock_streaming_response()
|
||||
)
|
||||
|
||||
client = Client(api_key="test-key", posthog_client=mock_client)
|
||||
client = Client(api_key="test-key", insights_client=mock_client)
|
||||
|
||||
response = client.models.generate_content_stream(
|
||||
model="gemini-2.5-flash",
|
||||
contents="What's the latest news?",
|
||||
posthog_distinct_id="test-id",
|
||||
insights_distinct_id="test-id",
|
||||
)
|
||||
|
||||
chunks = list(response)
|
||||
@@ -1080,12 +1080,12 @@ def test_empty_grounding_metadata_no_web_search(mock_client, mock_google_genai_c
|
||||
# Mock the generate_content method
|
||||
mock_google_genai_client.models.generate_content.return_value = mock_response
|
||||
|
||||
client = Client(api_key="test-key", posthog_client=mock_client)
|
||||
client = Client(api_key="test-key", insights_client=mock_client)
|
||||
|
||||
response = client.models.generate_content(
|
||||
model="gemini-2.5-flash",
|
||||
contents="Hello",
|
||||
posthog_distinct_id="test-id",
|
||||
insights_distinct_id="test-id",
|
||||
)
|
||||
|
||||
assert response == mock_response
|
||||
@@ -1143,12 +1143,12 @@ def test_empty_array_grounding_metadata_no_web_search(
|
||||
# Mock the generate_content method
|
||||
mock_google_genai_client.models.generate_content.return_value = mock_response
|
||||
|
||||
client = Client(api_key="test-key", posthog_client=mock_client)
|
||||
client = Client(api_key="test-key", insights_client=mock_client)
|
||||
|
||||
response = client.models.generate_content(
|
||||
model="gemini-2.5-flash",
|
||||
contents="What can you do?",
|
||||
posthog_distinct_id="test-id",
|
||||
insights_distinct_id="test-id",
|
||||
)
|
||||
|
||||
assert response == mock_response
|
||||
|
||||
@@ -121,13 +121,13 @@ async def test_async_client_basic_generation(
|
||||
return_value=mock_gemini_response
|
||||
)
|
||||
|
||||
client = AsyncClient(api_key="test-key", posthog_client=mock_client)
|
||||
client = AsyncClient(api_key="test-key", insights_client=mock_client)
|
||||
|
||||
response = await client.models.generate_content(
|
||||
model="gemini-2.0-flash",
|
||||
contents=["Tell me a fun fact about hedgehogs"],
|
||||
posthog_distinct_id="test-id",
|
||||
posthog_properties={"foo": "bar"},
|
||||
insights_distinct_id="test-id",
|
||||
insights_properties={"foo": "bar"},
|
||||
)
|
||||
|
||||
assert response == mock_gemini_response
|
||||
@@ -178,13 +178,13 @@ async def test_async_client_streaming_with_generate_content_stream(
|
||||
return_value=mock_streaming_response()
|
||||
)
|
||||
|
||||
client = AsyncClient(api_key="test-key", posthog_client=mock_client)
|
||||
client = AsyncClient(api_key="test-key", insights_client=mock_client)
|
||||
|
||||
response = await client.models.generate_content_stream(
|
||||
model="gemini-2.0-flash",
|
||||
contents=["Write a short story"],
|
||||
posthog_distinct_id="test-id",
|
||||
posthog_properties={"feature": "streaming"},
|
||||
insights_distinct_id="test-id",
|
||||
insights_properties={"feature": "streaming"},
|
||||
)
|
||||
|
||||
chunks = []
|
||||
@@ -239,7 +239,7 @@ async def test_async_client_streaming_with_tools(mock_client, mock_google_genai_
|
||||
return_value=mock_streaming_response()
|
||||
)
|
||||
|
||||
client = AsyncClient(api_key="test-key", posthog_client=mock_client)
|
||||
client = AsyncClient(api_key="test-key", insights_client=mock_client)
|
||||
|
||||
# Create mock tools configuration
|
||||
mock_tool = MagicMock()
|
||||
@@ -267,8 +267,8 @@ async def test_async_client_streaming_with_tools(mock_client, mock_google_genai_
|
||||
model="gemini-2.0-flash",
|
||||
contents=["What's the weather in SF?"],
|
||||
config=mock_config,
|
||||
posthog_distinct_id="test-id",
|
||||
posthog_properties={"feature": "streaming_with_tools"},
|
||||
insights_distinct_id="test-id",
|
||||
insights_properties={"feature": "streaming_with_tools"},
|
||||
)
|
||||
|
||||
chunks = []
|
||||
@@ -305,13 +305,13 @@ async def test_async_client_groups(
|
||||
return_value=mock_gemini_response
|
||||
)
|
||||
|
||||
client = AsyncClient(api_key="test-key", posthog_client=mock_client)
|
||||
client = AsyncClient(api_key="test-key", insights_client=mock_client)
|
||||
|
||||
await client.models.generate_content(
|
||||
model="gemini-2.0-flash",
|
||||
contents=["Hello"],
|
||||
posthog_distinct_id="test-id",
|
||||
posthog_groups={"company": "company_123"},
|
||||
insights_distinct_id="test-id",
|
||||
insights_groups={"company": "company_123"},
|
||||
)
|
||||
|
||||
call_args = mock_client.capture.call_args[1]
|
||||
@@ -326,13 +326,13 @@ async def test_async_client_privacy_mode_local(
|
||||
return_value=mock_gemini_response
|
||||
)
|
||||
|
||||
client = AsyncClient(api_key="test-key", posthog_client=mock_client)
|
||||
client = AsyncClient(api_key="test-key", insights_client=mock_client)
|
||||
|
||||
await client.models.generate_content(
|
||||
model="gemini-2.0-flash",
|
||||
contents=["Hello"],
|
||||
posthog_distinct_id="test-id",
|
||||
posthog_privacy_mode=True,
|
||||
insights_distinct_id="test-id",
|
||||
insights_privacy_mode=True,
|
||||
)
|
||||
|
||||
call_args = mock_client.capture.call_args[1]
|
||||
@@ -351,12 +351,12 @@ async def test_async_client_privacy_mode_global(
|
||||
return_value=mock_gemini_response
|
||||
)
|
||||
|
||||
client = AsyncClient(api_key="test-key", posthog_client=mock_client)
|
||||
client = AsyncClient(api_key="test-key", insights_client=mock_client)
|
||||
|
||||
await client.models.generate_content(
|
||||
model="gemini-2.0-flash",
|
||||
contents=["Hello"],
|
||||
posthog_distinct_id="test-id",
|
||||
insights_distinct_id="test-id",
|
||||
)
|
||||
|
||||
call_args = mock_client.capture.call_args[1]
|
||||
@@ -373,11 +373,11 @@ async def test_async_client_different_input_formats(
|
||||
return_value=mock_gemini_response
|
||||
)
|
||||
|
||||
client = AsyncClient(api_key="test-key", posthog_client=mock_client)
|
||||
client = AsyncClient(api_key="test-key", insights_client=mock_client)
|
||||
|
||||
# Test string input
|
||||
await client.models.generate_content(
|
||||
model="gemini-2.0-flash", contents="Hello", posthog_distinct_id="test-id"
|
||||
model="gemini-2.0-flash", contents="Hello", insights_distinct_id="test-id"
|
||||
)
|
||||
call_args = mock_client.capture.call_args[1]
|
||||
props = call_args["properties"]
|
||||
@@ -388,7 +388,7 @@ async def test_async_client_different_input_formats(
|
||||
await client.models.generate_content(
|
||||
model="gemini-2.0-flash",
|
||||
contents=[{"role": "user", "parts": [{"text": "hey"}]}],
|
||||
posthog_distinct_id="test-id",
|
||||
insights_distinct_id="test-id",
|
||||
)
|
||||
call_args = mock_client.capture.call_args[1]
|
||||
props = call_args["properties"]
|
||||
@@ -401,7 +401,7 @@ async def test_async_client_different_input_formats(
|
||||
await client.models.generate_content(
|
||||
model="gemini-2.0-flash",
|
||||
contents=[{"role": "user", "parts": [{"text": "Hello "}, {"text": "world"}]}],
|
||||
posthog_distinct_id="test-id",
|
||||
insights_distinct_id="test-id",
|
||||
)
|
||||
call_args = mock_client.capture.call_args[1]
|
||||
props = call_args["properties"]
|
||||
@@ -418,7 +418,7 @@ async def test_async_client_different_input_formats(
|
||||
# Test list input with string
|
||||
mock_client.capture.reset_mock()
|
||||
await client.models.generate_content(
|
||||
model="gemini-2.0-flash", contents=["List item"], posthog_distinct_id="test-id"
|
||||
model="gemini-2.0-flash", contents=["List item"], insights_distinct_id="test-id"
|
||||
)
|
||||
call_args = mock_client.capture.call_args[1]
|
||||
props = call_args["properties"]
|
||||
@@ -433,12 +433,12 @@ async def test_async_client_model_parameters(
|
||||
return_value=mock_gemini_response
|
||||
)
|
||||
|
||||
client = AsyncClient(api_key="test-key", posthog_client=mock_client)
|
||||
client = AsyncClient(api_key="test-key", insights_client=mock_client)
|
||||
|
||||
await client.models.generate_content(
|
||||
model="gemini-2.0-flash",
|
||||
contents=["Hello"],
|
||||
posthog_distinct_id="test-id",
|
||||
insights_distinct_id="test-id",
|
||||
temperature=0.7,
|
||||
max_tokens=100,
|
||||
)
|
||||
@@ -452,18 +452,18 @@ async def test_async_client_model_parameters(
|
||||
async def test_async_client_default_settings(
|
||||
mock_client, mock_google_genai_client, mock_gemini_response
|
||||
):
|
||||
"""Test async client with default PostHog settings"""
|
||||
"""Test async client with default Insights settings"""
|
||||
mock_google_genai_client.aio.models.generate_content = AsyncMock(
|
||||
return_value=mock_gemini_response
|
||||
)
|
||||
|
||||
client = AsyncClient(
|
||||
api_key="test-key",
|
||||
posthog_client=mock_client,
|
||||
posthog_distinct_id="default_user",
|
||||
posthog_properties={"team": "ai"},
|
||||
posthog_privacy_mode=False,
|
||||
posthog_groups={"company": "acme_corp"},
|
||||
insights_client=mock_client,
|
||||
insights_distinct_id="default_user",
|
||||
insights_properties={"team": "ai"},
|
||||
insights_privacy_mode=False,
|
||||
insights_groups={"company": "acme_corp"},
|
||||
)
|
||||
|
||||
# Call without overriding defaults
|
||||
@@ -487,21 +487,21 @@ async def test_async_client_override_defaults(
|
||||
|
||||
client = AsyncClient(
|
||||
api_key="test-key",
|
||||
posthog_client=mock_client,
|
||||
posthog_distinct_id="default_user",
|
||||
posthog_properties={"team": "ai"},
|
||||
posthog_privacy_mode=False,
|
||||
posthog_groups={"company": "acme_corp"},
|
||||
insights_client=mock_client,
|
||||
insights_distinct_id="default_user",
|
||||
insights_properties={"team": "ai"},
|
||||
insights_privacy_mode=False,
|
||||
insights_groups={"company": "acme_corp"},
|
||||
)
|
||||
|
||||
# Override defaults in call
|
||||
await client.models.generate_content(
|
||||
model="gemini-2.0-flash",
|
||||
contents=["Hello"],
|
||||
posthog_distinct_id="specific_user",
|
||||
posthog_properties={"feature": "chat", "urgent": True},
|
||||
posthog_privacy_mode=True,
|
||||
posthog_groups={"organization": "special_org"},
|
||||
insights_distinct_id="specific_user",
|
||||
insights_properties={"feature": "chat", "urgent": True},
|
||||
insights_privacy_mode=True,
|
||||
insights_groups={"organization": "special_org"},
|
||||
)
|
||||
|
||||
call_args = mock_client.capture.call_args[1]
|
||||
@@ -539,7 +539,7 @@ async def test_async_vertex_ai_parameters_passed_through(
|
||||
location="us-central1",
|
||||
debug_config=mock_debug_config,
|
||||
http_options=mock_http_options,
|
||||
posthog_client=mock_client,
|
||||
insights_client=mock_client,
|
||||
)
|
||||
|
||||
# Verify genai.Client was called with correct parameters
|
||||
@@ -559,7 +559,7 @@ async def test_async_api_key_mode(mock_client, mock_google_genai_client):
|
||||
# Create async client with just API key (traditional mode)
|
||||
AsyncClient(
|
||||
api_key="test-api-key",
|
||||
posthog_client=mock_client,
|
||||
insights_client=mock_client,
|
||||
)
|
||||
|
||||
# Verify genai.Client was called with only api_key
|
||||
@@ -574,12 +574,12 @@ async def test_async_function_calls_in_output_choices(
|
||||
return_value=mock_gemini_response_with_function_calls
|
||||
)
|
||||
|
||||
client = AsyncClient(api_key="test-key", posthog_client=mock_client)
|
||||
client = AsyncClient(api_key="test-key", insights_client=mock_client)
|
||||
|
||||
response = await client.models.generate_content(
|
||||
model="gemini-2.5-flash",
|
||||
contents=["What's the weather in San Francisco?"],
|
||||
posthog_distinct_id="test-id",
|
||||
insights_distinct_id="test-id",
|
||||
)
|
||||
|
||||
assert response == mock_gemini_response_with_function_calls
|
||||
@@ -637,12 +637,12 @@ async def test_async_cache_and_reasoning_tokens(mock_client, mock_google_genai_c
|
||||
return_value=mock_response
|
||||
)
|
||||
|
||||
client = AsyncClient(api_key="test-key", posthog_client=mock_client)
|
||||
client = AsyncClient(api_key="test-key", insights_client=mock_client)
|
||||
|
||||
response = await client.models.generate_content(
|
||||
model="gemini-2.5-pro",
|
||||
contents="Test with cache",
|
||||
posthog_distinct_id="test-id",
|
||||
insights_distinct_id="test-id",
|
||||
)
|
||||
|
||||
assert response == mock_response
|
||||
@@ -689,12 +689,12 @@ async def test_async_streaming_cache_and_reasoning_tokens(
|
||||
return_value=mock_streaming_response()
|
||||
)
|
||||
|
||||
client = AsyncClient(api_key="test-key", posthog_client=mock_client)
|
||||
client = AsyncClient(api_key="test-key", insights_client=mock_client)
|
||||
|
||||
response = await client.models.generate_content_stream(
|
||||
model="gemini-2.5-pro",
|
||||
contents="Test streaming with cache",
|
||||
posthog_distinct_id="test-id",
|
||||
insights_distinct_id="test-id",
|
||||
)
|
||||
|
||||
# Consume the stream
|
||||
@@ -704,7 +704,7 @@ async def test_async_streaming_cache_and_reasoning_tokens(
|
||||
|
||||
assert len(result) == 2
|
||||
|
||||
# Check PostHog capture was called
|
||||
# Check Insights capture was called
|
||||
assert mock_client.capture.call_count == 1
|
||||
|
||||
call_args = mock_client.capture.call_args[1]
|
||||
@@ -761,11 +761,11 @@ async def test_async_web_search_grounding(mock_client, mock_google_genai_client)
|
||||
return_value=mock_response
|
||||
)
|
||||
|
||||
client = AsyncClient(api_key="test-key", posthog_client=mock_client)
|
||||
client = AsyncClient(api_key="test-key", insights_client=mock_client)
|
||||
response = await client.models.generate_content(
|
||||
model="gemini-2.5-flash",
|
||||
contents="What's the latest news?",
|
||||
posthog_distinct_id="test-id",
|
||||
insights_distinct_id="test-id",
|
||||
)
|
||||
|
||||
assert response == mock_response
|
||||
@@ -829,12 +829,12 @@ async def test_async_streaming_with_web_search(mock_client, mock_google_genai_cl
|
||||
return_value=mock_streaming_response()
|
||||
)
|
||||
|
||||
client = AsyncClient(api_key="test-key", posthog_client=mock_client)
|
||||
client = AsyncClient(api_key="test-key", insights_client=mock_client)
|
||||
|
||||
response = await client.models.generate_content_stream(
|
||||
model="gemini-2.5-flash",
|
||||
contents="What's the latest news?",
|
||||
posthog_distinct_id="test-id",
|
||||
insights_distinct_id="test-id",
|
||||
)
|
||||
|
||||
chunks = []
|
||||
|
||||
@@ -113,7 +113,7 @@ def test_metadata_capture(mock_client):
|
||||
base_url="https://us.posthog.com",
|
||||
name="test",
|
||||
end_time=None,
|
||||
posthog_properties=None,
|
||||
insights_properties=None,
|
||||
)
|
||||
assert callbacks._runs[run_id] == expected
|
||||
with patch("time.time", return_value=1234567891):
|
||||
@@ -1049,7 +1049,7 @@ def test_base_url_retrieval(mock_client):
|
||||
prompt = ChatPromptTemplate.from_messages([("user", "Foo")])
|
||||
chain = prompt | ChatOpenAI(
|
||||
api_key="test",
|
||||
model="posthog-mini",
|
||||
model="insights-mini",
|
||||
base_url="https://test.posthog.com",
|
||||
)
|
||||
callbacks = CallbackHandler(mock_client)
|
||||
@@ -1270,7 +1270,7 @@ def test_metadata_tools(mock_client):
|
||||
name="test",
|
||||
tools=tools,
|
||||
end_time=None,
|
||||
posthog_properties=None,
|
||||
insights_properties=None,
|
||||
)
|
||||
assert callbacks._runs[run_id] == expected
|
||||
with patch("time.time", return_value=1234567891):
|
||||
@@ -1867,7 +1867,7 @@ def test_openai_reasoning_tokens_o4_mini(mock_client):
|
||||
|
||||
|
||||
def test_callback_handler_without_client():
|
||||
"""Test that CallbackHandler works properly when no PostHog client is passed."""
|
||||
"""Test that CallbackHandler works properly when no Insights client is passed."""
|
||||
with patch("hanzo_insights.ai.langchain.callbacks.setup") as mock_setup:
|
||||
mock_client = mock_setup.return_value
|
||||
|
||||
@@ -1984,7 +1984,7 @@ def test_tool_definition(mock_client):
|
||||
assert run == expected
|
||||
assert callbacks._runs == {}
|
||||
|
||||
# Now test that the tools are properly captured in the PostHog event
|
||||
# Now test that the tools are properly captured in the Insights event
|
||||
mock_response = MagicMock()
|
||||
mock_response.generations = [[MagicMock()]]
|
||||
|
||||
@@ -2266,8 +2266,8 @@ def test_agent_action_and_finish_imports():
|
||||
assert call_args["event"] == "$ai_span"
|
||||
|
||||
|
||||
def test_posthog_properties_field_in_generation_metadata(mock_client):
|
||||
"""Test that posthog_properties is properly stored in GenerationMetadata."""
|
||||
def test_insights_properties_field_in_generation_metadata(mock_client):
|
||||
"""Test that insights_properties is properly stored in GenerationMetadata."""
|
||||
callbacks = CallbackHandler(mock_client)
|
||||
run_id = uuid.uuid4()
|
||||
|
||||
@@ -2281,7 +2281,7 @@ def test_posthog_properties_field_in_generation_metadata(mock_client):
|
||||
metadata={
|
||||
"ls_model_name": "gpt-4o",
|
||||
"ls_provider": "openai",
|
||||
"posthog_properties": {"$ai_billable": True},
|
||||
"insights_properties": {"$ai_billable": True},
|
||||
},
|
||||
name="test",
|
||||
)
|
||||
@@ -2294,11 +2294,11 @@ def test_posthog_properties_field_in_generation_metadata(mock_client):
|
||||
provider="openai",
|
||||
base_url="https://api.openai.com",
|
||||
name="test",
|
||||
posthog_properties={"$ai_billable": True},
|
||||
insights_properties={"$ai_billable": True},
|
||||
end_time=None,
|
||||
)
|
||||
assert callbacks._runs[run_id] == expected
|
||||
assert callbacks._runs[run_id].posthog_properties == {"$ai_billable": True}
|
||||
assert callbacks._runs[run_id].insights_properties == {"$ai_billable": True}
|
||||
|
||||
callbacks._pop_run_metadata(run_id)
|
||||
|
||||
@@ -2313,15 +2313,15 @@ def test_posthog_properties_field_in_generation_metadata(mock_client):
|
||||
metadata={
|
||||
"ls_model_name": "gpt-4o",
|
||||
"ls_provider": "openai",
|
||||
"posthog_properties": {"$ai_billable": False},
|
||||
"insights_properties": {"$ai_billable": False},
|
||||
},
|
||||
name="test",
|
||||
)
|
||||
|
||||
assert callbacks._runs[run_id2].posthog_properties == {"$ai_billable": False}
|
||||
assert callbacks._runs[run_id2].insights_properties == {"$ai_billable": False}
|
||||
callbacks._pop_run_metadata(run_id2)
|
||||
|
||||
# Test when posthog_properties not provided
|
||||
# Test when insights_properties not provided
|
||||
run_id3 = uuid.uuid4()
|
||||
with patch("time.time", return_value=1234567890):
|
||||
callbacks._set_llm_metadata(
|
||||
@@ -2333,7 +2333,7 @@ def test_posthog_properties_field_in_generation_metadata(mock_client):
|
||||
name="test",
|
||||
)
|
||||
|
||||
assert callbacks._runs[run_id3].posthog_properties is None
|
||||
assert callbacks._runs[run_id3].insights_properties is None
|
||||
|
||||
|
||||
def test_billable_property_in_generation_event(mock_client):
|
||||
@@ -2349,7 +2349,7 @@ def test_billable_property_in_generation_event(mock_client):
|
||||
run_id,
|
||||
messages=[{"role": "user", "content": "Test"}],
|
||||
metadata={
|
||||
"posthog_properties": {"$ai_billable": True},
|
||||
"insights_properties": {"$ai_billable": True},
|
||||
"ls_model_name": "test-model",
|
||||
},
|
||||
invocation_params={},
|
||||
@@ -2412,12 +2412,12 @@ def test_billable_with_real_chain(mock_client):
|
||||
metadata={
|
||||
"ls_model_name": "fake-model",
|
||||
"ls_provider": "fake",
|
||||
"posthog_properties": {"$ai_billable": True},
|
||||
"insights_properties": {"$ai_billable": True},
|
||||
},
|
||||
invocation_params={"temperature": 0.7},
|
||||
)
|
||||
|
||||
assert callbacks._runs[run_id].posthog_properties == {"$ai_billable": True}
|
||||
assert callbacks._runs[run_id].insights_properties == {"$ai_billable": True}
|
||||
|
||||
mock_response = MagicMock()
|
||||
mock_response.generations = [[MagicMock()]]
|
||||
|
||||
@@ -467,12 +467,12 @@ def test_basic_completion(mock_client, mock_openai_response):
|
||||
"openai.resources.chat.completions.Completions.create",
|
||||
return_value=mock_openai_response,
|
||||
):
|
||||
client = OpenAI(api_key="test-key", posthog_client=mock_client)
|
||||
client = OpenAI(api_key="test-key", insights_client=mock_client)
|
||||
response = client.chat.completions.create(
|
||||
model="gpt-4",
|
||||
messages=[{"role": "user", "content": "Hello"}],
|
||||
posthog_distinct_id="test-id",
|
||||
posthog_properties={"foo": "bar"},
|
||||
insights_distinct_id="test-id",
|
||||
insights_properties={"foo": "bar"},
|
||||
)
|
||||
|
||||
assert response == mock_openai_response
|
||||
@@ -513,12 +513,12 @@ def test_embeddings(mock_client, mock_embedding_response):
|
||||
"openai.resources.embeddings.Embeddings.create",
|
||||
return_value=mock_embedding_response,
|
||||
):
|
||||
client = OpenAI(api_key="test-key", posthog_client=mock_client)
|
||||
client = OpenAI(api_key="test-key", insights_client=mock_client)
|
||||
response = client.embeddings.create(
|
||||
model="text-embedding-3-small",
|
||||
input="Hello world",
|
||||
posthog_distinct_id="test-id",
|
||||
posthog_properties={"foo": "bar"},
|
||||
insights_distinct_id="test-id",
|
||||
insights_properties={"foo": "bar"},
|
||||
)
|
||||
|
||||
assert response == mock_embedding_response
|
||||
@@ -543,12 +543,12 @@ def test_groups(mock_client, mock_openai_response):
|
||||
"openai.resources.chat.completions.Completions.create",
|
||||
return_value=mock_openai_response,
|
||||
):
|
||||
client = OpenAI(api_key="test-key", posthog_client=mock_client)
|
||||
client = OpenAI(api_key="test-key", insights_client=mock_client)
|
||||
response = client.chat.completions.create(
|
||||
model="gpt-4",
|
||||
messages=[{"role": "user", "content": "Hello"}],
|
||||
posthog_distinct_id="test-id",
|
||||
posthog_groups={"company": "test_company"},
|
||||
insights_distinct_id="test-id",
|
||||
insights_groups={"company": "test_company"},
|
||||
)
|
||||
|
||||
assert response == mock_openai_response
|
||||
@@ -564,12 +564,12 @@ def test_privacy_mode_local(mock_client, mock_openai_response):
|
||||
"openai.resources.chat.completions.Completions.create",
|
||||
return_value=mock_openai_response,
|
||||
):
|
||||
client = OpenAI(api_key="test-key", posthog_client=mock_client)
|
||||
client = OpenAI(api_key="test-key", insights_client=mock_client)
|
||||
response = client.chat.completions.create(
|
||||
model="gpt-4",
|
||||
messages=[{"role": "user", "content": "Hello"}],
|
||||
posthog_distinct_id="test-id",
|
||||
posthog_privacy_mode=True,
|
||||
insights_distinct_id="test-id",
|
||||
insights_privacy_mode=True,
|
||||
)
|
||||
|
||||
assert response == mock_openai_response
|
||||
@@ -587,12 +587,12 @@ def test_privacy_mode_global(mock_client, mock_openai_response):
|
||||
return_value=mock_openai_response,
|
||||
):
|
||||
mock_client.privacy_mode = True
|
||||
client = OpenAI(api_key="test-key", posthog_client=mock_client)
|
||||
client = OpenAI(api_key="test-key", insights_client=mock_client)
|
||||
response = client.chat.completions.create(
|
||||
model="gpt-4",
|
||||
messages=[{"role": "user", "content": "Hello"}],
|
||||
posthog_distinct_id="test-id",
|
||||
posthog_privacy_mode=False,
|
||||
insights_distinct_id="test-id",
|
||||
insights_privacy_mode=False,
|
||||
)
|
||||
|
||||
assert response == mock_openai_response
|
||||
@@ -609,7 +609,7 @@ def test_error(mock_client, mock_openai_response):
|
||||
"openai.resources.chat.completions.Completions.create",
|
||||
side_effect=Exception("Test error"),
|
||||
):
|
||||
client = OpenAI(api_key="test-key", posthog_client=mock_client)
|
||||
client = OpenAI(api_key="test-key", insights_client=mock_client)
|
||||
with pytest.raises(Exception):
|
||||
client.chat.completions.create(
|
||||
model="gpt-4", messages=[{"role": "user", "content": "Hello"}]
|
||||
@@ -628,12 +628,12 @@ def test_cached_tokens(mock_client, mock_openai_response_with_cached_tokens):
|
||||
"openai.resources.chat.completions.Completions.create",
|
||||
return_value=mock_openai_response_with_cached_tokens,
|
||||
):
|
||||
client = OpenAI(api_key="test-key", posthog_client=mock_client)
|
||||
client = OpenAI(api_key="test-key", insights_client=mock_client)
|
||||
response = client.chat.completions.create(
|
||||
model="gpt-4",
|
||||
messages=[{"role": "user", "content": "Hello"}],
|
||||
posthog_distinct_id="test-id",
|
||||
posthog_properties={"foo": "bar"},
|
||||
insights_distinct_id="test-id",
|
||||
insights_properties={"foo": "bar"},
|
||||
)
|
||||
|
||||
assert response == mock_openai_response_with_cached_tokens
|
||||
@@ -666,7 +666,7 @@ def test_tool_calls(mock_client, mock_openai_response_with_tool_calls):
|
||||
"openai.resources.chat.completions.Completions.create",
|
||||
return_value=mock_openai_response_with_tool_calls,
|
||||
):
|
||||
client = OpenAI(api_key="test-key", posthog_client=mock_client)
|
||||
client = OpenAI(api_key="test-key", insights_client=mock_client)
|
||||
response = client.chat.completions.create(
|
||||
model="gpt-4",
|
||||
messages=[
|
||||
@@ -682,7 +682,7 @@ def test_tool_calls(mock_client, mock_openai_response_with_tool_calls):
|
||||
},
|
||||
}
|
||||
],
|
||||
posthog_distinct_id="test-id",
|
||||
insights_distinct_id="test-id",
|
||||
)
|
||||
|
||||
assert response == mock_openai_response_with_tool_calls
|
||||
@@ -739,7 +739,7 @@ def test_tool_calls_only_no_content(mock_client, mock_openai_response_tool_calls
|
||||
"openai.resources.chat.completions.Completions.create",
|
||||
return_value=mock_openai_response_tool_calls_only,
|
||||
):
|
||||
client = OpenAI(api_key="test-key", posthog_client=mock_client)
|
||||
client = OpenAI(api_key="test-key", insights_client=mock_client)
|
||||
response = client.chat.completions.create(
|
||||
model="gpt-4",
|
||||
messages=[{"role": "user", "content": "Get weather for New York"}],
|
||||
@@ -753,7 +753,7 @@ def test_tool_calls_only_no_content(mock_client, mock_openai_response_tool_calls
|
||||
},
|
||||
}
|
||||
],
|
||||
posthog_distinct_id="test-id",
|
||||
insights_distinct_id="test-id",
|
||||
)
|
||||
|
||||
assert response == mock_openai_response_tool_calls_only
|
||||
@@ -793,7 +793,7 @@ def test_responses_api_tool_calls(mock_client, mock_responses_api_with_tool_call
|
||||
"openai.resources.responses.Responses.create",
|
||||
return_value=mock_responses_api_with_tool_calls,
|
||||
):
|
||||
client = OpenAI(api_key="test-key", posthog_client=mock_client)
|
||||
client = OpenAI(api_key="test-key", insights_client=mock_client)
|
||||
response = client.responses.create(
|
||||
model="gpt-4o-mini",
|
||||
input=[{"role": "user", "content": "What's the weather in Chicago?"}],
|
||||
@@ -808,7 +808,7 @@ def test_responses_api_tool_calls(mock_client, mock_responses_api_with_tool_call
|
||||
},
|
||||
}
|
||||
],
|
||||
posthog_distinct_id="test-id",
|
||||
insights_distinct_id="test-id",
|
||||
)
|
||||
|
||||
assert response == mock_responses_api_with_tool_calls
|
||||
@@ -851,7 +851,7 @@ def test_streaming_with_tool_calls(mock_client, streaming_tool_call_chunks):
|
||||
# Set up the mock to return our chunks when iterated
|
||||
mock_create.return_value = streaming_tool_call_chunks
|
||||
|
||||
client = OpenAI(api_key="test-key", posthog_client=mock_client)
|
||||
client = OpenAI(api_key="test-key", insights_client=mock_client)
|
||||
|
||||
# Call the streaming method
|
||||
response_generator = client.chat.completions.create(
|
||||
@@ -870,7 +870,7 @@ def test_streaming_with_tool_calls(mock_client, streaming_tool_call_chunks):
|
||||
}
|
||||
],
|
||||
stream=True,
|
||||
posthog_distinct_id="test-id",
|
||||
insights_distinct_id="test-id",
|
||||
)
|
||||
|
||||
# Consume the generator to trigger the event capture
|
||||
@@ -949,12 +949,12 @@ def test_responses_api(mock_client, mock_openai_response_with_responses_api):
|
||||
"openai.resources.responses.Responses.create",
|
||||
return_value=mock_openai_response_with_responses_api,
|
||||
):
|
||||
client = OpenAI(api_key="test-key", posthog_client=mock_client)
|
||||
client = OpenAI(api_key="test-key", insights_client=mock_client)
|
||||
response = client.responses.create(
|
||||
model="gpt-4o-mini",
|
||||
input="Hello",
|
||||
posthog_distinct_id="test-id",
|
||||
posthog_properties={"foo": "bar"},
|
||||
insights_distinct_id="test-id",
|
||||
insights_properties={"foo": "bar"},
|
||||
)
|
||||
assert response == mock_openai_response_with_responses_api
|
||||
assert mock_client.capture.call_count == 1
|
||||
@@ -986,7 +986,7 @@ def test_responses_parse(mock_client, mock_parsed_response):
|
||||
"openai.resources.responses.Responses.parse",
|
||||
return_value=mock_parsed_response,
|
||||
):
|
||||
client = OpenAI(api_key="test-key", posthog_client=mock_client)
|
||||
client = OpenAI(api_key="test-key", insights_client=mock_client)
|
||||
response = client.responses.parse(
|
||||
model="gpt-4o-2024-08-06",
|
||||
input=[
|
||||
@@ -1016,8 +1016,8 @@ def test_responses_parse(mock_client, mock_parsed_response):
|
||||
},
|
||||
}
|
||||
},
|
||||
posthog_distinct_id="test-id",
|
||||
posthog_properties={"foo": "bar"},
|
||||
insights_distinct_id="test-id",
|
||||
insights_properties={"foo": "bar"},
|
||||
)
|
||||
|
||||
assert response == mock_parsed_response
|
||||
@@ -1101,15 +1101,15 @@ def test_responses_api_streaming_with_tokens(mock_client):
|
||||
"openai.resources.responses.Responses.create",
|
||||
side_effect=mock_streaming_response,
|
||||
):
|
||||
client = OpenAI(api_key="test-key", posthog_client=mock_client)
|
||||
client = OpenAI(api_key="test-key", insights_client=mock_client)
|
||||
|
||||
# Consume the streaming response
|
||||
response = client.responses.create(
|
||||
model="gpt-4o-mini",
|
||||
input=[{"role": "user", "content": "Test message"}],
|
||||
stream=True,
|
||||
posthog_distinct_id="test-id",
|
||||
posthog_properties={"test": "streaming"},
|
||||
insights_distinct_id="test-id",
|
||||
insights_properties={"test": "streaming"},
|
||||
)
|
||||
|
||||
# Consume all chunks
|
||||
@@ -1153,7 +1153,7 @@ async def test_async_chat_streaming_with_tool_calls(
|
||||
with patch(
|
||||
"openai.resources.chat.completions.AsyncCompletions.create", new=mock_create
|
||||
):
|
||||
client = AsyncOpenAI(api_key="test-key", posthog_client=mock_client)
|
||||
client = AsyncOpenAI(api_key="test-key", insights_client=mock_client)
|
||||
|
||||
response_stream = await client.chat.completions.create(
|
||||
model="gpt-4",
|
||||
@@ -1171,7 +1171,7 @@ async def test_async_chat_streaming_with_tool_calls(
|
||||
}
|
||||
],
|
||||
stream=True,
|
||||
posthog_distinct_id="test-id",
|
||||
insights_distinct_id="test-id",
|
||||
)
|
||||
|
||||
chunks = []
|
||||
@@ -1239,14 +1239,14 @@ async def test_async_responses_streaming_with_tokens(mock_client):
|
||||
return chunk_iterable()
|
||||
|
||||
with patch("openai.resources.responses.AsyncResponses.create", new=mock_create):
|
||||
client = AsyncOpenAI(api_key="test-key", posthog_client=mock_client)
|
||||
client = AsyncOpenAI(api_key="test-key", insights_client=mock_client)
|
||||
|
||||
response_stream = await client.responses.create(
|
||||
model="gpt-4o-mini",
|
||||
input=[{"role": "user", "content": "Test message"}],
|
||||
stream=True,
|
||||
posthog_distinct_id="test-id",
|
||||
posthog_properties={"test": "streaming"},
|
||||
insights_distinct_id="test-id",
|
||||
insights_properties={"test": "streaming"},
|
||||
)
|
||||
|
||||
async for _ in response_stream:
|
||||
@@ -1274,13 +1274,13 @@ async def test_async_embeddings_create(mock_client, mock_embedding_response):
|
||||
mock_create = AsyncMock(return_value=mock_embedding_response)
|
||||
|
||||
with patch("openai.resources.embeddings.AsyncEmbeddings.create", new=mock_create):
|
||||
client = AsyncOpenAI(api_key="test-key", posthog_client=mock_client)
|
||||
client = AsyncOpenAI(api_key="test-key", insights_client=mock_client)
|
||||
|
||||
response = await client.embeddings.create(
|
||||
model="text-embedding-3-small",
|
||||
input="Hello world",
|
||||
posthog_distinct_id="test-id",
|
||||
posthog_properties={"foo": "bar"},
|
||||
insights_distinct_id="test-id",
|
||||
insights_properties={"foo": "bar"},
|
||||
)
|
||||
|
||||
assert response == mock_embedding_response
|
||||
@@ -1304,7 +1304,7 @@ def test_tool_definition(mock_client, mock_openai_response):
|
||||
"openai.resources.chat.completions.Completions.create",
|
||||
return_value=mock_openai_response,
|
||||
):
|
||||
client = OpenAI(api_key="test-key", posthog_client=mock_client)
|
||||
client = OpenAI(api_key="test-key", insights_client=mock_client)
|
||||
|
||||
# Define tools to be passed to the create function
|
||||
tools = [
|
||||
@@ -1331,8 +1331,8 @@ def test_tool_definition(mock_client, mock_openai_response):
|
||||
model="gpt-4o-mini",
|
||||
messages=[{"role": "user", "content": "hey"}],
|
||||
tools=tools,
|
||||
posthog_distinct_id="test-id",
|
||||
posthog_properties={"foo": "bar"},
|
||||
insights_distinct_id="test-id",
|
||||
insights_properties={"foo": "bar"},
|
||||
)
|
||||
|
||||
assert response == mock_openai_response
|
||||
@@ -1392,11 +1392,11 @@ def test_web_search_perplexity_style(mock_client):
|
||||
mock_response = MockResponseWithAnnotations()
|
||||
|
||||
with patch("openai.resources.chat.Completions.create", return_value=mock_response):
|
||||
client = OpenAI(api_key="test-key", posthog_client=mock_client)
|
||||
client = OpenAI(api_key="test-key", insights_client=mock_client)
|
||||
response = client.chat.completions.create(
|
||||
model="gpt-4-turbo",
|
||||
messages=[{"role": "user", "content": "What's happening in tech?"}],
|
||||
posthog_distinct_id="test-id",
|
||||
insights_distinct_id="test-id",
|
||||
)
|
||||
|
||||
assert response == mock_response
|
||||
@@ -1445,13 +1445,13 @@ def test_web_search_responses_api(mock_client):
|
||||
return mock_response
|
||||
|
||||
result = call_llm_and_track_usage(
|
||||
posthog_distinct_id="test-id",
|
||||
insights_distinct_id="test-id",
|
||||
ph_client=mock_client,
|
||||
provider="openai",
|
||||
posthog_trace_id=None,
|
||||
posthog_properties=None,
|
||||
posthog_privacy_mode=False,
|
||||
posthog_groups=None,
|
||||
insights_trace_id=None,
|
||||
insights_properties=None,
|
||||
insights_privacy_mode=False,
|
||||
insights_groups=None,
|
||||
base_url="https://api.openai.com/v1",
|
||||
call_method=mock_create_call,
|
||||
model="gpt-4o",
|
||||
@@ -1533,12 +1533,12 @@ def test_streaming_with_web_search(mock_client, streaming_web_search_chunks):
|
||||
with patch("openai.resources.chat.completions.Completions.create") as mock_create:
|
||||
mock_create.return_value = streaming_web_search_chunks
|
||||
|
||||
client = OpenAI(api_key="test-key", posthog_client=mock_client)
|
||||
client = OpenAI(api_key="test-key", insights_client=mock_client)
|
||||
response_generator = client.chat.completions.create(
|
||||
model="gpt-4",
|
||||
messages=[{"role": "user", "content": "Search for recent news"}],
|
||||
stream=True,
|
||||
posthog_distinct_id="test-id",
|
||||
insights_distinct_id="test-id",
|
||||
)
|
||||
|
||||
# Consume the generator to trigger the event capture
|
||||
@@ -1569,12 +1569,12 @@ def test_streaming_with_web_search_on_non_usage_chunk(
|
||||
with patch("openai.resources.chat.completions.Completions.create") as mock_create:
|
||||
mock_create.return_value = streaming_web_search_chunks
|
||||
|
||||
client = OpenAI(api_key="test-key", posthog_client=mock_client)
|
||||
client = OpenAI(api_key="test-key", insights_client=mock_client)
|
||||
response_generator = client.chat.completions.create(
|
||||
model="gpt-4",
|
||||
messages=[{"role": "user", "content": "Search for recent news"}],
|
||||
stream=True,
|
||||
posthog_distinct_id="test-id",
|
||||
insights_distinct_id="test-id",
|
||||
)
|
||||
|
||||
# Consume the generator to trigger the event capture
|
||||
@@ -1629,12 +1629,12 @@ async def test_async_chat_with_web_search(mock_client):
|
||||
with patch(
|
||||
"openai.resources.chat.completions.AsyncCompletions.create", new=mock_create
|
||||
):
|
||||
client = AsyncOpenAI(api_key="test-key", posthog_client=mock_client)
|
||||
client = AsyncOpenAI(api_key="test-key", insights_client=mock_client)
|
||||
|
||||
response = await client.chat.completions.create(
|
||||
model="gpt-4",
|
||||
messages=[{"role": "user", "content": "Search for recent news"}],
|
||||
posthog_distinct_id="test-id",
|
||||
insights_distinct_id="test-id",
|
||||
)
|
||||
|
||||
assert response == mock_response
|
||||
@@ -1672,13 +1672,13 @@ async def test_async_chat_streaming_with_web_search(
|
||||
with patch(
|
||||
"openai.resources.chat.completions.AsyncCompletions.create", new=mock_create
|
||||
):
|
||||
client = AsyncOpenAI(api_key="test-key", posthog_client=mock_client)
|
||||
client = AsyncOpenAI(api_key="test-key", insights_client=mock_client)
|
||||
|
||||
response_stream = await client.chat.completions.create(
|
||||
model="gpt-4",
|
||||
messages=[{"role": "user", "content": "Search for recent news"}],
|
||||
stream=True,
|
||||
posthog_distinct_id="test-id",
|
||||
insights_distinct_id="test-id",
|
||||
)
|
||||
|
||||
chunks = []
|
||||
@@ -1742,13 +1742,13 @@ def test_streaming_chat_extracts_model_from_chunk_when_not_in_kwargs(mock_client
|
||||
with patch("openai.resources.chat.completions.Completions.create") as mock_create:
|
||||
mock_create.return_value = chunks
|
||||
|
||||
client = OpenAI(api_key="test-key", posthog_client=mock_client)
|
||||
client = OpenAI(api_key="test-key", insights_client=mock_client)
|
||||
|
||||
# Note: NOT passing model in kwargs - simulates stored prompt usage
|
||||
response_generator = client.chat.completions.create(
|
||||
messages=[{"role": "user", "content": "Hello"}],
|
||||
stream=True,
|
||||
posthog_distinct_id="test-id",
|
||||
insights_distinct_id="test-id",
|
||||
)
|
||||
|
||||
# Consume the generator
|
||||
@@ -1788,13 +1788,13 @@ def test_streaming_chat_prefers_kwargs_model_over_chunk_model(mock_client):
|
||||
with patch("openai.resources.chat.completions.Completions.create") as mock_create:
|
||||
mock_create.return_value = chunks
|
||||
|
||||
client = OpenAI(api_key="test-key", posthog_client=mock_client)
|
||||
client = OpenAI(api_key="test-key", insights_client=mock_client)
|
||||
|
||||
response_generator = client.chat.completions.create(
|
||||
model="gpt-4o-from-kwargs", # Explicitly passed model
|
||||
messages=[{"role": "user", "content": "Hello"}],
|
||||
stream=True,
|
||||
posthog_distinct_id="test-id",
|
||||
insights_distinct_id="test-id",
|
||||
)
|
||||
|
||||
list(response_generator)
|
||||
@@ -1839,13 +1839,13 @@ def test_streaming_responses_api_extracts_model_from_response_object(mock_client
|
||||
with patch("openai.resources.responses.Responses.create") as mock_create:
|
||||
mock_create.return_value = iter(chunks)
|
||||
|
||||
client = OpenAI(api_key="test-key", posthog_client=mock_client)
|
||||
client = OpenAI(api_key="test-key", insights_client=mock_client)
|
||||
|
||||
# Note: NOT passing model - simulates stored prompt
|
||||
response_generator = client.responses.create(
|
||||
input=[{"role": "user", "content": "Hello"}],
|
||||
stream=True,
|
||||
posthog_distinct_id="test-id",
|
||||
insights_distinct_id="test-id",
|
||||
)
|
||||
|
||||
list(response_generator)
|
||||
@@ -1886,12 +1886,12 @@ def test_non_streaming_extracts_model_from_response(mock_client):
|
||||
"openai.resources.chat.completions.Completions.create",
|
||||
return_value=mock_response,
|
||||
):
|
||||
client = OpenAI(api_key="test-key", posthog_client=mock_client)
|
||||
client = OpenAI(api_key="test-key", insights_client=mock_client)
|
||||
|
||||
# Note: NOT passing model in kwargs
|
||||
response = client.chat.completions.create(
|
||||
messages=[{"role": "user", "content": "Hello"}],
|
||||
posthog_distinct_id="test-id",
|
||||
insights_distinct_id="test-id",
|
||||
)
|
||||
|
||||
assert response == mock_response
|
||||
@@ -1948,12 +1948,12 @@ def test_non_streaming_responses_api_extracts_model_from_response(mock_client):
|
||||
"openai.resources.responses.Responses.create",
|
||||
return_value=mock_response,
|
||||
):
|
||||
client = OpenAI(api_key="test-key", posthog_client=mock_client)
|
||||
client = OpenAI(api_key="test-key", insights_client=mock_client)
|
||||
|
||||
# Note: NOT passing model in kwargs
|
||||
response = client.responses.create(
|
||||
input="Hello",
|
||||
posthog_distinct_id="test-id",
|
||||
insights_distinct_id="test-id",
|
||||
)
|
||||
|
||||
assert response == mock_response
|
||||
@@ -1995,12 +1995,12 @@ def test_non_streaming_returns_none_when_no_model(mock_client):
|
||||
"openai.resources.chat.completions.Completions.create",
|
||||
return_value=mock_response,
|
||||
):
|
||||
client = OpenAI(api_key="test-key", posthog_client=mock_client)
|
||||
client = OpenAI(api_key="test-key", insights_client=mock_client)
|
||||
|
||||
# Note: NOT passing model in kwargs and response has no model
|
||||
client.chat.completions.create(
|
||||
messages=[{"role": "user", "content": "Hello"}],
|
||||
posthog_distinct_id="test-id",
|
||||
insights_distinct_id="test-id",
|
||||
)
|
||||
|
||||
call_args = mock_client.capture.call_args[1]
|
||||
@@ -2032,12 +2032,12 @@ def test_streaming_falls_back_to_unknown_when_no_model(mock_client):
|
||||
with patch("openai.resources.chat.completions.Completions.create") as mock_create:
|
||||
mock_create.return_value = [chunk]
|
||||
|
||||
client = OpenAI(api_key="test-key", posthog_client=mock_client)
|
||||
client = OpenAI(api_key="test-key", insights_client=mock_client)
|
||||
|
||||
response_generator = client.chat.completions.create(
|
||||
messages=[{"role": "user", "content": "Hello"}],
|
||||
stream=True,
|
||||
posthog_distinct_id="test-id",
|
||||
insights_distinct_id="test-id",
|
||||
)
|
||||
|
||||
list(response_generator)
|
||||
@@ -2083,13 +2083,13 @@ async def test_async_streaming_chat_extracts_model_from_chunk(mock_client):
|
||||
with patch(
|
||||
"openai.resources.chat.completions.AsyncCompletions.create", new=mock_create
|
||||
):
|
||||
client = AsyncOpenAI(api_key="test-key", posthog_client=mock_client)
|
||||
client = AsyncOpenAI(api_key="test-key", insights_client=mock_client)
|
||||
|
||||
# Note: NOT passing model
|
||||
response_stream = await client.chat.completions.create(
|
||||
messages=[{"role": "user", "content": "Hello"}],
|
||||
stream=True,
|
||||
posthog_distinct_id="test-id",
|
||||
insights_distinct_id="test-id",
|
||||
)
|
||||
|
||||
async for _ in response_stream:
|
||||
@@ -2137,12 +2137,12 @@ async def test_async_streaming_responses_extracts_model_from_response(mock_clien
|
||||
return chunk_iterable()
|
||||
|
||||
with patch("openai.resources.responses.AsyncResponses.create", new=mock_create):
|
||||
client = AsyncOpenAI(api_key="test-key", posthog_client=mock_client)
|
||||
client = AsyncOpenAI(api_key="test-key", insights_client=mock_client)
|
||||
|
||||
response_stream = await client.responses.create(
|
||||
input=[{"role": "user", "content": "Hello"}],
|
||||
stream=True,
|
||||
posthog_distinct_id="test-id",
|
||||
insights_distinct_id="test-id",
|
||||
)
|
||||
|
||||
async for _ in response_stream:
|
||||
|
||||
@@ -16,7 +16,7 @@ try:
|
||||
TranscriptionSpanData,
|
||||
)
|
||||
|
||||
from hanzo_insights.ai.openai_agents import PostHogTracingProcessor, instrument
|
||||
from hanzo_insights.ai.openai_agents import InsightsTracingProcessor, instrument
|
||||
|
||||
OPENAI_AGENTS_AVAILABLE = True
|
||||
except ImportError:
|
||||
@@ -39,7 +39,7 @@ def mock_client():
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def processor(mock_client):
|
||||
return PostHogTracingProcessor(
|
||||
return InsightsTracingProcessor(
|
||||
client=mock_client,
|
||||
distinct_id="test-user",
|
||||
privacy_mode=False,
|
||||
@@ -68,12 +68,12 @@ def mock_span():
|
||||
return span
|
||||
|
||||
|
||||
class TestPostHogTracingProcessor:
|
||||
"""Tests for the PostHogTracingProcessor class."""
|
||||
class TestInsightsTracingProcessor:
|
||||
"""Tests for the InsightsTracingProcessor class."""
|
||||
|
||||
def test_initialization(self, mock_client):
|
||||
"""Test processor initializes correctly."""
|
||||
processor = PostHogTracingProcessor(
|
||||
processor = InsightsTracingProcessor(
|
||||
client=mock_client,
|
||||
distinct_id="user@example.com",
|
||||
privacy_mode=True,
|
||||
@@ -93,7 +93,7 @@ class TestPostHogTracingProcessor:
|
||||
def resolver(trace):
|
||||
return trace.metadata.get("user_id", "default")
|
||||
|
||||
processor = PostHogTracingProcessor(
|
||||
processor = InsightsTracingProcessor(
|
||||
client=mock_client,
|
||||
distinct_id=resolver,
|
||||
)
|
||||
@@ -127,7 +127,7 @@ class TestPostHogTracingProcessor:
|
||||
|
||||
def test_personless_mode_when_no_distinct_id(self, mock_client, mock_trace):
|
||||
"""Test that trace events use personless mode when no distinct_id is provided."""
|
||||
processor = PostHogTracingProcessor(
|
||||
processor = InsightsTracingProcessor(
|
||||
client=mock_client,
|
||||
)
|
||||
|
||||
@@ -143,7 +143,7 @@ class TestPostHogTracingProcessor:
|
||||
self, mock_client, mock_trace, mock_span
|
||||
):
|
||||
"""Test that span events use personless mode when no distinct_id is provided."""
|
||||
processor = PostHogTracingProcessor(
|
||||
processor = InsightsTracingProcessor(
|
||||
client=mock_client,
|
||||
)
|
||||
|
||||
@@ -168,7 +168,7 @@ class TestPostHogTracingProcessor:
|
||||
def resolver(trace):
|
||||
return None # Simulate no user ID available
|
||||
|
||||
processor = PostHogTracingProcessor(
|
||||
processor = InsightsTracingProcessor(
|
||||
client=mock_client,
|
||||
distinct_id=resolver,
|
||||
)
|
||||
@@ -188,7 +188,7 @@ class TestPostHogTracingProcessor:
|
||||
|
||||
def test_person_profile_when_distinct_id_provided(self, mock_client, mock_trace):
|
||||
"""Test that events create person profiles when distinct_id is provided."""
|
||||
processor = PostHogTracingProcessor(
|
||||
processor = InsightsTracingProcessor(
|
||||
client=mock_client,
|
||||
distinct_id="real-user",
|
||||
)
|
||||
@@ -375,7 +375,7 @@ class TestPostHogTracingProcessor:
|
||||
|
||||
def test_privacy_mode_redacts_content(self, mock_client, mock_span):
|
||||
"""Test that privacy_mode redacts input/output content."""
|
||||
processor = PostHogTracingProcessor(
|
||||
processor = InsightsTracingProcessor(
|
||||
client=mock_client,
|
||||
distinct_id="test-user",
|
||||
privacy_mode=True,
|
||||
@@ -636,7 +636,7 @@ class TestPostHogTracingProcessor:
|
||||
|
||||
def test_groups_included_in_events(self, mock_client, mock_trace, mock_span):
|
||||
"""Test that groups are included in captured events."""
|
||||
processor = PostHogTracingProcessor(
|
||||
processor = InsightsTracingProcessor(
|
||||
client=mock_client,
|
||||
distinct_id="test-user",
|
||||
groups={"company": "acme", "team": "engineering"},
|
||||
@@ -650,7 +650,7 @@ class TestPostHogTracingProcessor:
|
||||
|
||||
def test_additional_properties_included(self, mock_client, mock_trace):
|
||||
"""Test that additional properties are included in events."""
|
||||
processor = PostHogTracingProcessor(
|
||||
processor = InsightsTracingProcessor(
|
||||
client=mock_client,
|
||||
distinct_id="test-user",
|
||||
properties={"environment": "production", "version": "1.0"},
|
||||
@@ -734,7 +734,7 @@ class TestPostHogTracingProcessor:
|
||||
def resolver(trace):
|
||||
return f"user-{trace.name}"
|
||||
|
||||
processor = PostHogTracingProcessor(
|
||||
processor = InsightsTracingProcessor(
|
||||
client=mock_client,
|
||||
distinct_id=resolver,
|
||||
)
|
||||
@@ -755,7 +755,7 @@ class TestPostHogTracingProcessor:
|
||||
|
||||
def test_eviction_of_stale_entries(self, mock_client):
|
||||
"""Test that stale entries are evicted when max is exceeded."""
|
||||
processor = PostHogTracingProcessor(
|
||||
processor = InsightsTracingProcessor(
|
||||
client=mock_client,
|
||||
distinct_id="test-user",
|
||||
)
|
||||
@@ -785,7 +785,7 @@ class TestInstrumentHelper:
|
||||
)
|
||||
|
||||
mock_add.assert_called_once_with(processor)
|
||||
assert isinstance(processor, PostHogTracingProcessor)
|
||||
assert isinstance(processor, InsightsTracingProcessor)
|
||||
|
||||
def test_instrument_with_privacy_mode(self, mock_client):
|
||||
"""Test instrument() respects privacy_mode."""
|
||||
|
||||
@@ -32,13 +32,13 @@ class TestPrompts(unittest.TestCase):
|
||||
"deleted": False,
|
||||
}
|
||||
|
||||
def create_mock_posthog(
|
||||
def create_mock_client(
|
||||
self,
|
||||
personal_api_key="phx_test_key",
|
||||
project_api_key="phc_test_key",
|
||||
host="https://us.posthog.com",
|
||||
host="https://us.insights.hanzo.ai",
|
||||
):
|
||||
"""Create a mock PostHog client."""
|
||||
"""Create a mock Insights client."""
|
||||
mock = MagicMock()
|
||||
mock.personal_api_key = personal_api_key
|
||||
mock.api_key = project_api_key
|
||||
@@ -55,8 +55,8 @@ class TestPromptsGet(TestPrompts):
|
||||
mock_get = mock_get_session.return_value.get
|
||||
mock_get.return_value = MockResponse(json_data=self.mock_prompt_response)
|
||||
|
||||
posthog = self.create_mock_posthog()
|
||||
prompts = Prompts(posthog)
|
||||
client = self.create_mock_client()
|
||||
prompts = Prompts(client)
|
||||
|
||||
result = prompts.get("test-prompt")
|
||||
|
||||
@@ -65,7 +65,7 @@ class TestPromptsGet(TestPrompts):
|
||||
call_args = mock_get.call_args
|
||||
self.assertEqual(
|
||||
call_args[0][0],
|
||||
"https://us.posthog.com/api/environments/@current/llm_prompts/name/test-prompt/?token=phc_test_key",
|
||||
"https://us.insights.hanzo.ai/api/environments/@current/llm_prompts/name/test-prompt/?token=phc_test_key",
|
||||
)
|
||||
self.assertIn("Authorization", call_args[1]["headers"])
|
||||
self.assertEqual(
|
||||
@@ -83,8 +83,8 @@ class TestPromptsGet(TestPrompts):
|
||||
}
|
||||
mock_get.return_value = MockResponse(json_data=versioned_prompt_response)
|
||||
|
||||
posthog = self.create_mock_posthog()
|
||||
prompts = Prompts(posthog)
|
||||
client = self.create_mock_client()
|
||||
prompts = Prompts(client)
|
||||
|
||||
result = prompts.get("test-prompt", version=1)
|
||||
|
||||
@@ -93,7 +93,7 @@ class TestPromptsGet(TestPrompts):
|
||||
call_args = mock_get.call_args
|
||||
self.assertEqual(
|
||||
call_args[0][0],
|
||||
"https://us.posthog.com/api/environments/@current/llm_prompts/name/test-prompt/?token=phc_test_key&version=1",
|
||||
"https://us.insights.hanzo.ai/api/environments/@current/llm_prompts/name/test-prompt/?token=phc_test_key&version=1",
|
||||
)
|
||||
|
||||
@patch("hanzo_insights.ai.prompts._get_session")
|
||||
@@ -104,8 +104,8 @@ class TestPromptsGet(TestPrompts):
|
||||
mock_get.return_value = MockResponse(json_data=self.mock_prompt_response)
|
||||
mock_time.return_value = 1000.0
|
||||
|
||||
posthog = self.create_mock_posthog()
|
||||
prompts = Prompts(posthog)
|
||||
client = self.create_mock_client()
|
||||
prompts = Prompts(client)
|
||||
|
||||
# First call - fetches from API
|
||||
result1 = prompts.get("test-prompt", cache_ttl_seconds=300)
|
||||
@@ -140,8 +140,8 @@ class TestPromptsGet(TestPrompts):
|
||||
MockResponse(json_data=versioned_prompt_response),
|
||||
]
|
||||
|
||||
posthog = self.create_mock_posthog()
|
||||
prompts = Prompts(posthog)
|
||||
client = self.create_mock_client()
|
||||
prompts = Prompts(client)
|
||||
|
||||
self.assertEqual(prompts.get("test-prompt"), latest_prompt_response["prompt"])
|
||||
self.assertEqual(
|
||||
@@ -171,8 +171,8 @@ class TestPromptsGet(TestPrompts):
|
||||
]
|
||||
mock_time.return_value = 1000.0
|
||||
|
||||
posthog = self.create_mock_posthog()
|
||||
prompts = Prompts(posthog)
|
||||
client = self.create_mock_client()
|
||||
prompts = Prompts(client)
|
||||
|
||||
# First call - fetches from API
|
||||
result1 = prompts.get("test-prompt", cache_ttl_seconds=60)
|
||||
@@ -201,8 +201,8 @@ class TestPromptsGet(TestPrompts):
|
||||
]
|
||||
mock_time.return_value = 1000.0
|
||||
|
||||
posthog = self.create_mock_posthog()
|
||||
prompts = Prompts(posthog)
|
||||
client = self.create_mock_client()
|
||||
prompts = Prompts(client)
|
||||
|
||||
# First call - populates cache
|
||||
result1 = prompts.get("test-prompt", cache_ttl_seconds=60)
|
||||
@@ -229,8 +229,8 @@ class TestPromptsGet(TestPrompts):
|
||||
mock_get = mock_get_session.return_value.get
|
||||
mock_get.side_effect = Exception("Network error")
|
||||
|
||||
posthog = self.create_mock_posthog()
|
||||
prompts = Prompts(posthog)
|
||||
client = self.create_mock_client()
|
||||
prompts = Prompts(client)
|
||||
|
||||
fallback = "Default system prompt."
|
||||
result = prompts.get("test-prompt", fallback=fallback)
|
||||
@@ -248,8 +248,8 @@ class TestPromptsGet(TestPrompts):
|
||||
mock_get = mock_get_session.return_value.get
|
||||
mock_get.side_effect = Exception("Network error")
|
||||
|
||||
posthog = self.create_mock_posthog()
|
||||
prompts = Prompts(posthog)
|
||||
client = self.create_mock_client()
|
||||
prompts = Prompts(client)
|
||||
|
||||
with self.assertRaises(Exception) as context:
|
||||
prompts.get("test-prompt")
|
||||
@@ -262,8 +262,8 @@ class TestPromptsGet(TestPrompts):
|
||||
mock_get = mock_get_session.return_value.get
|
||||
mock_get.return_value = MockResponse(status_code=404, ok=False)
|
||||
|
||||
posthog = self.create_mock_posthog()
|
||||
prompts = Prompts(posthog)
|
||||
client = self.create_mock_client()
|
||||
prompts = Prompts(client)
|
||||
|
||||
with self.assertRaises(Exception) as context:
|
||||
prompts.get("nonexistent-prompt")
|
||||
@@ -276,8 +276,8 @@ class TestPromptsGet(TestPrompts):
|
||||
mock_get = mock_get_session.return_value.get
|
||||
mock_get.return_value = MockResponse(status_code=404, ok=False)
|
||||
|
||||
posthog = self.create_mock_posthog()
|
||||
prompts = Prompts(posthog)
|
||||
client = self.create_mock_client()
|
||||
prompts = Prompts(client)
|
||||
|
||||
with self.assertRaises(Exception) as context:
|
||||
prompts.get("nonexistent-prompt", version=3)
|
||||
@@ -293,8 +293,8 @@ class TestPromptsGet(TestPrompts):
|
||||
mock_get = mock_get_session.return_value.get
|
||||
mock_get.return_value = MockResponse(status_code=403, ok=False)
|
||||
|
||||
posthog = self.create_mock_posthog()
|
||||
prompts = Prompts(posthog)
|
||||
client = self.create_mock_client()
|
||||
prompts = Prompts(client)
|
||||
|
||||
with self.assertRaises(Exception) as context:
|
||||
prompts.get("restricted-prompt")
|
||||
@@ -305,8 +305,8 @@ class TestPromptsGet(TestPrompts):
|
||||
|
||||
def test_throw_when_no_personal_api_key_configured(self):
|
||||
"""Should throw when no personal_api_key is configured."""
|
||||
posthog = self.create_mock_posthog(personal_api_key=None)
|
||||
prompts = Prompts(posthog)
|
||||
client = self.create_mock_client(personal_api_key=None)
|
||||
prompts = Prompts(client)
|
||||
|
||||
with self.assertRaises(Exception) as context:
|
||||
prompts.get("test-prompt")
|
||||
@@ -317,8 +317,8 @@ class TestPromptsGet(TestPrompts):
|
||||
|
||||
def test_throw_when_no_project_api_key_configured(self):
|
||||
"""Should throw when no project_api_key is configured."""
|
||||
posthog = self.create_mock_posthog(project_api_key=None)
|
||||
prompts = Prompts(posthog)
|
||||
client = self.create_mock_client(project_api_key=None)
|
||||
prompts = Prompts(client)
|
||||
|
||||
with self.assertRaises(Exception) as context:
|
||||
prompts.get("test-prompt")
|
||||
@@ -333,8 +333,8 @@ class TestPromptsGet(TestPrompts):
|
||||
mock_get = mock_get_session.return_value.get
|
||||
mock_get.return_value = MockResponse(json_data={"invalid": "response"})
|
||||
|
||||
posthog = self.create_mock_posthog()
|
||||
prompts = Prompts(posthog)
|
||||
client = self.create_mock_client()
|
||||
prompts = Prompts(client)
|
||||
|
||||
with self.assertRaises(Exception) as context:
|
||||
prompts.get("test-prompt")
|
||||
@@ -342,22 +342,22 @@ class TestPromptsGet(TestPrompts):
|
||||
self.assertIn("Invalid response format", str(context.exception))
|
||||
|
||||
@patch("hanzo_insights.ai.prompts._get_session")
|
||||
def test_use_custom_host_from_posthog_options(self, mock_get_session):
|
||||
"""Should use custom host from PostHog options."""
|
||||
def test_use_custom_host_from_insights_options(self, mock_get_session):
|
||||
"""Should use custom host from Insights options."""
|
||||
mock_get = mock_get_session.return_value.get
|
||||
mock_get.return_value = MockResponse(json_data=self.mock_prompt_response)
|
||||
|
||||
posthog = self.create_mock_posthog(host="https://eu.posthog.com")
|
||||
prompts = Prompts(posthog)
|
||||
client = self.create_mock_client(host="https://eu.insights.hanzo.ai")
|
||||
prompts = Prompts(client)
|
||||
|
||||
prompts.get("test-prompt")
|
||||
|
||||
call_args = mock_get.call_args
|
||||
self.assertTrue(
|
||||
call_args[0][0].startswith(
|
||||
"https://eu.posthog.com/api/environments/@current/llm_prompts/name/test-prompt/?token=phc_test_key"
|
||||
"https://eu.insights.hanzo.ai/api/environments/@current/llm_prompts/name/test-prompt/?token=phc_test_key"
|
||||
),
|
||||
f"Expected URL to start with 'https://eu.posthog.com/api/environments/@current/llm_prompts/name/test-prompt/?token=phc_test_key', got {call_args[0][0]}",
|
||||
f"Expected URL to start with 'https://eu.insights.hanzo.ai/api/environments/@current/llm_prompts/name/test-prompt/?token=phc_test_key', got {call_args[0][0]}",
|
||||
)
|
||||
|
||||
@patch("hanzo_insights.ai.prompts._get_session")
|
||||
@@ -368,8 +368,8 @@ class TestPromptsGet(TestPrompts):
|
||||
mock_get.return_value = MockResponse(json_data=self.mock_prompt_response)
|
||||
mock_time.return_value = 1000.0
|
||||
|
||||
posthog = self.create_mock_posthog()
|
||||
prompts = Prompts(posthog)
|
||||
client = self.create_mock_client()
|
||||
prompts = Prompts(client)
|
||||
|
||||
# First call
|
||||
prompts.get("test-prompt")
|
||||
@@ -399,8 +399,8 @@ class TestPromptsGet(TestPrompts):
|
||||
mock_get.return_value = MockResponse(json_data=self.mock_prompt_response)
|
||||
mock_time.return_value = 1000.0
|
||||
|
||||
posthog = self.create_mock_posthog()
|
||||
prompts = Prompts(posthog, default_cache_ttl_seconds=60)
|
||||
client = self.create_mock_client()
|
||||
prompts = Prompts(client, default_cache_ttl_seconds=60)
|
||||
|
||||
# First call
|
||||
prompts.get("test-prompt")
|
||||
@@ -419,20 +419,20 @@ class TestPromptsGet(TestPrompts):
|
||||
mock_get = mock_get_session.return_value.get
|
||||
mock_get.return_value = MockResponse(json_data=self.mock_prompt_response)
|
||||
|
||||
posthog = self.create_mock_posthog()
|
||||
prompts = Prompts(posthog)
|
||||
client = self.create_mock_client()
|
||||
prompts = Prompts(client)
|
||||
|
||||
prompts.get("prompt with spaces/and/slashes")
|
||||
|
||||
call_args = mock_get.call_args
|
||||
self.assertEqual(
|
||||
call_args[0][0],
|
||||
"https://us.posthog.com/api/environments/@current/llm_prompts/name/prompt%20with%20spaces%2Fand%2Fslashes/?token=phc_test_key",
|
||||
"https://us.insights.hanzo.ai/api/environments/@current/llm_prompts/name/prompt%20with%20spaces%2Fand%2Fslashes/?token=phc_test_key",
|
||||
)
|
||||
|
||||
@patch("hanzo_insights.ai.prompts._get_session")
|
||||
def test_work_with_direct_options_no_posthog_client(self, mock_get_session):
|
||||
"""Should work with direct options (no PostHog client)."""
|
||||
def test_work_with_direct_options_no_insights_client(self, mock_get_session):
|
||||
"""Should work with direct options (no Insights client)."""
|
||||
mock_get = mock_get_session.return_value.get
|
||||
mock_get.return_value = MockResponse(json_data=self.mock_prompt_response)
|
||||
|
||||
@@ -446,7 +446,7 @@ class TestPromptsGet(TestPrompts):
|
||||
call_args = mock_get.call_args
|
||||
self.assertEqual(
|
||||
call_args[0][0],
|
||||
"https://us.posthog.com/api/environments/@current/llm_prompts/name/test-prompt/?token=phc_direct_key",
|
||||
"https://us.insights.hanzo.ai/api/environments/@current/llm_prompts/name/test-prompt/?token=phc_direct_key",
|
||||
)
|
||||
self.assertEqual(
|
||||
call_args[1]["headers"]["Authorization"], "Bearer phx_direct_key"
|
||||
@@ -461,7 +461,7 @@ class TestPromptsGet(TestPrompts):
|
||||
prompts = Prompts(
|
||||
personal_api_key="phx_direct_key",
|
||||
project_api_key="phc_direct_key",
|
||||
host="https://eu.posthog.com",
|
||||
host="https://eu.insights.hanzo.ai",
|
||||
)
|
||||
|
||||
prompts.get("test-prompt")
|
||||
@@ -469,7 +469,7 @@ class TestPromptsGet(TestPrompts):
|
||||
call_args = mock_get.call_args
|
||||
self.assertEqual(
|
||||
call_args[0][0],
|
||||
"https://eu.posthog.com/api/environments/@current/llm_prompts/name/test-prompt/?token=phc_direct_key",
|
||||
"https://eu.insights.hanzo.ai/api/environments/@current/llm_prompts/name/test-prompt/?token=phc_direct_key",
|
||||
)
|
||||
|
||||
@patch("hanzo_insights.ai.prompts._get_session")
|
||||
@@ -505,8 +505,8 @@ class TestPromptsCompile(TestPrompts):
|
||||
|
||||
def test_replace_a_single_variable(self):
|
||||
"""Should replace a single variable."""
|
||||
posthog = self.create_mock_posthog()
|
||||
prompts = Prompts(posthog)
|
||||
client = self.create_mock_client()
|
||||
prompts = Prompts(client)
|
||||
|
||||
result = prompts.compile("Hello, {{name}}!", {"name": "World"})
|
||||
|
||||
@@ -514,8 +514,8 @@ class TestPromptsCompile(TestPrompts):
|
||||
|
||||
def test_replace_multiple_variables(self):
|
||||
"""Should replace multiple variables."""
|
||||
posthog = self.create_mock_posthog()
|
||||
prompts = Prompts(posthog)
|
||||
client = self.create_mock_client()
|
||||
prompts = Prompts(client)
|
||||
|
||||
result = prompts.compile(
|
||||
"Hello, {{name}}! Welcome to {{company}}. Your tier is {{tier}}.",
|
||||
@@ -528,8 +528,8 @@ class TestPromptsCompile(TestPrompts):
|
||||
|
||||
def test_handle_numbers(self):
|
||||
"""Should handle numbers."""
|
||||
posthog = self.create_mock_posthog()
|
||||
prompts = Prompts(posthog)
|
||||
client = self.create_mock_client()
|
||||
prompts = Prompts(client)
|
||||
|
||||
result = prompts.compile("You have {{count}} items.", {"count": 42})
|
||||
|
||||
@@ -537,8 +537,8 @@ class TestPromptsCompile(TestPrompts):
|
||||
|
||||
def test_handle_booleans(self):
|
||||
"""Should handle booleans."""
|
||||
posthog = self.create_mock_posthog()
|
||||
prompts = Prompts(posthog)
|
||||
client = self.create_mock_client()
|
||||
prompts = Prompts(client)
|
||||
|
||||
result = prompts.compile("Feature enabled: {{enabled}}", {"enabled": True})
|
||||
|
||||
@@ -546,8 +546,8 @@ class TestPromptsCompile(TestPrompts):
|
||||
|
||||
def test_leave_unmatched_variables_unchanged(self):
|
||||
"""Should leave unmatched variables unchanged."""
|
||||
posthog = self.create_mock_posthog()
|
||||
prompts = Prompts(posthog)
|
||||
client = self.create_mock_client()
|
||||
prompts = Prompts(client)
|
||||
|
||||
result = prompts.compile(
|
||||
"Hello, {{name}}! Your {{unknown}} is ready.", {"name": "World"}
|
||||
@@ -557,8 +557,8 @@ class TestPromptsCompile(TestPrompts):
|
||||
|
||||
def test_handle_prompts_with_no_variables(self):
|
||||
"""Should handle prompts with no variables."""
|
||||
posthog = self.create_mock_posthog()
|
||||
prompts = Prompts(posthog)
|
||||
client = self.create_mock_client()
|
||||
prompts = Prompts(client)
|
||||
|
||||
result = prompts.compile("You are a helpful assistant.", {})
|
||||
|
||||
@@ -566,8 +566,8 @@ class TestPromptsCompile(TestPrompts):
|
||||
|
||||
def test_handle_empty_variables_dict(self):
|
||||
"""Should handle empty variables dict."""
|
||||
posthog = self.create_mock_posthog()
|
||||
prompts = Prompts(posthog)
|
||||
client = self.create_mock_client()
|
||||
prompts = Prompts(client)
|
||||
|
||||
result = prompts.compile("Hello, {{name}}!", {})
|
||||
|
||||
@@ -575,8 +575,8 @@ class TestPromptsCompile(TestPrompts):
|
||||
|
||||
def test_handle_multiple_occurrences_of_same_variable(self):
|
||||
"""Should handle multiple occurrences of the same variable."""
|
||||
posthog = self.create_mock_posthog()
|
||||
prompts = Prompts(posthog)
|
||||
client = self.create_mock_client()
|
||||
prompts = Prompts(client)
|
||||
|
||||
result = prompts.compile(
|
||||
"Hello, {{name}}! Goodbye, {{name}}!", {"name": "World"}
|
||||
@@ -620,8 +620,8 @@ class TestPromptsClearCache(TestPrompts):
|
||||
|
||||
def test_clear_cache_with_version_and_no_name_raises_value_error(self):
|
||||
"""Should enforce that versioned cache clearing requires a prompt name."""
|
||||
posthog = self.create_mock_posthog()
|
||||
prompts = Prompts(posthog)
|
||||
client = self.create_mock_client()
|
||||
prompts = Prompts(client)
|
||||
|
||||
with self.assertRaises(ValueError) as context:
|
||||
prompts.clear_cache(version=1)
|
||||
@@ -640,8 +640,8 @@ class TestPromptsClearCache(TestPrompts):
|
||||
MockResponse(json_data=self.mock_prompt_response),
|
||||
]
|
||||
|
||||
posthog = self.create_mock_posthog()
|
||||
prompts = Prompts(posthog)
|
||||
client = self.create_mock_client()
|
||||
prompts = Prompts(client)
|
||||
|
||||
# Populate cache with two prompts
|
||||
prompts.get("test-prompt")
|
||||
@@ -680,8 +680,8 @@ class TestPromptsClearCache(TestPrompts):
|
||||
MockResponse(json_data=versioned_prompt_response),
|
||||
]
|
||||
|
||||
posthog = self.create_mock_posthog()
|
||||
prompts = Prompts(posthog)
|
||||
client = self.create_mock_client()
|
||||
prompts = Prompts(client)
|
||||
|
||||
prompts.get("test-prompt")
|
||||
prompts.get("test-prompt", version=1)
|
||||
@@ -717,8 +717,8 @@ class TestPromptsClearCache(TestPrompts):
|
||||
MockResponse(json_data=versioned_prompt_response),
|
||||
]
|
||||
|
||||
posthog = self.create_mock_posthog()
|
||||
prompts = Prompts(posthog)
|
||||
client = self.create_mock_client()
|
||||
prompts = Prompts(client)
|
||||
|
||||
prompts.get("test-prompt")
|
||||
prompts.get("test-prompt", version=1)
|
||||
@@ -743,8 +743,8 @@ class TestPromptsClearCache(TestPrompts):
|
||||
MockResponse(json_data=other_prompt_response),
|
||||
]
|
||||
|
||||
posthog = self.create_mock_posthog()
|
||||
prompts = Prompts(posthog)
|
||||
client = self.create_mock_client()
|
||||
prompts = Prompts(client)
|
||||
|
||||
# Populate cache with two prompts
|
||||
prompts.get("test-prompt")
|
||||
|
||||
@@ -26,7 +26,7 @@ class TestSystemPromptCapture(unittest.TestCase):
|
||||
self.test_user_message = "Hello, how are you?"
|
||||
self.test_response = "I'm doing well, thank you!"
|
||||
|
||||
# Create mock PostHog client
|
||||
# Create mock Insights client
|
||||
self.client = Client(FAKE_TEST_API_KEY)
|
||||
self.client._enqueue = MagicMock()
|
||||
self.client.privacy_mode = False
|
||||
@@ -88,7 +88,7 @@ class TestSystemPromptCapture(unittest.TestCase):
|
||||
"openai.resources.chat.completions.Completions.create",
|
||||
return_value=mock_response,
|
||||
):
|
||||
client = OpenAI(posthog_client=self.client, api_key="test")
|
||||
client = OpenAI(insights_client=self.client, api_key="test")
|
||||
|
||||
messages = [
|
||||
{"role": "system", "content": self.test_system_prompt},
|
||||
@@ -96,7 +96,7 @@ class TestSystemPromptCapture(unittest.TestCase):
|
||||
]
|
||||
|
||||
client.chat.completions.create(
|
||||
model="gpt-4", messages=messages, posthog_distinct_id="test-user"
|
||||
model="gpt-4", messages=messages, insights_distinct_id="test-user"
|
||||
)
|
||||
|
||||
self.assertEqual(len(self.client._enqueue.call_args_list), 1)
|
||||
@@ -137,7 +137,7 @@ class TestSystemPromptCapture(unittest.TestCase):
|
||||
"openai.resources.chat.completions.Completions.create",
|
||||
return_value=mock_response,
|
||||
):
|
||||
client = OpenAI(posthog_client=self.client, api_key="test")
|
||||
client = OpenAI(insights_client=self.client, api_key="test")
|
||||
|
||||
messages = [{"role": "user", "content": self.test_user_message}]
|
||||
|
||||
@@ -145,7 +145,7 @@ class TestSystemPromptCapture(unittest.TestCase):
|
||||
model="gpt-4",
|
||||
messages=messages,
|
||||
system=self.test_system_prompt,
|
||||
posthog_distinct_id="test-user",
|
||||
insights_distinct_id="test-user",
|
||||
)
|
||||
|
||||
self.assertEqual(len(self.client._enqueue.call_args_list), 1)
|
||||
@@ -201,7 +201,7 @@ class TestSystemPromptCapture(unittest.TestCase):
|
||||
"openai.resources.chat.completions.Completions.create",
|
||||
return_value=[chunk1, chunk2],
|
||||
):
|
||||
client = OpenAI(posthog_client=self.client, api_key="test")
|
||||
client = OpenAI(insights_client=self.client, api_key="test")
|
||||
|
||||
messages = [{"role": "user", "content": self.test_user_message}]
|
||||
|
||||
@@ -210,7 +210,7 @@ class TestSystemPromptCapture(unittest.TestCase):
|
||||
messages=messages,
|
||||
system=self.test_system_prompt,
|
||||
stream=True,
|
||||
posthog_distinct_id="test-user",
|
||||
insights_distinct_id="test-user",
|
||||
)
|
||||
|
||||
list(response_generator) # Consume generator
|
||||
@@ -235,7 +235,7 @@ class TestSystemPromptCapture(unittest.TestCase):
|
||||
mock_response.usage.cache_creation_input_tokens = None
|
||||
mock_create.return_value = mock_response
|
||||
|
||||
client = Anthropic(posthog_client=self.client, api_key="test")
|
||||
client = Anthropic(insights_client=self.client, api_key="test")
|
||||
|
||||
messages = [
|
||||
{"role": "system", "content": self.test_system_prompt},
|
||||
@@ -245,7 +245,7 @@ class TestSystemPromptCapture(unittest.TestCase):
|
||||
client.messages.create(
|
||||
model="claude-3-5-sonnet-20241022",
|
||||
messages=messages,
|
||||
posthog_distinct_id="test-user",
|
||||
insights_distinct_id="test-user",
|
||||
)
|
||||
|
||||
self.assertEqual(len(self.client._enqueue.call_args_list), 1)
|
||||
@@ -267,7 +267,7 @@ class TestSystemPromptCapture(unittest.TestCase):
|
||||
mock_response.usage.cache_creation_input_tokens = None
|
||||
mock_create.return_value = mock_response
|
||||
|
||||
client = Anthropic(posthog_client=self.client, api_key="test")
|
||||
client = Anthropic(insights_client=self.client, api_key="test")
|
||||
|
||||
messages = [{"role": "user", "content": self.test_user_message}]
|
||||
|
||||
@@ -275,7 +275,7 @@ class TestSystemPromptCapture(unittest.TestCase):
|
||||
model="claude-3-5-sonnet-20241022",
|
||||
messages=messages,
|
||||
system=self.test_system_prompt,
|
||||
posthog_distinct_id="test-user",
|
||||
insights_distinct_id="test-user",
|
||||
)
|
||||
|
||||
self.assertEqual(len(self.client._enqueue.call_args_list), 1)
|
||||
@@ -306,7 +306,7 @@ class TestSystemPromptCapture(unittest.TestCase):
|
||||
mock_client_instance.models = mock_models_instance
|
||||
mock_genai_class.return_value = mock_client_instance
|
||||
|
||||
client = Client(posthog_client=self.client, api_key="test")
|
||||
client = Client(insights_client=self.client, api_key="test")
|
||||
|
||||
contents = [
|
||||
{"role": "system", "content": self.test_system_prompt},
|
||||
@@ -316,7 +316,7 @@ class TestSystemPromptCapture(unittest.TestCase):
|
||||
client.models.generate_content(
|
||||
model="gemini-2.0-flash",
|
||||
contents=contents,
|
||||
posthog_distinct_id="test-user",
|
||||
insights_distinct_id="test-user",
|
||||
)
|
||||
|
||||
self.assertEqual(len(self.client._enqueue.call_args_list), 1)
|
||||
@@ -346,7 +346,7 @@ class TestSystemPromptCapture(unittest.TestCase):
|
||||
mock_client_instance.models = mock_models_instance
|
||||
mock_genai_class.return_value = mock_client_instance
|
||||
|
||||
client = Client(posthog_client=self.client, api_key="test")
|
||||
client = Client(insights_client=self.client, api_key="test")
|
||||
|
||||
contents = [{"role": "user", "content": self.test_user_message}]
|
||||
config = {"system_instruction": self.test_system_prompt}
|
||||
@@ -355,7 +355,7 @@ class TestSystemPromptCapture(unittest.TestCase):
|
||||
model="gemini-2.0-flash",
|
||||
contents=contents,
|
||||
config=config,
|
||||
posthog_distinct_id="test-user",
|
||||
insights_distinct_id="test-user",
|
||||
)
|
||||
|
||||
self.assertEqual(len(self.client._enqueue.call_args_list), 1)
|
||||
|
||||
@@ -5,10 +5,10 @@ from hanzo_insights.ai.utils import _get_tokens_source
|
||||
|
||||
@parameterized.expand(
|
||||
[
|
||||
("no_posthog_properties", {"$ai_input_tokens": 100}, None, "sdk"),
|
||||
("empty_posthog_properties", {"$ai_input_tokens": 100}, {}, "sdk"),
|
||||
("no_insights_properties", {"$ai_input_tokens": 100}, None, "sdk"),
|
||||
("empty_insights_properties", {"$ai_input_tokens": 100}, {}, "sdk"),
|
||||
(
|
||||
"unrelated_posthog_properties",
|
||||
"unrelated_insights_properties",
|
||||
{"$ai_input_tokens": 100},
|
||||
{"foo": "bar"},
|
||||
"sdk",
|
||||
@@ -57,6 +57,6 @@ from hanzo_insights.ai.utils import _get_tokens_source
|
||||
),
|
||||
]
|
||||
)
|
||||
def test_get_tokens_source(name, sdk_tags, posthog_properties, expected):
|
||||
result = _get_tokens_source(sdk_tags, posthog_properties)
|
||||
def test_get_tokens_source(name, sdk_tags, insights_properties, expected):
|
||||
result = _get_tokens_source(sdk_tags, insights_properties)
|
||||
assert result == expected
|
||||
|
||||
@@ -20,7 +20,7 @@ if not settings.configured:
|
||||
)
|
||||
django.setup()
|
||||
|
||||
from hanzo_insights.integrations.django import PosthogContextMiddleware
|
||||
from hanzo_insights.integrations.django import InsightsContextMiddleware
|
||||
|
||||
|
||||
class MockRequest:
|
||||
@@ -45,7 +45,7 @@ class MockRequest:
|
||||
return f"{scheme}://{self._host}{self.path}"
|
||||
|
||||
|
||||
class TestPosthogContextMiddleware(unittest.TestCase):
|
||||
class TestInsightsContextMiddleware(unittest.TestCase):
|
||||
def create_middleware(
|
||||
self,
|
||||
extra_tags=None,
|
||||
@@ -60,24 +60,24 @@ class TestPosthogContextMiddleware(unittest.TestCase):
|
||||
|
||||
with patch("django.conf.settings") as mock_settings:
|
||||
# Configure mock settings
|
||||
mock_settings.POSTHOG_MW_EXTRA_TAGS = extra_tags
|
||||
mock_settings.POSTHOG_MW_REQUEST_FILTER = request_filter
|
||||
mock_settings.POSTHOG_MW_TAG_MAP = tag_map
|
||||
mock_settings.POSTHOG_MW_CAPTURE_EXCEPTIONS = capture_exceptions
|
||||
mock_settings.POSTHOG_MW_CLIENT = None
|
||||
mock_settings.INSIGHTS_MW_EXTRA_TAGS = extra_tags
|
||||
mock_settings.INSIGHTS_MW_REQUEST_FILTER = request_filter
|
||||
mock_settings.INSIGHTS_MW_TAG_MAP = tag_map
|
||||
mock_settings.INSIGHTS_MW_CAPTURE_EXCEPTIONS = capture_exceptions
|
||||
mock_settings.INSIGHTS_MW_CLIENT = None
|
||||
|
||||
# Make hasattr work correctly
|
||||
def mock_hasattr(obj, name):
|
||||
return name in [
|
||||
"POSTHOG_MW_EXTRA_TAGS",
|
||||
"POSTHOG_MW_REQUEST_FILTER",
|
||||
"POSTHOG_MW_TAG_MAP",
|
||||
"POSTHOG_MW_CAPTURE_EXCEPTIONS",
|
||||
"POSTHOG_MW_CLIENT",
|
||||
"INSIGHTS_MW_EXTRA_TAGS",
|
||||
"INSIGHTS_MW_REQUEST_FILTER",
|
||||
"INSIGHTS_MW_TAG_MAP",
|
||||
"INSIGHTS_MW_CAPTURE_EXCEPTIONS",
|
||||
"INSIGHTS_MW_CLIENT",
|
||||
]
|
||||
|
||||
with patch("builtins.hasattr", side_effect=mock_hasattr):
|
||||
middleware = PosthogContextMiddleware(get_response)
|
||||
middleware = InsightsContextMiddleware(get_response)
|
||||
|
||||
return middleware
|
||||
|
||||
@@ -87,8 +87,8 @@ class TestPosthogContextMiddleware(unittest.TestCase):
|
||||
middleware = self.create_middleware()
|
||||
request = MockRequest(
|
||||
headers={
|
||||
"X-POSTHOG-SESSION-ID": "session-123",
|
||||
"X-POSTHOG-DISTINCT-ID": "user-456",
|
||||
"X-INSIGHTS-SESSION-ID": "session-123",
|
||||
"X-INSIGHTS-DISTINCT-ID": "user-456",
|
||||
},
|
||||
method="POST",
|
||||
path="/api/test",
|
||||
@@ -104,7 +104,7 @@ class TestPosthogContextMiddleware(unittest.TestCase):
|
||||
self.assertEqual(tags["$request_method"], "POST")
|
||||
|
||||
def test_extract_tags_missing_headers(self):
|
||||
"""Test tag extraction when PostHog headers are missing"""
|
||||
"""Test tag extraction when Insights headers are missing"""
|
||||
|
||||
with new_context():
|
||||
middleware = self.create_middleware()
|
||||
@@ -118,12 +118,12 @@ class TestPosthogContextMiddleware(unittest.TestCase):
|
||||
self.assertEqual(tags["$request_method"], "GET")
|
||||
|
||||
def test_extract_tags_partial_headers(self):
|
||||
"""Test tag extraction with only some PostHog headers present"""
|
||||
"""Test tag extraction with only some Insights headers present"""
|
||||
|
||||
with new_context():
|
||||
middleware = self.create_middleware()
|
||||
request = MockRequest(
|
||||
headers={"X-POSTHOG-SESSION-ID": "session-only"}, method="PUT"
|
||||
headers={"X-INSIGHTS-SESSION-ID": "session-only"}, method="PUT"
|
||||
)
|
||||
|
||||
tags = middleware.extract_tags(request)
|
||||
@@ -141,7 +141,7 @@ class TestPosthogContextMiddleware(unittest.TestCase):
|
||||
with new_context():
|
||||
middleware = self.create_middleware(extra_tags=extra_tags_func)
|
||||
request = MockRequest(
|
||||
headers={"X-POSTHOG-SESSION-ID": "session-123"}, method="GET"
|
||||
headers={"X-INSIGHTS-SESSION-ID": "session-123"}, method="GET"
|
||||
)
|
||||
|
||||
tags = middleware.extract_tags(request)
|
||||
@@ -167,7 +167,7 @@ class TestPosthogContextMiddleware(unittest.TestCase):
|
||||
tag_map=tag_map_func, extra_tags=extra_tags_func
|
||||
)
|
||||
request = MockRequest(
|
||||
headers={"X-POSTHOG-SESSION-ID": "session-123"}, method="GET"
|
||||
headers={"X-INSIGHTS-SESSION-ID": "session-123"}, method="GET"
|
||||
)
|
||||
|
||||
tags = middleware.extract_tags(request)
|
||||
@@ -230,7 +230,7 @@ class TestPosthogContextMiddleware(unittest.TestCase):
|
||||
middleware.client = mock_client
|
||||
|
||||
request = MockRequest(
|
||||
headers={"X-POSTHOG-DISTINCT-ID": "test-user"},
|
||||
headers={"X-INSIGHTS-DISTINCT-ID": "test-user"},
|
||||
method="POST",
|
||||
path="/api/endpoint",
|
||||
)
|
||||
@@ -282,7 +282,7 @@ class TestPosthogContextMiddleware(unittest.TestCase):
|
||||
mock_client.capture_exception.assert_not_called()
|
||||
|
||||
|
||||
class TestPosthogContextMiddlewareSync(unittest.TestCase):
|
||||
class TestInsightsContextMiddlewareSync(unittest.TestCase):
|
||||
"""Test synchronous middleware behavior"""
|
||||
|
||||
def test_sync_middleware_call(self):
|
||||
@@ -291,13 +291,13 @@ class TestPosthogContextMiddlewareSync(unittest.TestCase):
|
||||
get_response = Mock(return_value=mock_response)
|
||||
|
||||
# Create middleware with sync get_response
|
||||
middleware = PosthogContextMiddleware(get_response)
|
||||
middleware = InsightsContextMiddleware(get_response)
|
||||
|
||||
# Verify sync mode detected
|
||||
self.assertFalse(middleware._is_coroutine)
|
||||
|
||||
request = MockRequest(
|
||||
headers={"X-POSTHOG-SESSION-ID": "test-session"},
|
||||
headers={"X-INSIGHTS-SESSION-ID": "test-session"},
|
||||
method="GET",
|
||||
path="/test",
|
||||
)
|
||||
@@ -318,7 +318,7 @@ class TestPosthogContextMiddlewareSync(unittest.TestCase):
|
||||
def request_filter(req):
|
||||
return False
|
||||
|
||||
middleware = PosthogContextMiddleware.__new__(PosthogContextMiddleware)
|
||||
middleware = InsightsContextMiddleware.__new__(InsightsContextMiddleware)
|
||||
middleware.get_response = get_response
|
||||
middleware._is_coroutine = False
|
||||
middleware.request_filter = request_filter
|
||||
@@ -351,7 +351,7 @@ class TestPosthogContextMiddlewareSync(unittest.TestCase):
|
||||
mock_client = Mock()
|
||||
get_response = Mock(return_value=Mock(status_code=500))
|
||||
|
||||
middleware = PosthogContextMiddleware(get_response)
|
||||
middleware = InsightsContextMiddleware(get_response)
|
||||
middleware.client = mock_client
|
||||
|
||||
def get_response_simulating_django(request):
|
||||
@@ -380,7 +380,7 @@ class TestPosthogContextMiddlewareSync(unittest.TestCase):
|
||||
)
|
||||
|
||||
|
||||
class TestPosthogContextMiddlewareAsync(unittest.TestCase):
|
||||
class TestInsightsContextMiddlewareAsync(unittest.TestCase):
|
||||
"""Test asynchronous middleware behavior"""
|
||||
|
||||
def test_async_middleware_detection(self):
|
||||
@@ -389,7 +389,7 @@ class TestPosthogContextMiddlewareAsync(unittest.TestCase):
|
||||
async def async_get_response(request):
|
||||
return Mock()
|
||||
|
||||
middleware = PosthogContextMiddleware(async_get_response)
|
||||
middleware = InsightsContextMiddleware(async_get_response)
|
||||
|
||||
# Verify async mode detected
|
||||
self.assertTrue(middleware._is_coroutine)
|
||||
@@ -403,10 +403,10 @@ class TestPosthogContextMiddlewareAsync(unittest.TestCase):
|
||||
async def async_get_response(request):
|
||||
return mock_response
|
||||
|
||||
middleware = PosthogContextMiddleware(async_get_response)
|
||||
middleware = InsightsContextMiddleware(async_get_response)
|
||||
|
||||
request = MockRequest(
|
||||
headers={"X-POSTHOG-SESSION-ID": "async-session"},
|
||||
headers={"X-INSIGHTS-SESSION-ID": "async-session"},
|
||||
method="POST",
|
||||
path="/async-test",
|
||||
)
|
||||
@@ -434,7 +434,7 @@ class TestPosthogContextMiddlewareAsync(unittest.TestCase):
|
||||
return mock_response
|
||||
|
||||
# Properly initialize middleware
|
||||
middleware = PosthogContextMiddleware(async_get_response)
|
||||
middleware = InsightsContextMiddleware(async_get_response)
|
||||
# Override request filter after initialization
|
||||
middleware.request_filter = lambda req: False
|
||||
|
||||
@@ -459,10 +459,10 @@ class TestPosthogContextMiddlewareAsync(unittest.TestCase):
|
||||
self.assertEqual(session_id, "async-session-123")
|
||||
return mock_response
|
||||
|
||||
middleware = PosthogContextMiddleware(async_get_response)
|
||||
middleware = InsightsContextMiddleware(async_get_response)
|
||||
|
||||
request = MockRequest(
|
||||
headers={"X-POSTHOG-SESSION-ID": "async-session-123"},
|
||||
headers={"X-INSIGHTS-SESSION-ID": "async-session-123"},
|
||||
method="GET",
|
||||
)
|
||||
|
||||
@@ -483,7 +483,7 @@ class TestPosthogContextMiddlewareAsync(unittest.TestCase):
|
||||
raise ValueError("Async test exception")
|
||||
|
||||
# Properly initialize middleware
|
||||
middleware = PosthogContextMiddleware(raise_exception)
|
||||
middleware = InsightsContextMiddleware(raise_exception)
|
||||
middleware.client = mock_client # Override with mock client
|
||||
|
||||
request = MockRequest()
|
||||
@@ -525,11 +525,11 @@ class TestPosthogContextMiddlewareAsync(unittest.TestCase):
|
||||
self.assertEqual(distinct_id, "123")
|
||||
return mock_response
|
||||
|
||||
middleware = PosthogContextMiddleware(async_get_response)
|
||||
middleware = InsightsContextMiddleware(async_get_response)
|
||||
middleware.client = Mock()
|
||||
|
||||
request = MockRequest(
|
||||
headers={"X-POSTHOG-SESSION-ID": "test-session"}, method="GET"
|
||||
headers={"X-INSIGHTS-SESSION-ID": "test-session"}, method="GET"
|
||||
)
|
||||
|
||||
# Mock auser() to return authenticated user
|
||||
@@ -561,11 +561,11 @@ class TestPosthogContextMiddlewareAsync(unittest.TestCase):
|
||||
self.assertIsNone(distinct_id)
|
||||
return mock_response
|
||||
|
||||
middleware = PosthogContextMiddleware(async_get_response)
|
||||
middleware = InsightsContextMiddleware(async_get_response)
|
||||
middleware.client = Mock()
|
||||
|
||||
request = MockRequest(
|
||||
headers={"X-POSTHOG-SESSION-ID": "test-session"}, method="GET"
|
||||
headers={"X-INSIGHTS-SESSION-ID": "test-session"}, method="GET"
|
||||
)
|
||||
|
||||
async def mock_auser():
|
||||
@@ -591,12 +591,12 @@ class TestPosthogContextMiddlewareAsync(unittest.TestCase):
|
||||
async def async_get_response(request):
|
||||
return mock_response
|
||||
|
||||
middleware = PosthogContextMiddleware(async_get_response)
|
||||
middleware = InsightsContextMiddleware(async_get_response)
|
||||
middleware.client = Mock()
|
||||
|
||||
# Request without auser method (no auth middleware)
|
||||
request = MockRequest(
|
||||
headers={"X-POSTHOG-SESSION-ID": "test-session"}, method="GET"
|
||||
headers={"X-INSIGHTS-SESSION-ID": "test-session"}, method="GET"
|
||||
)
|
||||
|
||||
with new_context():
|
||||
@@ -621,12 +621,12 @@ class TestPosthogContextMiddlewareAsync(unittest.TestCase):
|
||||
async def async_get_response(request):
|
||||
return mock_response
|
||||
|
||||
middleware = PosthogContextMiddleware(async_get_response)
|
||||
middleware = InsightsContextMiddleware(async_get_response)
|
||||
middleware.extra_tags = extra_tags_callback
|
||||
middleware.client = Mock()
|
||||
|
||||
request = MockRequest(
|
||||
headers={"X-POSTHOG-SESSION-ID": "test-session"}, method="GET"
|
||||
headers={"X-INSIGHTS-SESSION-ID": "test-session"}, method="GET"
|
||||
)
|
||||
|
||||
# Mock auser for no user
|
||||
@@ -658,12 +658,12 @@ class TestPosthogContextMiddlewareAsync(unittest.TestCase):
|
||||
async def async_get_response(request):
|
||||
return mock_response
|
||||
|
||||
middleware = PosthogContextMiddleware(async_get_response)
|
||||
middleware = InsightsContextMiddleware(async_get_response)
|
||||
middleware.tag_map = tag_map_callback
|
||||
middleware.client = Mock()
|
||||
|
||||
request = MockRequest(
|
||||
headers={"X-POSTHOG-SESSION-ID": "test-session"}, method="GET"
|
||||
headers={"X-INSIGHTS-SESSION-ID": "test-session"}, method="GET"
|
||||
)
|
||||
|
||||
# Mock auser for no user
|
||||
@@ -699,12 +699,12 @@ class TestPosthogContextMiddlewareAsync(unittest.TestCase):
|
||||
self.assertEqual(session_id, "async-sess-123")
|
||||
return mock_response
|
||||
|
||||
middleware = PosthogContextMiddleware(async_get_response)
|
||||
middleware = InsightsContextMiddleware(async_get_response)
|
||||
middleware.client = Mock()
|
||||
|
||||
request = MockRequest(
|
||||
headers={
|
||||
"X-POSTHOG-SESSION-ID": "async-sess-123",
|
||||
"X-INSIGHTS-SESSION-ID": "async-sess-123",
|
||||
"X-Forwarded-For": "192.168.1.1",
|
||||
"User-Agent": "TestAgent/1.0",
|
||||
},
|
||||
@@ -725,13 +725,13 @@ class TestPosthogContextMiddlewareAsync(unittest.TestCase):
|
||||
asyncio.run(run_test())
|
||||
|
||||
|
||||
class TestPosthogContextMiddlewareHybrid(unittest.TestCase):
|
||||
class TestInsightsContextMiddlewareHybrid(unittest.TestCase):
|
||||
"""Test hybrid middleware behavior with mixed sync/async chains"""
|
||||
|
||||
def test_hybrid_flags_set(self):
|
||||
"""Test that both capability flags are set"""
|
||||
self.assertTrue(PosthogContextMiddleware.sync_capable)
|
||||
self.assertTrue(PosthogContextMiddleware.async_capable)
|
||||
self.assertTrue(InsightsContextMiddleware.sync_capable)
|
||||
self.assertTrue(InsightsContextMiddleware.async_capable)
|
||||
|
||||
def test_sync_to_async_routing(self):
|
||||
"""Test that __call__ routes to __acall__ when async"""
|
||||
@@ -740,7 +740,7 @@ class TestPosthogContextMiddlewareHybrid(unittest.TestCase):
|
||||
async def async_get_response(request):
|
||||
return Mock()
|
||||
|
||||
middleware = PosthogContextMiddleware(async_get_response)
|
||||
middleware = InsightsContextMiddleware(async_get_response)
|
||||
|
||||
# Verify routing happens
|
||||
request = MockRequest()
|
||||
@@ -759,7 +759,7 @@ class TestPosthogContextMiddlewareHybrid(unittest.TestCase):
|
||||
def sync_get_response(request):
|
||||
return mock_response
|
||||
|
||||
middleware = PosthogContextMiddleware(sync_get_response)
|
||||
middleware = InsightsContextMiddleware(sync_get_response)
|
||||
|
||||
request = MockRequest()
|
||||
result = middleware(request)
|
||||
|
||||
@@ -61,7 +61,7 @@ class TestClient(unittest.TestCase):
|
||||
self.assertTrue(isinstance(msg["timestamp"], str))
|
||||
self.assertIsNotNone(msg.get("uuid"))
|
||||
self.assertEqual(msg["distinct_id"], "distinct_id")
|
||||
self.assertEqual(msg["properties"]["$lib"], "posthog-python")
|
||||
self.assertEqual(msg["properties"]["$lib"], "insights-python")
|
||||
self.assertEqual(msg["properties"]["$lib_version"], VERSION)
|
||||
# these will change between platforms so just asssert on presence here
|
||||
assert msg["properties"]["$python_runtime"] == mock.ANY
|
||||
@@ -88,7 +88,7 @@ class TestClient(unittest.TestCase):
|
||||
self.assertTrue(isinstance(msg["timestamp"], str))
|
||||
self.assertEqual(msg["uuid"], uuid)
|
||||
self.assertEqual(msg["distinct_id"], "distinct_id")
|
||||
self.assertEqual(msg["properties"]["$lib"], "posthog-python")
|
||||
self.assertEqual(msg["properties"]["$lib"], "insights-python")
|
||||
self.assertEqual(msg["properties"]["$lib_version"], VERSION)
|
||||
|
||||
def test_basic_capture_with_project_api_key(self):
|
||||
@@ -111,7 +111,7 @@ class TestClient(unittest.TestCase):
|
||||
self.assertEqual(msg["event"], "python test event")
|
||||
self.assertTrue(isinstance(msg["timestamp"], str))
|
||||
self.assertEqual(msg["distinct_id"], "distinct_id")
|
||||
self.assertEqual(msg["properties"]["$lib"], "posthog-python")
|
||||
self.assertEqual(msg["properties"]["$lib"], "insights-python")
|
||||
self.assertEqual(msg["properties"]["$lib_version"], VERSION)
|
||||
|
||||
def test_basic_super_properties(self):
|
||||
@@ -299,7 +299,7 @@ class TestClient(unittest.TestCase):
|
||||
self.assertTrue(isinstance(msg["timestamp"], str))
|
||||
self.assertIsNotNone(msg.get("uuid"))
|
||||
self.assertEqual(msg["distinct_id"], "distinct_id")
|
||||
self.assertEqual(msg["properties"]["$lib"], "posthog-python")
|
||||
self.assertEqual(msg["properties"]["$lib"], "insights-python")
|
||||
self.assertEqual(msg["properties"]["$lib_version"], VERSION)
|
||||
self.assertEqual(
|
||||
msg["properties"]["$feature/beta-feature"], "random-variant"
|
||||
@@ -424,7 +424,7 @@ class TestClient(unittest.TestCase):
|
||||
self.assertTrue(isinstance(msg["timestamp"], str))
|
||||
self.assertIsNotNone(msg.get("uuid"))
|
||||
self.assertEqual(msg["distinct_id"], "distinct_id")
|
||||
self.assertEqual(msg["properties"]["$lib"], "posthog-python")
|
||||
self.assertEqual(msg["properties"]["$lib"], "insights-python")
|
||||
self.assertEqual(msg["properties"]["$lib_version"], VERSION)
|
||||
self.assertEqual(
|
||||
msg["properties"]["$feature/beta-feature-local"], "third-variant"
|
||||
@@ -595,7 +595,7 @@ class TestClient(unittest.TestCase):
|
||||
self.assertTrue(isinstance(msg["timestamp"], str))
|
||||
self.assertIsNotNone(msg.get("uuid"))
|
||||
self.assertEqual(msg["distinct_id"], "distinct_id")
|
||||
self.assertEqual(msg["properties"]["$lib"], "posthog-python")
|
||||
self.assertEqual(msg["properties"]["$lib"], "insights-python")
|
||||
self.assertEqual(msg["properties"]["$lib_version"], VERSION)
|
||||
self.assertEqual(
|
||||
msg["properties"]["$feature/beta-feature-local"], "my-custom-variant"
|
||||
@@ -641,7 +641,7 @@ class TestClient(unittest.TestCase):
|
||||
self.assertIsNotNone(msg.get("uuid"))
|
||||
self.assertEqual(msg["distinct_id"], "distinct_id")
|
||||
self.assertTrue(msg["properties"]["$geoip_disable"])
|
||||
self.assertEqual(msg["properties"]["$lib"], "posthog-python")
|
||||
self.assertEqual(msg["properties"]["$lib"], "insights-python")
|
||||
self.assertEqual(msg["properties"]["$lib_version"], VERSION)
|
||||
self.assertEqual(
|
||||
msg["properties"]["$feature/beta-feature"], "random-variant"
|
||||
@@ -655,7 +655,7 @@ class TestClient(unittest.TestCase):
|
||||
self.assertEqual(patch_flags.call_count, 1)
|
||||
patch_flags.assert_called_with(
|
||||
"random_key",
|
||||
"https://us.i.posthog.com",
|
||||
"https://us.i.insights.hanzo.ai",
|
||||
timeout=3,
|
||||
distinct_id="distinct_id",
|
||||
groups={},
|
||||
@@ -706,7 +706,7 @@ class TestClient(unittest.TestCase):
|
||||
self.assertIsNotNone(msg.get("uuid"))
|
||||
self.assertTrue("$geoip_disable" not in msg["properties"])
|
||||
self.assertEqual(msg["distinct_id"], "distinct_id")
|
||||
self.assertEqual(msg["properties"]["$lib"], "posthog-python")
|
||||
self.assertEqual(msg["properties"]["$lib"], "insights-python")
|
||||
self.assertEqual(msg["properties"]["$lib_version"], VERSION)
|
||||
self.assertEqual(
|
||||
msg["properties"]["$feature/beta-feature"], "random-variant"
|
||||
@@ -720,7 +720,7 @@ class TestClient(unittest.TestCase):
|
||||
self.assertEqual(patch_flags.call_count, 1)
|
||||
patch_flags.assert_called_with(
|
||||
"random_key",
|
||||
"https://us.i.posthog.com",
|
||||
"https://us.i.insights.hanzo.ai",
|
||||
timeout=12,
|
||||
distinct_id="distinct_id",
|
||||
groups={},
|
||||
@@ -758,7 +758,7 @@ class TestClient(unittest.TestCase):
|
||||
self.assertTrue(isinstance(msg["timestamp"], str))
|
||||
self.assertIsNotNone(msg.get("uuid"))
|
||||
self.assertEqual(msg["distinct_id"], "distinct_id")
|
||||
self.assertEqual(msg["properties"]["$lib"], "posthog-python")
|
||||
self.assertEqual(msg["properties"]["$lib"], "insights-python")
|
||||
self.assertEqual(msg["properties"]["$lib_version"], VERSION)
|
||||
self.assertTrue("$feature/beta-feature" not in msg["properties"])
|
||||
self.assertTrue("$active_feature_flags" not in msg["properties"])
|
||||
@@ -1156,7 +1156,7 @@ class TestClient(unittest.TestCase):
|
||||
self.assertEqual(msg["timestamp"], "2014-09-03T00:00:00+00:00")
|
||||
self.assertEqual(msg["properties"]["property"], "value")
|
||||
self.assertEqual(msg["event"], "python test event")
|
||||
self.assertEqual(msg["properties"]["$lib"], "posthog-python")
|
||||
self.assertEqual(msg["properties"]["$lib"], "insights-python")
|
||||
self.assertEqual(msg["properties"]["$lib_version"], VERSION)
|
||||
self.assertEqual(msg["uuid"], "new-uuid")
|
||||
self.assertEqual(msg["distinct_id"], "distinct_id")
|
||||
@@ -1221,7 +1221,7 @@ class TestClient(unittest.TestCase):
|
||||
|
||||
self.assertEqual(msg["timestamp"], "2014-09-03T00:00:00+00:00")
|
||||
self.assertEqual(msg["$set"]["trait"], "value")
|
||||
self.assertEqual(msg["properties"]["$lib"], "posthog-python")
|
||||
self.assertEqual(msg["properties"]["$lib"], "insights-python")
|
||||
self.assertEqual(msg["properties"]["$lib_version"], VERSION)
|
||||
self.assertTrue(isinstance(msg["timestamp"], str))
|
||||
self.assertEqual(msg["uuid"], "new-uuid")
|
||||
@@ -1265,7 +1265,7 @@ class TestClient(unittest.TestCase):
|
||||
|
||||
self.assertEqual(msg["timestamp"], "2014-09-03T00:00:00+00:00")
|
||||
self.assertEqual(msg["$set_once"]["trait"], "value")
|
||||
self.assertEqual(msg["properties"]["$lib"], "posthog-python")
|
||||
self.assertEqual(msg["properties"]["$lib"], "insights-python")
|
||||
self.assertEqual(msg["properties"]["$lib_version"], VERSION)
|
||||
self.assertTrue(isinstance(msg["timestamp"], str))
|
||||
self.assertEqual(msg["uuid"], "new-uuid")
|
||||
@@ -1290,7 +1290,7 @@ class TestClient(unittest.TestCase):
|
||||
"$group_type": "organization",
|
||||
"$group_key": "id:5",
|
||||
"$group_set": {},
|
||||
"$lib": "posthog-python",
|
||||
"$lib": "insights-python",
|
||||
"$lib_version": VERSION,
|
||||
"$geoip_disable": True,
|
||||
},
|
||||
@@ -1319,7 +1319,7 @@ class TestClient(unittest.TestCase):
|
||||
"$group_type": "organization",
|
||||
"$group_key": "id:5",
|
||||
"$group_set": {},
|
||||
"$lib": "posthog-python",
|
||||
"$lib": "insights-python",
|
||||
"$lib_version": VERSION,
|
||||
"$geoip_disable": True,
|
||||
},
|
||||
@@ -1352,7 +1352,7 @@ class TestClient(unittest.TestCase):
|
||||
"$group_type": "organization",
|
||||
"$group_key": "id:5",
|
||||
"$group_set": {"trait": "value"},
|
||||
"$lib": "posthog-python",
|
||||
"$lib": "insights-python",
|
||||
"$lib_version": VERSION,
|
||||
"$geoip_disable": True,
|
||||
},
|
||||
@@ -1387,7 +1387,7 @@ class TestClient(unittest.TestCase):
|
||||
"$group_type": "organization",
|
||||
"$group_key": "id:5",
|
||||
"$group_set": {"trait": "value"},
|
||||
"$lib": "posthog-python",
|
||||
"$lib": "insights-python",
|
||||
"$lib_version": VERSION,
|
||||
"$geoip_disable": True,
|
||||
},
|
||||
@@ -1454,7 +1454,7 @@ class TestClient(unittest.TestCase):
|
||||
self.assertEqual(msg["event"], "python test event")
|
||||
self.assertEqual(msg["distinct_id"], "distinct_id")
|
||||
self.assertEqual(msg["properties"]["$session_id"], session_id)
|
||||
self.assertEqual(msg["properties"]["$lib"], "posthog-python")
|
||||
self.assertEqual(msg["properties"]["$lib"], "insights-python")
|
||||
self.assertEqual(msg["properties"]["$lib_version"], VERSION)
|
||||
|
||||
# Check additional expected properties
|
||||
@@ -1607,7 +1607,7 @@ class TestClient(unittest.TestCase):
|
||||
self.assertEqual(msg["properties"][key], value)
|
||||
|
||||
# Verify system properties are still added
|
||||
self.assertEqual(msg["properties"]["$lib"], "posthog-python")
|
||||
self.assertEqual(msg["properties"]["$lib"], "insights-python")
|
||||
self.assertEqual(msg["properties"]["$lib_version"], VERSION)
|
||||
|
||||
@parameterized.expand(
|
||||
@@ -1920,7 +1920,7 @@ class TestClient(unittest.TestCase):
|
||||
client.get_feature_flag("random_key", "some_id", disable_geoip=True)
|
||||
patch_flags.assert_called_with(
|
||||
"random_key",
|
||||
"https://us.i.posthog.com",
|
||||
"https://us.i.insights.hanzo.ai",
|
||||
timeout=3,
|
||||
distinct_id="some_id",
|
||||
groups={},
|
||||
@@ -1936,7 +1936,7 @@ class TestClient(unittest.TestCase):
|
||||
)
|
||||
patch_flags.assert_called_with(
|
||||
"random_key",
|
||||
"https://us.i.posthog.com",
|
||||
"https://us.i.insights.hanzo.ai",
|
||||
timeout=3,
|
||||
distinct_id="feature_enabled_distinct_id",
|
||||
groups={},
|
||||
@@ -1950,7 +1950,7 @@ class TestClient(unittest.TestCase):
|
||||
client.get_all_flags_and_payloads("all_flags_payloads_id")
|
||||
patch_flags.assert_called_with(
|
||||
"random_key",
|
||||
"https://us.i.posthog.com",
|
||||
"https://us.i.insights.hanzo.ai",
|
||||
timeout=3,
|
||||
distinct_id="all_flags_payloads_id",
|
||||
groups={},
|
||||
@@ -2107,7 +2107,7 @@ class TestClient(unittest.TestCase):
|
||||
expected_call["flag_keys_to_evaluate"] = expected_flag_keys
|
||||
|
||||
patch_flags.assert_called_with(
|
||||
"random_key", "https://us.i.posthog.com", timeout=3, **expected_call
|
||||
"random_key", "https://us.i.insights.hanzo.ai", timeout=3, **expected_call
|
||||
)
|
||||
|
||||
@mock.patch("hanzo_insights.client.flags")
|
||||
@@ -2131,7 +2131,7 @@ class TestClient(unittest.TestCase):
|
||||
client.get_feature_flag("random_key", "some_id")
|
||||
patch_flags.assert_called_with(
|
||||
"random_key",
|
||||
"https://us.i.posthog.com",
|
||||
"https://us.i.insights.hanzo.ai",
|
||||
timeout=3,
|
||||
distinct_id="some_id",
|
||||
groups={},
|
||||
@@ -2151,7 +2151,7 @@ class TestClient(unittest.TestCase):
|
||||
)
|
||||
patch_flags.assert_called_with(
|
||||
"random_key",
|
||||
"https://us.i.posthog.com",
|
||||
"https://us.i.insights.hanzo.ai",
|
||||
timeout=3,
|
||||
distinct_id="some_id",
|
||||
groups={},
|
||||
|
||||
@@ -10,8 +10,8 @@ def test_excepthook(tmpdir):
|
||||
app.write(
|
||||
dedent(
|
||||
"""
|
||||
from hanzo_insights import Posthog
|
||||
posthog = Posthog('phc_x', host='https://eu.i.posthog.com', enable_exception_autocapture=True, debug=True, on_error=lambda e, batch: print('error handling batch: ', e, batch))
|
||||
from hanzo_insights import Insights
|
||||
client = Insights('phc_x', host='https://eu.i.insights.hanzo.ai', enable_exception_autocapture=True, debug=True, on_error=lambda e, batch: print('error handling batch: ', e, batch))
|
||||
|
||||
# frame_value = "LOL"
|
||||
|
||||
@@ -40,14 +40,14 @@ def test_code_variables_capture(tmpdir):
|
||||
dedent(
|
||||
"""
|
||||
import os
|
||||
from hanzo_insights import Posthog
|
||||
from hanzo_insights import Insights
|
||||
|
||||
class UnserializableObject:
|
||||
pass
|
||||
|
||||
posthog = Posthog(
|
||||
client = Insights(
|
||||
'phc_x',
|
||||
host='https://eu.i.posthog.com',
|
||||
host='https://eu.i.insights.hanzo.ai',
|
||||
debug=True,
|
||||
enable_exception_autocapture=True,
|
||||
capture_exception_code_variables=True,
|
||||
@@ -118,29 +118,29 @@ def test_code_variables_capture(tmpdir):
|
||||
assert b"'my_bool': 'True'" in output
|
||||
assert b'"my_dict": "{\\"name\\": \\"test\\", \\"value\\": 123}"' in output
|
||||
assert (
|
||||
b'{\\"safe_key\\": \\"safe_value\\", \\"password\\": \\"$$_posthog_redacted_based_on_masking_rules_$$\\", \\"other_key\\": \\"$$_posthog_redacted_based_on_masking_rules_$$\\"}'
|
||||
b'{\\"safe_key\\": \\"safe_value\\", \\"password\\": \\"$$_insights_redacted_based_on_masking_rules_$$\\", \\"other_key\\": \\"$$_insights_redacted_based_on_masking_rules_$$\\"}'
|
||||
in output
|
||||
)
|
||||
assert (
|
||||
b'{\\"level1\\": {\\"level2\\": {\\"api_key\\": \\"$$_posthog_redacted_based_on_masking_rules_$$\\", \\"data\\": \\"$$_posthog_redacted_based_on_masking_rules_$$\\", \\"safe\\": \\"visible\\"}}}'
|
||||
b'{\\"level1\\": {\\"level2\\": {\\"api_key\\": \\"$$_insights_redacted_based_on_masking_rules_$$\\", \\"data\\": \\"$$_insights_redacted_based_on_masking_rules_$$\\", \\"safe\\": \\"visible\\"}}}'
|
||||
in output
|
||||
)
|
||||
assert (
|
||||
b'[\\"safe_item\\", \\"$$_posthog_redacted_based_on_masking_rules_$$\\", \\"another_safe\\"]'
|
||||
b'[\\"safe_item\\", \\"$$_insights_redacted_based_on_masking_rules_$$\\", \\"another_safe\\"]'
|
||||
in output
|
||||
)
|
||||
assert (
|
||||
b'[\\"tuple_safe\\", \\"$$_posthog_redacted_based_on_masking_rules_$$\\", \\"tuple_also_safe\\"]'
|
||||
b'[\\"tuple_safe\\", \\"$$_insights_redacted_based_on_masking_rules_$$\\", \\"tuple_also_safe\\"]'
|
||||
in output
|
||||
)
|
||||
assert (
|
||||
b'[{\\"id\\": 1, \\"password\\": \\"$$_posthog_redacted_based_on_masking_rules_$$\\"}, {\\"id\\": 2, \\"value\\": \\"safe_value\\"}]'
|
||||
b'[{\\"id\\": 1, \\"password\\": \\"$$_insights_redacted_based_on_masking_rules_$$\\"}, {\\"id\\": 2, \\"value\\": \\"safe_value\\"}]'
|
||||
in output
|
||||
)
|
||||
assert b"<__main__.UnserializableObject object at" in output
|
||||
assert b"'my_password': '$$_posthog_redacted_based_on_masking_rules_$$'" in output
|
||||
assert b"'my_password': '$$_insights_redacted_based_on_masking_rules_$$'" in output
|
||||
assert (
|
||||
b"'my_innocent_var': '$$_posthog_redacted_based_on_masking_rules_$$'" in output
|
||||
b"'my_innocent_var': '$$_insights_redacted_based_on_masking_rules_$$'" in output
|
||||
)
|
||||
assert b"'__should_be_ignored':" not in output
|
||||
|
||||
@@ -161,11 +161,11 @@ def test_code_variables_context_override(tmpdir):
|
||||
"""
|
||||
import os
|
||||
import hanzo_insights
|
||||
from hanzo_insights import Posthog
|
||||
from hanzo_insights import Insights
|
||||
|
||||
posthog_client = Posthog(
|
||||
insights_client = Insights(
|
||||
'phc_x',
|
||||
host='https://eu.i.posthog.com',
|
||||
host='https://eu.i.insights.hanzo.ai',
|
||||
debug=True,
|
||||
enable_exception_autocapture=True,
|
||||
capture_exception_code_variables=False,
|
||||
@@ -178,7 +178,7 @@ def test_code_variables_context_override(tmpdir):
|
||||
|
||||
1/0
|
||||
|
||||
with hanzo_insights.new_context(client=posthog_client):
|
||||
with hanzo_insights.new_context(client=insights_client):
|
||||
hanzo_insights.set_capture_exception_code_variables_context(True)
|
||||
hanzo_insights.set_code_variables_mask_patterns_context([r"(?i).*bank.*"])
|
||||
hanzo_insights.set_code_variables_ignore_patterns_context([])
|
||||
@@ -195,7 +195,7 @@ def test_code_variables_context_override(tmpdir):
|
||||
|
||||
assert b"ZeroDivisionError" in output
|
||||
assert b"code_variables" in output
|
||||
assert b"'bank': '$$_posthog_redacted_based_on_masking_rules_$$'" in output
|
||||
assert b"'bank': '$$_insights_redacted_based_on_masking_rules_$$'" in output
|
||||
assert b"'__dunder_var': 'should_be_visible'" in output
|
||||
|
||||
|
||||
@@ -205,11 +205,11 @@ def test_code_variables_size_limiter(tmpdir):
|
||||
dedent(
|
||||
"""
|
||||
import os
|
||||
from hanzo_insights import Posthog
|
||||
from hanzo_insights import Insights
|
||||
|
||||
posthog = Posthog(
|
||||
client = Insights(
|
||||
'phc_x',
|
||||
host='https://eu.i.posthog.com',
|
||||
host='https://eu.i.insights.hanzo.ai',
|
||||
debug=True,
|
||||
enable_exception_autocapture=True,
|
||||
capture_exception_code_variables=True,
|
||||
@@ -299,11 +299,11 @@ def test_code_variables_disabled_capture(tmpdir):
|
||||
dedent(
|
||||
"""
|
||||
import os
|
||||
from hanzo_insights import Posthog
|
||||
from hanzo_insights import Insights
|
||||
|
||||
posthog = Posthog(
|
||||
client = Insights(
|
||||
'phc_x',
|
||||
host='https://eu.i.posthog.com',
|
||||
host='https://eu.i.insights.hanzo.ai',
|
||||
debug=True,
|
||||
enable_exception_autocapture=True,
|
||||
capture_exception_code_variables=False,
|
||||
@@ -341,11 +341,11 @@ def test_code_variables_enabled_then_disabled_in_context(tmpdir):
|
||||
"""
|
||||
import os
|
||||
import hanzo_insights
|
||||
from hanzo_insights import Posthog
|
||||
from hanzo_insights import Insights
|
||||
|
||||
posthog_client = Posthog(
|
||||
insights_client = Insights(
|
||||
'phc_x',
|
||||
host='https://eu.i.posthog.com',
|
||||
host='https://eu.i.insights.hanzo.ai',
|
||||
debug=True,
|
||||
enable_exception_autocapture=True,
|
||||
capture_exception_code_variables=True,
|
||||
@@ -358,7 +358,7 @@ def test_code_variables_enabled_then_disabled_in_context(tmpdir):
|
||||
|
||||
1/0
|
||||
|
||||
with hanzo_insights.new_context(client=posthog_client):
|
||||
with hanzo_insights.new_context(client=insights_client):
|
||||
hanzo_insights.set_capture_exception_code_variables_context(False)
|
||||
|
||||
process_data()
|
||||
@@ -388,15 +388,15 @@ def test_code_variables_repr_fallback(tmpdir):
|
||||
from datetime import datetime, timedelta
|
||||
from decimal import Decimal
|
||||
from fractions import Fraction
|
||||
from hanzo_insights import Posthog
|
||||
from hanzo_insights import Insights
|
||||
|
||||
class CustomReprClass:
|
||||
def __repr__(self):
|
||||
return '<CustomReprClass: custom representation>'
|
||||
|
||||
posthog = Posthog(
|
||||
client = Insights(
|
||||
'phc_x',
|
||||
host='https://eu.i.posthog.com',
|
||||
host='https://eu.i.insights.hanzo.ai',
|
||||
debug=True,
|
||||
enable_exception_autocapture=True,
|
||||
capture_exception_code_variables=True,
|
||||
@@ -458,11 +458,11 @@ def test_code_variables_too_long_string_value_replaced(tmpdir):
|
||||
dedent(
|
||||
"""
|
||||
import os
|
||||
from hanzo_insights import Posthog
|
||||
from hanzo_insights import Insights
|
||||
|
||||
posthog = Posthog(
|
||||
client = Insights(
|
||||
'phc_x',
|
||||
host='https://eu.i.posthog.com',
|
||||
host='https://eu.i.insights.hanzo.ai',
|
||||
debug=True,
|
||||
enable_exception_autocapture=True,
|
||||
capture_exception_code_variables=True,
|
||||
@@ -491,9 +491,9 @@ def test_code_variables_too_long_string_value_replaced(tmpdir):
|
||||
|
||||
assert "'short_value': 'I am short'" in output
|
||||
|
||||
assert "$$_posthog_value_too_long_$$" in output
|
||||
assert "$$_insights_value_too_long_$$" in output
|
||||
|
||||
assert "'long_blob': '$$_posthog_value_too_long_$$'" in output
|
||||
assert "'long_blob': '$$_insights_value_too_long_$$'" in output
|
||||
|
||||
|
||||
def test_code_variables_too_long_string_in_nested_dict(tmpdir):
|
||||
@@ -502,11 +502,11 @@ def test_code_variables_too_long_string_in_nested_dict(tmpdir):
|
||||
dedent(
|
||||
"""
|
||||
import os
|
||||
from hanzo_insights import Posthog
|
||||
from hanzo_insights import Insights
|
||||
|
||||
posthog = Posthog(
|
||||
client = Insights(
|
||||
'phc_x',
|
||||
host='https://eu.i.posthog.com',
|
||||
host='https://eu.i.insights.hanzo.ai',
|
||||
debug=True,
|
||||
enable_exception_autocapture=True,
|
||||
capture_exception_code_variables=True,
|
||||
@@ -541,7 +541,7 @@ def test_code_variables_too_long_string_in_nested_dict(tmpdir):
|
||||
assert "short_val" in output
|
||||
assert "ok" in output
|
||||
|
||||
assert "$$_posthog_value_too_long_$$" in output
|
||||
assert "$$_insights_value_too_long_$$" in output
|
||||
assert "y" * 1000 not in output
|
||||
assert "z" * 1000 not in output
|
||||
|
||||
@@ -567,7 +567,7 @@ def test_mask_sensitive_data_too_long_dict_key():
|
||||
assert result["short"] == "visible"
|
||||
# This then gets shortened by the JSON truncation at 1024 chars anyways so no worries
|
||||
assert result["k" * 20000] == CODE_VARIABLES_TOO_LONG_VALUE
|
||||
assert result["password"] == "$$_posthog_redacted_based_on_masking_rules_$$"
|
||||
assert result["password"] == "$$_insights_redacted_based_on_masking_rules_$$"
|
||||
|
||||
|
||||
def test_mask_sensitive_data_circular_ref():
|
||||
|
||||
@@ -712,7 +712,7 @@ class TestGetFeatureFlagResult(unittest.TestCase):
|
||||
class TestFeatureFlagErrorWithStaleCacheFallback(unittest.TestCase):
|
||||
"""Tests for stale cache fallback behavior when flag evaluation fails.
|
||||
|
||||
When the PostHog API is unavailable (timeout, connection error, etc.), the SDK
|
||||
When the Insights API is unavailable (timeout, connection error, etc.), the SDK
|
||||
falls back to stale cached flag values if available. These tests verify that:
|
||||
1. The stale cached value is returned when an error occurs
|
||||
2. The $feature_flag_error property is still set (for debugging)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""
|
||||
Tests for FlagDefinitionCacheProvider functionality.
|
||||
|
||||
These tests follow the patterns from the TypeScript implementation in posthog-js/packages/node.
|
||||
These tests follow the patterns from the TypeScript implementation in insights-js/packages/node.
|
||||
"""
|
||||
|
||||
import threading
|
||||
|
||||
@@ -339,13 +339,13 @@ class TestGet(unittest.TestCase):
|
||||
("https://us.posthog.com.rg.proxy.com", "https://us.posthog.com.rg.proxy.com"),
|
||||
("app.posthog.com", "app.posthog.com"),
|
||||
("eu.posthog.com", "eu.posthog.com"),
|
||||
("https://app.posthog.com", "https://us.i.posthog.com"),
|
||||
("https://eu.posthog.com", "https://eu.i.posthog.com"),
|
||||
("https://us.posthog.com", "https://us.i.posthog.com"),
|
||||
("https://app.posthog.com/", "https://us.i.posthog.com"),
|
||||
("https://eu.posthog.com/", "https://eu.i.posthog.com"),
|
||||
("https://us.posthog.com/", "https://us.i.posthog.com"),
|
||||
(None, "https://us.i.posthog.com"),
|
||||
("https://app.posthog.com", "https://us.i.insights.hanzo.ai"),
|
||||
("https://eu.posthog.com", "https://eu.i.insights.hanzo.ai"),
|
||||
("https://us.posthog.com", "https://us.i.insights.hanzo.ai"),
|
||||
("https://app.posthog.com/", "https://us.i.insights.hanzo.ai"),
|
||||
("https://eu.posthog.com/", "https://eu.i.insights.hanzo.ai"),
|
||||
("https://us.posthog.com/", "https://us.i.insights.hanzo.ai"),
|
||||
(None, "https://us.i.insights.hanzo.ai"),
|
||||
],
|
||||
)
|
||||
def test_routing_to_custom_host(host, expected):
|
||||
|
||||
@@ -277,7 +277,7 @@ class FlagCache:
|
||||
|
||||
class RedisFlagCache:
|
||||
def __init__(
|
||||
self, redis_client, default_ttl=300, stale_ttl=3600, key_prefix="posthog:flags:"
|
||||
self, redis_client, default_ttl=300, stale_ttl=3600, key_prefix="insights:flags:"
|
||||
):
|
||||
self.redis = redis_client
|
||||
self.default_ttl = default_ttl
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
# Convenience re-export so `from insights import Insights` works.
|
||||
from hanzo_insights import * # noqa: F401, F403
|
||||
from hanzo_insights import Insights, Posthog, Client # noqa: F401
|
||||
from hanzo_insights import Insights, Client # noqa: F401
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
"""
|
||||
Test that verifies exception capture functionality.
|
||||
|
||||
These tests verify that exceptions are actually captured to PostHog, not just that
|
||||
These tests verify that exceptions are actually captured to Insights, not just that
|
||||
500 responses are returned.
|
||||
|
||||
Without process_exception(), view exceptions are NOT captured to PostHog (v6.7.11 and earlier).
|
||||
Without process_exception(), view exceptions are NOT captured to Insights (v6.7.11 and earlier).
|
||||
With process_exception(), Django calls this method to capture exceptions before
|
||||
converting them to 500 responses.
|
||||
"""
|
||||
@@ -30,7 +30,7 @@ def asgi_app():
|
||||
@pytest.mark.asyncio
|
||||
async def test_async_exception_is_captured(asgi_app):
|
||||
"""
|
||||
Test that async view exceptions are captured to PostHog.
|
||||
Test that async view exceptions are captured to Insights.
|
||||
|
||||
The middleware's process_exception() method ensures exceptions are captured.
|
||||
Without it (v6.7.11 and earlier), exceptions are NOT captured even though 500 is returned.
|
||||
@@ -50,7 +50,7 @@ async def test_async_exception_is_captured(asgi_app):
|
||||
}
|
||||
)
|
||||
|
||||
# Patch at the posthog module level where middleware imports from
|
||||
# Patch at the hanzo_insights module level where middleware imports from
|
||||
with patch("hanzo_insights.capture_exception", side_effect=mock_capture):
|
||||
async with AsyncClient(
|
||||
transport=ASGITransport(app=asgi_app), base_url="http://testserver"
|
||||
@@ -60,8 +60,8 @@ async def test_async_exception_is_captured(asgi_app):
|
||||
# Django returns 500
|
||||
assert response.status_code == 500
|
||||
|
||||
# CRITICAL: Verify PostHog captured the exception
|
||||
assert len(captured) > 0, "Exception was NOT captured to PostHog!"
|
||||
# CRITICAL: Verify Insights captured the exception
|
||||
assert len(captured) > 0, "Exception was NOT captured to Insights!"
|
||||
|
||||
# Verify it's the right exception
|
||||
exception_data = captured[0]
|
||||
@@ -72,7 +72,7 @@ async def test_async_exception_is_captured(asgi_app):
|
||||
@pytest.mark.asyncio
|
||||
async def test_sync_exception_is_captured(asgi_app):
|
||||
"""
|
||||
Test that sync view exceptions are captured to PostHog.
|
||||
Test that sync view exceptions are captured to Insights.
|
||||
|
||||
The middleware's process_exception() method ensures exceptions are captured.
|
||||
Without it (v6.7.11 and earlier), exceptions are NOT captured even though 500 is returned.
|
||||
@@ -92,7 +92,7 @@ async def test_sync_exception_is_captured(asgi_app):
|
||||
}
|
||||
)
|
||||
|
||||
# Patch at the posthog module level where middleware imports from
|
||||
# Patch at the hanzo_insights module level where middleware imports from
|
||||
with patch("hanzo_insights.capture_exception", side_effect=mock_capture):
|
||||
async with AsyncClient(
|
||||
transport=ASGITransport(app=asgi_app), base_url="http://testserver"
|
||||
@@ -102,8 +102,8 @@ async def test_sync_exception_is_captured(asgi_app):
|
||||
# Django returns 500
|
||||
assert response.status_code == 500
|
||||
|
||||
# CRITICAL: Verify PostHog captured the exception
|
||||
assert len(captured) > 0, "Exception was NOT captured to PostHog!"
|
||||
# CRITICAL: Verify Insights captured the exception
|
||||
assert len(captured) > 0, "Exception was NOT captured to Insights!"
|
||||
|
||||
# Verify it's the right exception
|
||||
exception_data = captured[0]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
Tests for PostHog Django middleware in async context.
|
||||
Tests for Insights Django middleware in async context.
|
||||
|
||||
These tests verify that the middleware correctly handles:
|
||||
1. Async user access (request.auser() in Django 5)
|
||||
@@ -103,7 +103,7 @@ async def test_async_authenticated_user_access(asgi_app):
|
||||
|
||||
# Make request with session cookie - this should trigger the bug in v6.7.11
|
||||
# Disable exception capture to see the SynchronousOnlyOperation clearly
|
||||
with override_settings(POSTHOG_MW_CAPTURE_EXCEPTIONS=False):
|
||||
with override_settings(INSIGHTS_MW_CAPTURE_EXCEPTIONS=False):
|
||||
async with AsyncClient(
|
||||
transport=ASGITransport(app=asgi_app),
|
||||
base_url="http://testserver",
|
||||
@@ -139,10 +139,10 @@ async def test_async_exception_capture(asgi_app):
|
||||
"""
|
||||
Test that middleware handles exceptions from async views.
|
||||
|
||||
The middleware's process_exception() method captures view exceptions to PostHog
|
||||
The middleware's process_exception() method captures view exceptions to Insights
|
||||
before Django converts them to 500 responses. This test verifies the exception
|
||||
causes a 500 response. See test_exception_capture.py for tests that verify
|
||||
actual exception capture to PostHog.
|
||||
actual exception capture to Insights.
|
||||
"""
|
||||
async with AsyncClient(
|
||||
transport=ASGITransport(app=asgi_app), base_url="http://testserver"
|
||||
@@ -158,7 +158,7 @@ async def test_sync_exception_capture(asgi_app):
|
||||
"""
|
||||
Test that middleware handles exceptions from sync views.
|
||||
|
||||
The middleware's process_exception() method captures view exceptions to PostHog.
|
||||
The middleware's process_exception() method captures view exceptions to Insights.
|
||||
This test verifies the exception causes a 500 response.
|
||||
"""
|
||||
async with AsyncClient(
|
||||
|
||||
@@ -47,7 +47,7 @@ MIDDLEWARE = [
|
||||
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||
"django.contrib.messages.middleware.MessageMiddleware",
|
||||
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||
"hanzo_insights.integrations.django.PosthogContextMiddleware", # Test PostHog middleware
|
||||
"hanzo_insights.integrations.django.InsightsContextMiddleware", # Test Insights middleware
|
||||
]
|
||||
|
||||
ROOT_URLCONF = "testdjango.urls"
|
||||
@@ -123,7 +123,7 @@ STATIC_URL = "static/"
|
||||
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
|
||||
|
||||
|
||||
# PostHog settings for testing
|
||||
POSTHOG_API_KEY = "test-key"
|
||||
# Insights settings for testing
|
||||
INSIGHTS_API_KEY = "test-key"
|
||||
POSTHOG_HOST = "https://app.posthog.com"
|
||||
POSTHOG_MW_CAPTURE_EXCEPTIONS = True
|
||||
INSIGHTS_MW_CAPTURE_EXCEPTIONS = True
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
Test views for validating PostHog middleware with Django 5 ASGI.
|
||||
Test views for validating Insights middleware with Django 5 ASGI.
|
||||
"""
|
||||
|
||||
from django.http import JsonResponse
|
||||
|
||||
@@ -17,10 +17,10 @@ ignore_missing_imports = True
|
||||
[mypy-sentry_sdk.*]
|
||||
ignore_missing_imports = True
|
||||
|
||||
[mypy-posthog.test.*]
|
||||
[mypy-hanzo_insights.test.*]
|
||||
ignore_errors = True
|
||||
|
||||
[mypy-posthog.*.test.*]
|
||||
[mypy-hanzo_insights.*.test.*]
|
||||
ignore_errors = True
|
||||
|
||||
[mypy-openai.*]
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
"""
|
||||
PostHog Python SDK Test Adapter
|
||||
Insights Python SDK Test Adapter
|
||||
|
||||
This adapter implements the SDK Test Adapter Interface defined in the PostHog Capture API Contract.
|
||||
It wraps the posthog-python SDK and exposes a REST API for the test harness to exercise.
|
||||
This adapter implements the SDK Test Adapter Interface defined in the Capture API Contract.
|
||||
It wraps the insights-python SDK and exposes a REST API for the test harness to exercise.
|
||||
"""
|
||||
|
||||
import logging
|
||||
@@ -13,7 +13,7 @@ from typing import Any, Dict, List, Optional
|
||||
|
||||
from flask import Flask, jsonify, request
|
||||
|
||||
from posthog import Client
|
||||
from hanzo_insights import Client
|
||||
from hanzo_insights.request import batch_post as original_batch_post
|
||||
from hanzo_insights.version import VERSION
|
||||
|
||||
@@ -193,7 +193,7 @@ def health():
|
||||
"""Health check endpoint"""
|
||||
return jsonify(
|
||||
{
|
||||
"sdk_name": "posthog-python",
|
||||
"sdk_name": "insights-python",
|
||||
"sdk_version": VERSION,
|
||||
"adapter_version": "1.0.0",
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user