posthoganalytics 6.7.1__tar.gz → 6.7.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. {posthoganalytics-6.7.1/posthoganalytics.egg-info → posthoganalytics-6.7.3}/PKG-INFO +1 -1
  2. {posthoganalytics-6.7.1 → posthoganalytics-6.7.3}/posthoganalytics/ai/anthropic/__init__.py +10 -0
  3. {posthoganalytics-6.7.1 → posthoganalytics-6.7.3}/posthoganalytics/ai/anthropic/anthropic.py +94 -65
  4. {posthoganalytics-6.7.1 → posthoganalytics-6.7.3}/posthoganalytics/ai/anthropic/anthropic_async.py +88 -22
  5. posthoganalytics-6.7.3/posthoganalytics/ai/anthropic/anthropic_converter.py +403 -0
  6. posthoganalytics-6.7.3/posthoganalytics/ai/gemini/__init__.py +22 -0
  7. {posthoganalytics-6.7.1 → posthoganalytics-6.7.3}/posthoganalytics/ai/gemini/gemini.py +63 -69
  8. posthoganalytics-6.7.3/posthoganalytics/ai/gemini/gemini_converter.py +460 -0
  9. posthoganalytics-6.7.3/posthoganalytics/ai/openai/__init__.py +20 -0
  10. {posthoganalytics-6.7.1 → posthoganalytics-6.7.3}/posthoganalytics/ai/openai/openai.py +114 -155
  11. {posthoganalytics-6.7.1 → posthoganalytics-6.7.3}/posthoganalytics/ai/openai/openai_async.py +76 -82
  12. posthoganalytics-6.7.3/posthoganalytics/ai/openai/openai_converter.py +612 -0
  13. posthoganalytics-6.7.3/posthoganalytics/ai/types.py +124 -0
  14. posthoganalytics-6.7.3/posthoganalytics/ai/utils.py +552 -0
  15. {posthoganalytics-6.7.1 → posthoganalytics-6.7.3}/posthoganalytics/client.py +7 -7
  16. {posthoganalytics-6.7.1 → posthoganalytics-6.7.3}/posthoganalytics/test/test_feature_flags.py +2 -2
  17. {posthoganalytics-6.7.1 → posthoganalytics-6.7.3}/posthoganalytics/version.py +1 -1
  18. {posthoganalytics-6.7.1 → posthoganalytics-6.7.3/posthoganalytics.egg-info}/PKG-INFO +1 -1
  19. {posthoganalytics-6.7.1 → posthoganalytics-6.7.3}/posthoganalytics.egg-info/SOURCES.txt +4 -0
  20. posthoganalytics-6.7.1/posthoganalytics/ai/gemini/__init__.py +0 -11
  21. posthoganalytics-6.7.1/posthoganalytics/ai/openai/__init__.py +0 -5
  22. posthoganalytics-6.7.1/posthoganalytics/ai/utils.py +0 -631
  23. {posthoganalytics-6.7.1 → posthoganalytics-6.7.3}/LICENSE +0 -0
  24. {posthoganalytics-6.7.1 → posthoganalytics-6.7.3}/MANIFEST.in +0 -0
  25. {posthoganalytics-6.7.1 → posthoganalytics-6.7.3}/README.md +0 -0
  26. {posthoganalytics-6.7.1 → posthoganalytics-6.7.3}/posthoganalytics/__init__.py +0 -0
  27. {posthoganalytics-6.7.1 → posthoganalytics-6.7.3}/posthoganalytics/ai/__init__.py +0 -0
  28. {posthoganalytics-6.7.1 → posthoganalytics-6.7.3}/posthoganalytics/ai/anthropic/anthropic_providers.py +0 -0
  29. {posthoganalytics-6.7.1 → posthoganalytics-6.7.3}/posthoganalytics/ai/langchain/__init__.py +0 -0
  30. {posthoganalytics-6.7.1 → posthoganalytics-6.7.3}/posthoganalytics/ai/langchain/callbacks.py +0 -0
  31. {posthoganalytics-6.7.1 → posthoganalytics-6.7.3}/posthoganalytics/ai/openai/openai_providers.py +0 -0
  32. {posthoganalytics-6.7.1 → posthoganalytics-6.7.3}/posthoganalytics/ai/sanitization.py +0 -0
  33. {posthoganalytics-6.7.1 → posthoganalytics-6.7.3}/posthoganalytics/args.py +0 -0
  34. {posthoganalytics-6.7.1 → posthoganalytics-6.7.3}/posthoganalytics/consumer.py +0 -0
  35. {posthoganalytics-6.7.1 → posthoganalytics-6.7.3}/posthoganalytics/contexts.py +0 -0
  36. {posthoganalytics-6.7.1 → posthoganalytics-6.7.3}/posthoganalytics/exception_capture.py +0 -0
  37. {posthoganalytics-6.7.1 → posthoganalytics-6.7.3}/posthoganalytics/exception_utils.py +0 -0
  38. {posthoganalytics-6.7.1 → posthoganalytics-6.7.3}/posthoganalytics/feature_flags.py +0 -0
  39. {posthoganalytics-6.7.1 → posthoganalytics-6.7.3}/posthoganalytics/integrations/__init__.py +0 -0
  40. {posthoganalytics-6.7.1 → posthoganalytics-6.7.3}/posthoganalytics/integrations/django.py +0 -0
  41. {posthoganalytics-6.7.1 → posthoganalytics-6.7.3}/posthoganalytics/poller.py +0 -0
  42. {posthoganalytics-6.7.1 → posthoganalytics-6.7.3}/posthoganalytics/py.typed +0 -0
  43. {posthoganalytics-6.7.1 → posthoganalytics-6.7.3}/posthoganalytics/request.py +0 -0
  44. {posthoganalytics-6.7.1 → posthoganalytics-6.7.3}/posthoganalytics/test/__init__.py +0 -0
  45. {posthoganalytics-6.7.1 → posthoganalytics-6.7.3}/posthoganalytics/test/test_before_send.py +0 -0
  46. {posthoganalytics-6.7.1 → posthoganalytics-6.7.3}/posthoganalytics/test/test_client.py +0 -0
  47. {posthoganalytics-6.7.1 → posthoganalytics-6.7.3}/posthoganalytics/test/test_consumer.py +0 -0
  48. {posthoganalytics-6.7.1 → posthoganalytics-6.7.3}/posthoganalytics/test/test_contexts.py +0 -0
  49. {posthoganalytics-6.7.1 → posthoganalytics-6.7.3}/posthoganalytics/test/test_exception_capture.py +0 -0
  50. {posthoganalytics-6.7.1 → posthoganalytics-6.7.3}/posthoganalytics/test/test_feature_flag.py +0 -0
  51. {posthoganalytics-6.7.1 → posthoganalytics-6.7.3}/posthoganalytics/test/test_feature_flag_result.py +0 -0
  52. {posthoganalytics-6.7.1 → posthoganalytics-6.7.3}/posthoganalytics/test/test_module.py +0 -0
  53. {posthoganalytics-6.7.1 → posthoganalytics-6.7.3}/posthoganalytics/test/test_request.py +0 -0
  54. {posthoganalytics-6.7.1 → posthoganalytics-6.7.3}/posthoganalytics/test/test_size_limited_dict.py +0 -0
  55. {posthoganalytics-6.7.1 → posthoganalytics-6.7.3}/posthoganalytics/test/test_types.py +0 -0
  56. {posthoganalytics-6.7.1 → posthoganalytics-6.7.3}/posthoganalytics/test/test_utils.py +0 -0
  57. {posthoganalytics-6.7.1 → posthoganalytics-6.7.3}/posthoganalytics/types.py +0 -0
  58. {posthoganalytics-6.7.1 → posthoganalytics-6.7.3}/posthoganalytics/utils.py +0 -0
  59. {posthoganalytics-6.7.1 → posthoganalytics-6.7.3}/posthoganalytics.egg-info/dependency_links.txt +0 -0
  60. {posthoganalytics-6.7.1 → posthoganalytics-6.7.3}/posthoganalytics.egg-info/requires.txt +0 -0
  61. {posthoganalytics-6.7.1 → posthoganalytics-6.7.3}/posthoganalytics.egg-info/top_level.txt +0 -0
  62. {posthoganalytics-6.7.1 → posthoganalytics-6.7.3}/pyproject.toml +0 -0
  63. {posthoganalytics-6.7.1 → posthoganalytics-6.7.3}/setup.cfg +0 -0
  64. {posthoganalytics-6.7.1 → posthoganalytics-6.7.3}/setup.py +0 -0
  65. {posthoganalytics-6.7.1 → posthoganalytics-6.7.3}/setup_analytics.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: posthoganalytics
3
- Version: 6.7.1
3
+ Version: 6.7.3
4
4
  Summary: Integrate PostHog into any python application.
5
5
  Home-page: https://github.com/posthog/posthog-python
6
6
  Author: Posthog
@@ -6,6 +6,12 @@ from .anthropic_providers import (
6
6
  AsyncAnthropicBedrock,
7
7
  AsyncAnthropicVertex,
8
8
  )
9
+ from .anthropic_converter import (
10
+ format_anthropic_response,
11
+ format_anthropic_input,
12
+ extract_anthropic_tools,
13
+ format_anthropic_streaming_content,
14
+ )
9
15
 
10
16
  __all__ = [
11
17
  "Anthropic",
@@ -14,4 +20,8 @@ __all__ = [
14
20
  "AsyncAnthropicBedrock",
15
21
  "AnthropicVertex",
16
22
  "AsyncAnthropicVertex",
23
+ "format_anthropic_response",
24
+ "format_anthropic_input",
25
+ "extract_anthropic_tools",
26
+ "format_anthropic_streaming_content",
17
27
  ]
@@ -8,13 +8,19 @@ except ImportError:
8
8
 
9
9
  import time
10
10
  import uuid
11
- from typing import Any, Dict, Optional
11
+ from typing import Any, Dict, List, Optional
12
12
 
13
+ from posthoganalytics.ai.types import StreamingContentBlock, TokenUsage, ToolInProgress
13
14
  from posthoganalytics.ai.utils import (
14
15
  call_llm_and_track_usage,
15
- get_model_params,
16
- merge_system_prompt,
17
- with_privacy_mode,
16
+ merge_usage_stats,
17
+ )
18
+ from posthoganalytics.ai.anthropic.anthropic_converter import (
19
+ extract_anthropic_usage_from_event,
20
+ handle_anthropic_content_block_start,
21
+ handle_anthropic_text_delta,
22
+ handle_anthropic_tool_delta,
23
+ finalize_anthropic_tool_input,
18
24
  )
19
25
  from posthoganalytics.ai.sanitization import sanitize_anthropic
20
26
  from posthoganalytics.client import Client as PostHogClient
@@ -62,6 +68,7 @@ class WrappedMessages(Messages):
62
68
  posthog_groups: Optional group analytics properties
63
69
  **kwargs: Arguments passed to Anthropic's messages.create
64
70
  """
71
+
65
72
  if posthog_trace_id is None:
66
73
  posthog_trace_id = str(uuid.uuid4())
67
74
 
@@ -119,35 +126,66 @@ class WrappedMessages(Messages):
119
126
  **kwargs: Any,
120
127
  ):
121
128
  start_time = time.time()
122
- usage_stats: Dict[str, int] = {"input_tokens": 0, "output_tokens": 0}
123
- accumulated_content = []
129
+ usage_stats: TokenUsage = TokenUsage(input_tokens=0, output_tokens=0)
130
+ accumulated_content = ""
131
+ content_blocks: List[StreamingContentBlock] = []
132
+ tools_in_progress: Dict[str, ToolInProgress] = {}
133
+ current_text_block: Optional[StreamingContentBlock] = None
124
134
  response = super().create(**kwargs)
125
135
 
126
136
  def generator():
127
137
  nonlocal usage_stats
128
- nonlocal accumulated_content # noqa: F824
138
+ nonlocal accumulated_content
139
+ nonlocal content_blocks
140
+ nonlocal tools_in_progress
141
+ nonlocal current_text_block
142
+
129
143
  try:
130
144
  for event in response:
131
- if hasattr(event, "usage") and event.usage:
132
- usage_stats = {
133
- k: getattr(event.usage, k, 0)
134
- for k in [
135
- "input_tokens",
136
- "output_tokens",
137
- "cache_read_input_tokens",
138
- "cache_creation_input_tokens",
139
- ]
140
- }
141
-
142
- if hasattr(event, "content") and event.content:
143
- accumulated_content.append(event.content)
145
+ # Extract usage stats from event
146
+ event_usage = extract_anthropic_usage_from_event(event)
147
+ merge_usage_stats(usage_stats, event_usage)
148
+
149
+ # Handle content block start events
150
+ if hasattr(event, "type") and event.type == "content_block_start":
151
+ block, tool = handle_anthropic_content_block_start(event)
152
+
153
+ if block:
154
+ content_blocks.append(block)
155
+
156
+ if block.get("type") == "text":
157
+ current_text_block = block
158
+ else:
159
+ current_text_block = None
160
+
161
+ if tool:
162
+ tool_id = tool["block"].get("id")
163
+ if tool_id:
164
+ tools_in_progress[tool_id] = tool
165
+
166
+ # Handle text delta events
167
+ delta_text = handle_anthropic_text_delta(event, current_text_block)
168
+
169
+ if delta_text:
170
+ accumulated_content += delta_text
171
+
172
+ # Handle tool input delta events
173
+ handle_anthropic_tool_delta(
174
+ event, content_blocks, tools_in_progress
175
+ )
176
+
177
+ # Handle content block stop events
178
+ if hasattr(event, "type") and event.type == "content_block_stop":
179
+ current_text_block = None
180
+ finalize_anthropic_tool_input(
181
+ event, content_blocks, tools_in_progress
182
+ )
144
183
 
145
184
  yield event
146
185
 
147
186
  finally:
148
187
  end_time = time.time()
149
188
  latency = end_time - start_time
150
- output = "".join(accumulated_content)
151
189
 
152
190
  self._capture_streaming_event(
153
191
  posthog_distinct_id,
@@ -158,7 +196,8 @@ class WrappedMessages(Messages):
158
196
  kwargs,
159
197
  usage_stats,
160
198
  latency,
161
- output,
199
+ content_blocks,
200
+ accumulated_content,
162
201
  )
163
202
 
164
203
  return generator()
@@ -171,49 +210,39 @@ class WrappedMessages(Messages):
171
210
  posthog_privacy_mode: bool,
172
211
  posthog_groups: Optional[Dict[str, Any]],
173
212
  kwargs: Dict[str, Any],
174
- usage_stats: Dict[str, int],
213
+ usage_stats: TokenUsage,
175
214
  latency: float,
176
- output: str,
215
+ content_blocks: List[StreamingContentBlock],
216
+ accumulated_content: str,
177
217
  ):
178
- if posthog_trace_id is None:
179
- posthog_trace_id = str(uuid.uuid4())
180
-
181
- event_properties = {
182
- "$ai_provider": "anthropic",
183
- "$ai_model": kwargs.get("model"),
184
- "$ai_model_parameters": get_model_params(kwargs),
185
- "$ai_input": with_privacy_mode(
186
- self._client._ph_client,
187
- posthog_privacy_mode,
188
- sanitize_anthropic(merge_system_prompt(kwargs, "anthropic")),
189
- ),
190
- "$ai_output_choices": with_privacy_mode(
191
- self._client._ph_client,
192
- posthog_privacy_mode,
193
- [{"content": output, "role": "assistant"}],
194
- ),
195
- "$ai_http_status": 200,
196
- "$ai_input_tokens": usage_stats.get("input_tokens", 0),
197
- "$ai_output_tokens": usage_stats.get("output_tokens", 0),
198
- "$ai_cache_read_input_tokens": usage_stats.get(
199
- "cache_read_input_tokens", 0
200
- ),
201
- "$ai_cache_creation_input_tokens": usage_stats.get(
202
- "cache_creation_input_tokens", 0
218
+ from posthoganalytics.ai.types import StreamingEventData
219
+ from posthoganalytics.ai.anthropic.anthropic_converter import (
220
+ format_anthropic_streaming_input,
221
+ format_anthropic_streaming_output_complete,
222
+ )
223
+ from posthoganalytics.ai.utils import capture_streaming_event
224
+
225
+ # Prepare standardized event data
226
+ formatted_input = format_anthropic_streaming_input(kwargs)
227
+ sanitized_input = sanitize_anthropic(formatted_input)
228
+
229
+ event_data = StreamingEventData(
230
+ provider="anthropic",
231
+ model=kwargs.get("model", "unknown"),
232
+ base_url=str(self._client.base_url),
233
+ kwargs=kwargs,
234
+ formatted_input=sanitized_input,
235
+ formatted_output=format_anthropic_streaming_output_complete(
236
+ content_blocks, accumulated_content
203
237
  ),
204
- "$ai_latency": latency,
205
- "$ai_trace_id": posthog_trace_id,
206
- "$ai_base_url": str(self._client.base_url),
207
- **(posthog_properties or {}),
208
- }
209
-
210
- if posthog_distinct_id is None:
211
- event_properties["$process_person_profile"] = False
212
-
213
- if hasattr(self._client._ph_client, "capture"):
214
- self._client._ph_client.capture(
215
- distinct_id=posthog_distinct_id or posthog_trace_id,
216
- event="$ai_generation",
217
- properties=event_properties,
218
- groups=posthog_groups,
219
- )
238
+ usage_stats=usage_stats,
239
+ latency=latency,
240
+ distinct_id=posthog_distinct_id,
241
+ trace_id=posthog_trace_id,
242
+ properties=posthog_properties,
243
+ privacy_mode=posthog_privacy_mode,
244
+ groups=posthog_groups,
245
+ )
246
+
247
+ # Use the common capture function
248
+ capture_streaming_event(self._client._ph_client, event_data)
@@ -8,15 +8,26 @@ except ImportError:
8
8
 
9
9
  import time
10
10
  import uuid
11
- from typing import Any, Dict, Optional
11
+ from typing import Any, Dict, List, Optional
12
12
 
13
13
  from posthoganalytics import setup
14
+ from posthoganalytics.ai.types import StreamingContentBlock, TokenUsage, ToolInProgress
14
15
  from posthoganalytics.ai.utils import (
15
16
  call_llm_and_track_usage_async,
17
+ extract_available_tool_calls,
16
18
  get_model_params,
17
19
  merge_system_prompt,
20
+ merge_usage_stats,
18
21
  with_privacy_mode,
19
22
  )
23
+ from posthoganalytics.ai.anthropic.anthropic_converter import (
24
+ format_anthropic_streaming_content,
25
+ extract_anthropic_usage_from_event,
26
+ handle_anthropic_content_block_start,
27
+ handle_anthropic_text_delta,
28
+ handle_anthropic_tool_delta,
29
+ finalize_anthropic_tool_input,
30
+ )
20
31
  from posthoganalytics.ai.sanitization import sanitize_anthropic
21
32
  from posthoganalytics.client import Client as PostHogClient
22
33
 
@@ -62,6 +73,7 @@ class AsyncWrappedMessages(AsyncMessages):
62
73
  posthog_groups: Optional group analytics properties
63
74
  **kwargs: Arguments passed to Anthropic's messages.create
64
75
  """
76
+
65
77
  if posthog_trace_id is None:
66
78
  posthog_trace_id = str(uuid.uuid4())
67
79
 
@@ -119,35 +131,66 @@ class AsyncWrappedMessages(AsyncMessages):
119
131
  **kwargs: Any,
120
132
  ):
121
133
  start_time = time.time()
122
- usage_stats: Dict[str, int] = {"input_tokens": 0, "output_tokens": 0}
123
- accumulated_content = []
134
+ usage_stats: TokenUsage = TokenUsage(input_tokens=0, output_tokens=0)
135
+ accumulated_content = ""
136
+ content_blocks: List[StreamingContentBlock] = []
137
+ tools_in_progress: Dict[str, ToolInProgress] = {}
138
+ current_text_block: Optional[StreamingContentBlock] = None
124
139
  response = await super().create(**kwargs)
125
140
 
126
141
  async def generator():
127
142
  nonlocal usage_stats
128
- nonlocal accumulated_content # noqa: F824
143
+ nonlocal accumulated_content
144
+ nonlocal content_blocks
145
+ nonlocal tools_in_progress
146
+ nonlocal current_text_block
147
+
129
148
  try:
130
149
  async for event in response:
131
- if hasattr(event, "usage") and event.usage:
132
- usage_stats = {
133
- k: getattr(event.usage, k, 0)
134
- for k in [
135
- "input_tokens",
136
- "output_tokens",
137
- "cache_read_input_tokens",
138
- "cache_creation_input_tokens",
139
- ]
140
- }
141
-
142
- if hasattr(event, "content") and event.content:
143
- accumulated_content.append(event.content)
150
+ # Extract usage stats from event
151
+ event_usage = extract_anthropic_usage_from_event(event)
152
+ merge_usage_stats(usage_stats, event_usage)
153
+
154
+ # Handle content block start events
155
+ if hasattr(event, "type") and event.type == "content_block_start":
156
+ block, tool = handle_anthropic_content_block_start(event)
157
+
158
+ if block:
159
+ content_blocks.append(block)
160
+
161
+ if block.get("type") == "text":
162
+ current_text_block = block
163
+ else:
164
+ current_text_block = None
165
+
166
+ if tool:
167
+ tool_id = tool["block"].get("id")
168
+ if tool_id:
169
+ tools_in_progress[tool_id] = tool
170
+
171
+ # Handle text delta events
172
+ delta_text = handle_anthropic_text_delta(event, current_text_block)
173
+
174
+ if delta_text:
175
+ accumulated_content += delta_text
176
+
177
+ # Handle tool input delta events
178
+ handle_anthropic_tool_delta(
179
+ event, content_blocks, tools_in_progress
180
+ )
181
+
182
+ # Handle content block stop events
183
+ if hasattr(event, "type") and event.type == "content_block_stop":
184
+ current_text_block = None
185
+ finalize_anthropic_tool_input(
186
+ event, content_blocks, tools_in_progress
187
+ )
144
188
 
145
189
  yield event
146
190
 
147
191
  finally:
148
192
  end_time = time.time()
149
193
  latency = end_time - start_time
150
- output = "".join(accumulated_content)
151
194
 
152
195
  await self._capture_streaming_event(
153
196
  posthog_distinct_id,
@@ -158,7 +201,8 @@ class AsyncWrappedMessages(AsyncMessages):
158
201
  kwargs,
159
202
  usage_stats,
160
203
  latency,
161
- output,
204
+ content_blocks,
205
+ accumulated_content,
162
206
  )
163
207
 
164
208
  return generator()
@@ -171,13 +215,29 @@ class AsyncWrappedMessages(AsyncMessages):
171
215
  posthog_privacy_mode: bool,
172
216
  posthog_groups: Optional[Dict[str, Any]],
173
217
  kwargs: Dict[str, Any],
174
- usage_stats: Dict[str, int],
218
+ usage_stats: TokenUsage,
175
219
  latency: float,
176
- output: str,
220
+ content_blocks: List[StreamingContentBlock],
221
+ accumulated_content: str,
177
222
  ):
178
223
  if posthog_trace_id is None:
179
224
  posthog_trace_id = str(uuid.uuid4())
180
225
 
226
+ # Format output using converter
227
+ formatted_content = format_anthropic_streaming_content(content_blocks)
228
+ formatted_output = []
229
+
230
+ if formatted_content:
231
+ formatted_output = [{"role": "assistant", "content": formatted_content}]
232
+ else:
233
+ # Fallback to accumulated content if no blocks
234
+ formatted_output = [
235
+ {
236
+ "role": "assistant",
237
+ "content": [{"type": "text", "text": accumulated_content}],
238
+ }
239
+ ]
240
+
181
241
  event_properties = {
182
242
  "$ai_provider": "anthropic",
183
243
  "$ai_model": kwargs.get("model"),
@@ -190,7 +250,7 @@ class AsyncWrappedMessages(AsyncMessages):
190
250
  "$ai_output_choices": with_privacy_mode(
191
251
  self._client._ph_client,
192
252
  posthog_privacy_mode,
193
- [{"content": output, "role": "assistant"}],
253
+ formatted_output,
194
254
  ),
195
255
  "$ai_http_status": 200,
196
256
  "$ai_input_tokens": usage_stats.get("input_tokens", 0),
@@ -207,6 +267,12 @@ class AsyncWrappedMessages(AsyncMessages):
207
267
  **(posthog_properties or {}),
208
268
  }
209
269
 
270
+ # Add tools if available
271
+ available_tools = extract_available_tool_calls("anthropic", kwargs)
272
+
273
+ if available_tools:
274
+ event_properties["$ai_tools"] = available_tools
275
+
210
276
  if posthog_distinct_id is None:
211
277
  event_properties["$process_person_profile"] = False
212
278