posthoganalytics 6.7.14__py3-none-any.whl → 6.8.0__py3-none-any.whl

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.
@@ -14,14 +14,9 @@ from posthoganalytics import setup
14
14
  from posthoganalytics.ai.types import StreamingContentBlock, TokenUsage, ToolInProgress
15
15
  from posthoganalytics.ai.utils import (
16
16
  call_llm_and_track_usage_async,
17
- extract_available_tool_calls,
18
- get_model_params,
19
- merge_system_prompt,
20
17
  merge_usage_stats,
21
- with_privacy_mode,
22
18
  )
23
19
  from posthoganalytics.ai.anthropic.anthropic_converter import (
24
- format_anthropic_streaming_content,
25
20
  extract_anthropic_usage_from_event,
26
21
  handle_anthropic_content_block_start,
27
22
  handle_anthropic_text_delta,
@@ -220,66 +215,34 @@ class AsyncWrappedMessages(AsyncMessages):
220
215
  content_blocks: List[StreamingContentBlock],
221
216
  accumulated_content: str,
222
217
  ):
223
- if posthog_trace_id is None:
224
- posthog_trace_id = str(uuid.uuid4())
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
-
241
- event_properties = {
242
- "$ai_provider": "anthropic",
243
- "$ai_model": kwargs.get("model"),
244
- "$ai_model_parameters": get_model_params(kwargs),
245
- "$ai_input": with_privacy_mode(
246
- self._client._ph_client,
247
- posthog_privacy_mode,
248
- sanitize_anthropic(merge_system_prompt(kwargs, "anthropic")),
249
- ),
250
- "$ai_output_choices": with_privacy_mode(
251
- self._client._ph_client,
252
- posthog_privacy_mode,
253
- formatted_output,
254
- ),
255
- "$ai_http_status": 200,
256
- "$ai_input_tokens": usage_stats.get("input_tokens", 0),
257
- "$ai_output_tokens": usage_stats.get("output_tokens", 0),
258
- "$ai_cache_read_input_tokens": usage_stats.get(
259
- "cache_read_input_tokens", 0
260
- ),
261
- "$ai_cache_creation_input_tokens": usage_stats.get(
262
- "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
263
237
  ),
264
- "$ai_latency": latency,
265
- "$ai_trace_id": posthog_trace_id,
266
- "$ai_base_url": str(self._client.base_url),
267
- **(posthog_properties or {}),
268
- }
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
-
276
- if posthog_distinct_id is None:
277
- event_properties["$process_person_profile"] = False
278
-
279
- if hasattr(self._client._ph_client, "capture"):
280
- self._client._ph_client.capture(
281
- distinct_id=posthog_distinct_id or posthog_trace_id,
282
- event="$ai_generation",
283
- properties=event_properties,
284
- groups=posthog_groups,
285
- )
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)
@@ -163,6 +163,32 @@ def format_anthropic_streaming_content(
163
163
  return formatted
164
164
 
165
165
 
166
+ def extract_anthropic_web_search_count(response: Any) -> int:
167
+ """
168
+ Extract web search count from Anthropic response.
169
+
170
+ Anthropic provides exact web search counts via usage.server_tool_use.web_search_requests.
171
+
172
+ Args:
173
+ response: The response from Anthropic API
174
+
175
+ Returns:
176
+ Number of web search requests (0 if none)
177
+ """
178
+ if not hasattr(response, "usage"):
179
+ return 0
180
+
181
+ if not hasattr(response.usage, "server_tool_use"):
182
+ return 0
183
+
184
+ server_tool_use = response.usage.server_tool_use
185
+
186
+ if hasattr(server_tool_use, "web_search_requests"):
187
+ return max(0, int(getattr(server_tool_use, "web_search_requests", 0)))
188
+
189
+ return 0
190
+
191
+
166
192
  def extract_anthropic_usage_from_response(response: Any) -> TokenUsage:
167
193
  """
168
194
  Extract usage from a full Anthropic response (non-streaming).
@@ -191,6 +217,10 @@ def extract_anthropic_usage_from_response(response: Any) -> TokenUsage:
191
217
  if cache_creation and cache_creation > 0:
192
218
  result["cache_creation_input_tokens"] = cache_creation
193
219
 
220
+ web_search_count = extract_anthropic_web_search_count(response)
221
+ if web_search_count > 0:
222
+ result["web_search_count"] = web_search_count
223
+
194
224
  return result
195
225
 
196
226
 
@@ -222,6 +252,16 @@ def extract_anthropic_usage_from_event(event: Any) -> TokenUsage:
222
252
  if hasattr(event, "usage") and event.usage:
223
253
  usage["output_tokens"] = getattr(event.usage, "output_tokens", 0)
224
254
 
255
+ # Extract web search count from usage
256
+ if hasattr(event.usage, "server_tool_use"):
257
+ server_tool_use = event.usage.server_tool_use
258
+ if hasattr(server_tool_use, "web_search_requests"):
259
+ web_search_count = int(
260
+ getattr(server_tool_use, "web_search_requests", 0)
261
+ )
262
+ if web_search_count > 0:
263
+ usage["web_search_count"] = web_search_count
264
+
225
265
  return usage
226
266
 
227
267
 
@@ -338,6 +338,61 @@ def format_gemini_input(contents: Any) -> List[FormattedMessage]:
338
338
  return [_format_object_message(contents)]
339
339
 
340
340
 
341
+ def extract_gemini_web_search_count(response: Any) -> int:
342
+ """
343
+ Extract web search count from Gemini response.
344
+
345
+ Gemini bills per request that uses grounding, not per query.
346
+ Returns 1 if grounding_metadata is present with actual search data, 0 otherwise.
347
+
348
+ Args:
349
+ response: The response from Gemini API
350
+
351
+ Returns:
352
+ 1 if web search/grounding was used, 0 otherwise
353
+ """
354
+
355
+ # Check for grounding_metadata in candidates
356
+ if hasattr(response, "candidates"):
357
+ for candidate in response.candidates:
358
+ if (
359
+ hasattr(candidate, "grounding_metadata")
360
+ and candidate.grounding_metadata
361
+ ):
362
+ grounding_metadata = candidate.grounding_metadata
363
+
364
+ # Check if web_search_queries exists and is non-empty
365
+ if hasattr(grounding_metadata, "web_search_queries"):
366
+ queries = grounding_metadata.web_search_queries
367
+
368
+ if queries is not None and len(queries) > 0:
369
+ return 1
370
+
371
+ # Check if grounding_chunks exists and is non-empty
372
+ if hasattr(grounding_metadata, "grounding_chunks"):
373
+ chunks = grounding_metadata.grounding_chunks
374
+
375
+ if chunks is not None and len(chunks) > 0:
376
+ return 1
377
+
378
+ # Also check for google_search or grounding in function call names
379
+ if hasattr(candidate, "content") and candidate.content:
380
+ if hasattr(candidate.content, "parts") and candidate.content.parts:
381
+ for part in candidate.content.parts:
382
+ if hasattr(part, "function_call") and part.function_call:
383
+ function_name = getattr(
384
+ part.function_call, "name", ""
385
+ ).lower()
386
+
387
+ if (
388
+ "google_search" in function_name
389
+ or "grounding" in function_name
390
+ ):
391
+ return 1
392
+
393
+ return 0
394
+
395
+
341
396
  def _extract_usage_from_metadata(metadata: Any) -> TokenUsage:
342
397
  """
343
398
  Common logic to extract usage from Gemini metadata.
@@ -382,7 +437,14 @@ def extract_gemini_usage_from_response(response: Any) -> TokenUsage:
382
437
  if not hasattr(response, "usage_metadata") or not response.usage_metadata:
383
438
  return TokenUsage(input_tokens=0, output_tokens=0)
384
439
 
385
- return _extract_usage_from_metadata(response.usage_metadata)
440
+ usage = _extract_usage_from_metadata(response.usage_metadata)
441
+
442
+ # Add web search count if present
443
+ web_search_count = extract_gemini_web_search_count(response)
444
+ if web_search_count > 0:
445
+ usage["web_search_count"] = web_search_count
446
+
447
+ return usage
386
448
 
387
449
 
388
450
  def extract_gemini_usage_from_chunk(chunk: Any) -> TokenUsage:
@@ -398,11 +460,19 @@ def extract_gemini_usage_from_chunk(chunk: Any) -> TokenUsage:
398
460
 
399
461
  usage: TokenUsage = TokenUsage()
400
462
 
463
+ # Extract web search count from the chunk before checking for usage_metadata
464
+ # Web search indicators can appear on any chunk, not just those with usage data
465
+ web_search_count = extract_gemini_web_search_count(chunk)
466
+ if web_search_count > 0:
467
+ usage["web_search_count"] = web_search_count
468
+
401
469
  if not hasattr(chunk, "usage_metadata") or not chunk.usage_metadata:
402
470
  return usage
403
471
 
404
- # Use the shared helper to extract usage
405
- usage = _extract_usage_from_metadata(chunk.usage_metadata)
472
+ usage_from_metadata = _extract_usage_from_metadata(chunk.usage_metadata)
473
+
474
+ # Merge the usage from metadata with any web search count we found
475
+ usage.update(usage_from_metadata)
406
476
 
407
477
  return usage
408
478
 
@@ -213,6 +213,15 @@ class WrappedResponses:
213
213
  **(posthog_properties or {}),
214
214
  }
215
215
 
216
+ # Add web search count if present
217
+ web_search_count = usage_stats.get("web_search_count")
218
+ if (
219
+ web_search_count is not None
220
+ and isinstance(web_search_count, int)
221
+ and web_search_count > 0
222
+ ):
223
+ event_properties["$ai_web_search_count"] = web_search_count
224
+
216
225
  if available_tool_calls:
217
226
  event_properties["$ai_tools"] = available_tool_calls
218
227
 
@@ -444,6 +453,16 @@ class WrappedCompletions:
444
453
  **(posthog_properties or {}),
445
454
  }
446
455
 
456
+ # Add web search count if present
457
+ web_search_count = usage_stats.get("web_search_count")
458
+
459
+ if (
460
+ web_search_count is not None
461
+ and isinstance(web_search_count, int)
462
+ and web_search_count > 0
463
+ ):
464
+ event_properties["$ai_web_search_count"] = web_search_count
465
+
447
466
  if available_tool_calls:
448
467
  event_properties["$ai_tools"] = available_tool_calls
449
468
 
@@ -255,6 +255,113 @@ def format_openai_streaming_content(
255
255
  return formatted
256
256
 
257
257
 
258
+ def extract_openai_web_search_count(response: Any) -> int:
259
+ """
260
+ Extract web search count from OpenAI response.
261
+
262
+ Uses a two-tier detection strategy:
263
+ 1. Priority 1 (exact count): Check for output[].type == "web_search_call" (Responses API)
264
+ 2. Priority 2 (binary detection): Check for various web search indicators:
265
+ - Root-level citations, search_results, or usage.search_context_size (Perplexity)
266
+ - Annotations with type "url_citation" in choices/output (including delta for streaming)
267
+
268
+ Args:
269
+ response: The response from OpenAI API
270
+
271
+ Returns:
272
+ Number of web search requests (exact count or binary 1/0)
273
+ """
274
+
275
+ # Priority 1: Check for exact count in Responses API output
276
+ if hasattr(response, "output"):
277
+ web_search_count = 0
278
+
279
+ for item in response.output:
280
+ if hasattr(item, "type") and item.type == "web_search_call":
281
+ web_search_count += 1
282
+
283
+ web_search_count = max(0, web_search_count)
284
+
285
+ if web_search_count > 0:
286
+ return web_search_count
287
+
288
+ # Priority 2: Binary detection (returns 1 or 0)
289
+
290
+ # Check root-level indicators (Perplexity)
291
+ if hasattr(response, "citations"):
292
+ citations = getattr(response, "citations")
293
+
294
+ if citations and len(citations) > 0:
295
+ return 1
296
+
297
+ if hasattr(response, "search_results"):
298
+ search_results = getattr(response, "search_results")
299
+
300
+ if search_results and len(search_results) > 0:
301
+ return 1
302
+
303
+ if hasattr(response, "usage") and hasattr(response.usage, "search_context_size"):
304
+ if response.usage.search_context_size:
305
+ return 1
306
+
307
+ # Check for url_citation annotations in choices (Chat Completions)
308
+ if hasattr(response, "choices"):
309
+ for choice in response.choices:
310
+ # Check message.annotations (non-streaming or final chunk)
311
+ if hasattr(choice, "message") and hasattr(choice.message, "annotations"):
312
+ annotations = choice.message.annotations
313
+
314
+ if annotations:
315
+ for annotation in annotations:
316
+ # Support both dict and object formats
317
+ annotation_type = (
318
+ annotation.get("type")
319
+ if isinstance(annotation, dict)
320
+ else getattr(annotation, "type", None)
321
+ )
322
+
323
+ if annotation_type == "url_citation":
324
+ return 1
325
+
326
+ # Check delta.annotations (streaming chunks)
327
+ if hasattr(choice, "delta") and hasattr(choice.delta, "annotations"):
328
+ annotations = choice.delta.annotations
329
+
330
+ if annotations:
331
+ for annotation in annotations:
332
+ # Support both dict and object formats
333
+ annotation_type = (
334
+ annotation.get("type")
335
+ if isinstance(annotation, dict)
336
+ else getattr(annotation, "type", None)
337
+ )
338
+
339
+ if annotation_type == "url_citation":
340
+ return 1
341
+
342
+ # Check for url_citation annotations in output (Responses API)
343
+ if hasattr(response, "output"):
344
+ for item in response.output:
345
+ if hasattr(item, "content") and isinstance(item.content, list):
346
+ for content_item in item.content:
347
+ if hasattr(content_item, "annotations"):
348
+ annotations = content_item.annotations
349
+
350
+ if annotations:
351
+ for annotation in annotations:
352
+ # Support both dict and object formats
353
+ annotation_type = (
354
+ annotation.get("type")
355
+ if isinstance(annotation, dict)
356
+ else getattr(annotation, "type", None)
357
+ )
358
+
359
+ if annotation_type == "url_citation":
360
+ return 1
361
+
362
+ return 0
363
+
364
+
258
365
  def extract_openai_usage_from_response(response: Any) -> TokenUsage:
259
366
  """
260
367
  Extract usage statistics from a full OpenAI response (non-streaming).
@@ -312,6 +419,10 @@ def extract_openai_usage_from_response(response: Any) -> TokenUsage:
312
419
  if reasoning_tokens > 0:
313
420
  result["reasoning_tokens"] = reasoning_tokens
314
421
 
422
+ web_search_count = extract_openai_web_search_count(response)
423
+ if web_search_count > 0:
424
+ result["web_search_count"] = web_search_count
425
+
315
426
  return result
316
427
 
317
428
 
@@ -334,6 +445,13 @@ def extract_openai_usage_from_chunk(
334
445
  usage: TokenUsage = TokenUsage()
335
446
 
336
447
  if provider_type == "chat":
448
+ # Extract web search count from the chunk before checking for usage
449
+ # Web search indicators (citations, annotations) can appear on any chunk,
450
+ # not just those with usage data
451
+ web_search_count = extract_openai_web_search_count(chunk)
452
+ if web_search_count > 0:
453
+ usage["web_search_count"] = web_search_count
454
+
337
455
  if not hasattr(chunk, "usage") or not chunk.usage:
338
456
  return usage
339
457
 
@@ -386,6 +504,12 @@ def extract_openai_usage_from_chunk(
386
504
  response_usage.output_tokens_details.reasoning_tokens
387
505
  )
388
506
 
507
+ # Extract web search count from the complete response
508
+ if hasattr(chunk, "response"):
509
+ web_search_count = extract_openai_web_search_count(chunk.response)
510
+ if web_search_count > 0:
511
+ usage["web_search_count"] = web_search_count
512
+
389
513
  return usage
390
514
 
391
515
 
@@ -63,6 +63,7 @@ class TokenUsage(TypedDict, total=False):
63
63
  cache_read_input_tokens: Optional[int]
64
64
  cache_creation_input_tokens: Optional[int]
65
65
  reasoning_tokens: Optional[int]
66
+ web_search_count: Optional[int]
66
67
 
67
68
 
68
69
  class ProviderResponse(TypedDict, total=False):
@@ -53,6 +53,12 @@ def merge_usage_stats(
53
53
  if source_reasoning is not None:
54
54
  current = target.get("reasoning_tokens") or 0
55
55
  target["reasoning_tokens"] = current + source_reasoning
56
+
57
+ source_web_search = source.get("web_search_count")
58
+ if source_web_search is not None:
59
+ current = target.get("web_search_count") or 0
60
+ target["web_search_count"] = max(current, source_web_search)
61
+
56
62
  elif mode == "cumulative":
57
63
  # Replace with latest values (already cumulative)
58
64
  if source.get("input_tokens") is not None:
@@ -67,6 +73,9 @@ def merge_usage_stats(
67
73
  ]
68
74
  if source.get("reasoning_tokens") is not None:
69
75
  target["reasoning_tokens"] = source["reasoning_tokens"]
76
+ if source.get("web_search_count") is not None:
77
+ target["web_search_count"] = source["web_search_count"]
78
+
70
79
  else:
71
80
  raise ValueError(f"Invalid mode: {mode}. Must be 'incremental' or 'cumulative'")
72
81
 
@@ -311,6 +320,10 @@ def call_llm_and_track_usage(
311
320
  if reasoning is not None and reasoning > 0:
312
321
  event_properties["$ai_reasoning_tokens"] = reasoning
313
322
 
323
+ web_search_count = usage.get("web_search_count")
324
+ if web_search_count is not None and web_search_count > 0:
325
+ event_properties["$ai_web_search_count"] = web_search_count
326
+
314
327
  if posthog_distinct_id is None:
315
328
  event_properties["$process_person_profile"] = False
316
329
 
@@ -414,6 +427,14 @@ async def call_llm_and_track_usage_async(
414
427
  if cache_creation is not None and cache_creation > 0:
415
428
  event_properties["$ai_cache_creation_input_tokens"] = cache_creation
416
429
 
430
+ reasoning = usage.get("reasoning_tokens")
431
+ if reasoning is not None and reasoning > 0:
432
+ event_properties["$ai_reasoning_tokens"] = reasoning
433
+
434
+ web_search_count = usage.get("web_search_count")
435
+ if web_search_count is not None and web_search_count > 0:
436
+ event_properties["$ai_web_search_count"] = web_search_count
437
+
417
438
  if posthog_distinct_id is None:
418
439
  event_properties["$process_person_profile"] = False
419
440
 
@@ -535,6 +556,15 @@ def capture_streaming_event(
535
556
  if value is not None and isinstance(value, int) and value > 0:
536
557
  event_properties[f"$ai_{field}"] = value
537
558
 
559
+ # Add web search count if present (all providers)
560
+ web_search_count = event_data["usage_stats"].get("web_search_count")
561
+ if (
562
+ web_search_count is not None
563
+ and isinstance(web_search_count, int)
564
+ and web_search_count > 0
565
+ ):
566
+ event_properties["$ai_web_search_count"] = web_search_count
567
+
538
568
  # Handle provider-specific fields
539
569
  if (
540
570
  event_data["provider"] == "openai"
@@ -1,4 +1,4 @@
1
- VERSION = "6.7.14"
1
+ VERSION = "6.8.0"
2
2
 
3
3
  if __name__ == "__main__":
4
4
  print(VERSION, end="") # noqa: T201
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: posthoganalytics
3
- Version: 6.7.14
3
+ Version: 6.8.0
4
4
  Summary: Integrate PostHog into any python application.
5
5
  Home-page: https://github.com/posthog/posthog-python
6
6
  Author: Posthog
@@ -11,25 +11,25 @@ posthoganalytics/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
11
  posthoganalytics/request.py,sha256=Bsl2c5WwONKPQzwWMmKPX5VgOlwSiIcSNfhXgoz62Y8,6186
12
12
  posthoganalytics/types.py,sha256=Dl3aFGX9XUR0wMmK12r2s5Hjan9jL4HpQ9GHpVcEq5U,10207
13
13
  posthoganalytics/utils.py,sha256=-0w-OLcCaoldkbBebPzQyBzLJSo9G9yBOg8NDVz7La8,16088
14
- posthoganalytics/version.py,sha256=k4cftBsqeSbHfTCer7Hg6W0a46plTTfVS_st6Xhjlhc,88
14
+ posthoganalytics/version.py,sha256=xqPIL3Vi5H0V2bZOTQqSHr-lQXEhP8VBNZcmOrTSRUw,87
15
15
  posthoganalytics/ai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
16
  posthoganalytics/ai/sanitization.py,sha256=owipZ4eJYtd4JTI-CM_klatclXaeaIec3XJBOUfsOnQ,5770
17
- posthoganalytics/ai/types.py,sha256=ceubs4K9xf8vQx7wokq1NL9hPtxyS7D7sUOuT7Lx1lM,3237
18
- posthoganalytics/ai/utils.py,sha256=WPeb-K_c4wwLJ2fr6s6OAq9YuxwvFhyGqQdIwaRSUQw,20364
17
+ posthoganalytics/ai/types.py,sha256=arX98hR1PIPeJ3vFikxTlACIh1xPp6aEUw1gBLcKoB0,3273
18
+ posthoganalytics/ai/utils.py,sha256=scXl1oSDepef0KOwYI5Tr6gwT3tDe5QqAa8BgAiO2JM,21610
19
19
  posthoganalytics/ai/anthropic/__init__.py,sha256=8nTvETZzkfW-P3zBMmp06GOHs0N-xyOGu7Oa4di_lno,669
20
20
  posthoganalytics/ai/anthropic/anthropic.py,sha256=njOoVb9vkCdnPWAQuVF0XB0BnT2y1ScIryrCGyt5ur8,8750
21
- posthoganalytics/ai/anthropic/anthropic_async.py,sha256=nM3oFcNLw6meEtV6RfrvhFcuxD4aS-CXDuepRHycUjM,10169
22
- posthoganalytics/ai/anthropic/anthropic_converter.py,sha256=LWIQ1kyK3vV3rLBmQIcd-98fet7isK3uhTRmBqBN0lk,11776
21
+ posthoganalytics/ai/anthropic/anthropic_async.py,sha256=EKqDjxoiiGNV2VsLhmMoi_1yKoMSTTUrthTkJlttV8A,8870
22
+ posthoganalytics/ai/anthropic/anthropic_converter.py,sha256=0IrXWWGpvE6IIbpczl0osrf4R4XqYDQMBMsKKB_NinY,13071
23
23
  posthoganalytics/ai/anthropic/anthropic_providers.py,sha256=y1_qc8Lbip-YDmpimPGg3DfTm5g-WZk5FrRCXzwF_Ow,2139
24
24
  posthoganalytics/ai/gemini/__init__.py,sha256=JV_9-gBR87leHgZW4XAYZP7LSl4YaXeuhqDUpA8HygA,383
25
25
  posthoganalytics/ai/gemini/gemini.py,sha256=A2acjT_m8ru2YwgIk15aN21CRVEl2jh8pbqjmHplMC8,15035
26
- posthoganalytics/ai/gemini/gemini_converter.py,sha256=WzRsid-FjXRyhAI5wQ9-tjTapYVCTRKuMPcZFYKUdIo,16027
26
+ posthoganalytics/ai/gemini/gemini_converter.py,sha256=lfd-AqBYdM3_OJtuvkFb9AlSba1gQt4K5TpKqzXykdk,18749
27
27
  posthoganalytics/ai/langchain/__init__.py,sha256=9CqAwLynTGj3ASAR80C3PmdTdrYGmu99tz0JL-HPFgI,70
28
28
  posthoganalytics/ai/langchain/callbacks.py,sha256=syDeSb4hOrwxjEtlmRodVhdgVAQi8iwg1Z63YHNUhvA,30297
29
29
  posthoganalytics/ai/openai/__init__.py,sha256=u4OuUT7k1NgFj0TrxjuyegOg7a_UA8nAU6a-Hszr0OM,490
30
30
  posthoganalytics/ai/openai/openai.py,sha256=I05NruE9grWezM_EgOZBiG5Ej_gABsDcYKN0pRQWvzU,20235
31
- posthoganalytics/ai/openai/openai_async.py,sha256=YAaj8Q-X3bExx-BXLWUOtdTMdj3RKe8bUkTyjamNURo,21829
32
- posthoganalytics/ai/openai/openai_converter.py,sha256=VBaAGdXPSVNgfvCnSAojslWkTRO2luUxpjafR-WMEbs,20469
31
+ posthoganalytics/ai/openai/openai_async.py,sha256=mIxFZykDgMi3ws_fNWikEhwvkZmKqfYgeeB2yhxlZjQ,22490
32
+ posthoganalytics/ai/openai/openai_converter.py,sha256=ug-b3NNVB8f4-4uyhNs3Y7P71BCx4daGc4iA57NYTH4,25484
33
33
  posthoganalytics/ai/openai/openai_providers.py,sha256=RPVmj2V0_lAdno_ax5Ul2kwhBA9_rRgAdl_sCqrQc6M,4004
34
34
  posthoganalytics/integrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
35
  posthoganalytics/integrations/django.py,sha256=aJ_fLjeMqnnF01Zp8N3c9OeXvWwDL_X_o7aqhlw3e5U,12660
@@ -47,8 +47,8 @@ posthoganalytics/test/test_request.py,sha256=Zc0VbkjpVmj8mKokQm9rzdgTr0b1U44vvMY
47
47
  posthoganalytics/test/test_size_limited_dict.py,sha256=-5IQjIEr_-Dql24M0HusdR_XroOMrtgiT0v6ZQCRvzo,774
48
48
  posthoganalytics/test/test_types.py,sha256=bRPHdwVpP7hu7emsplU8UVyzSQptv6PaG5lAoOD_BtM,7595
49
49
  posthoganalytics/test/test_utils.py,sha256=sqUTbfweVcxxFRd3WDMFXqPMyU6DvzOBeAOc68Py9aw,9620
50
- posthoganalytics-6.7.14.dist-info/licenses/LICENSE,sha256=wGf9JBotDkSygFj43m49oiKlFnpMnn97keiZKF-40vE,2450
51
- posthoganalytics-6.7.14.dist-info/METADATA,sha256=PN07-Rd1IsaPQ0wk7AIE0G828vN2abA7p54wlN5ysfA,6025
52
- posthoganalytics-6.7.14.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
53
- posthoganalytics-6.7.14.dist-info/top_level.txt,sha256=8QsNIqIkBh1p2TXvKp0Em9ZLZKwe3uIqCETyW4s1GOE,17
54
- posthoganalytics-6.7.14.dist-info/RECORD,,
50
+ posthoganalytics-6.8.0.dist-info/licenses/LICENSE,sha256=wGf9JBotDkSygFj43m49oiKlFnpMnn97keiZKF-40vE,2450
51
+ posthoganalytics-6.8.0.dist-info/METADATA,sha256=aJLZEPJb-8QghbEJJ3GUEBEAcib56DsCiIs6-kFiwls,6024
52
+ posthoganalytics-6.8.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
53
+ posthoganalytics-6.8.0.dist-info/top_level.txt,sha256=8QsNIqIkBh1p2TXvKp0Em9ZLZKwe3uIqCETyW4s1GOE,17
54
+ posthoganalytics-6.8.0.dist-info/RECORD,,