posthog 6.7.3__py3-none-any.whl → 6.7.4__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.
@@ -3,7 +3,8 @@ import time
3
3
  import uuid
4
4
  from typing import Any, Dict, Optional
5
5
 
6
- from posthog.ai.types import TokenUsage
6
+ from posthog.ai.types import TokenUsage, StreamingEventData
7
+ from posthog.ai.utils import merge_system_prompt
7
8
 
8
9
  try:
9
10
  from google import genai
@@ -19,7 +20,6 @@ from posthog.ai.utils import (
19
20
  merge_usage_stats,
20
21
  )
21
22
  from posthog.ai.gemini.gemini_converter import (
22
- format_gemini_input,
23
23
  extract_gemini_usage_from_chunk,
24
24
  extract_gemini_content_from_chunk,
25
25
  format_gemini_streaming_output,
@@ -356,10 +356,8 @@ class Models:
356
356
  latency: float,
357
357
  output: Any,
358
358
  ):
359
- from posthog.ai.types import StreamingEventData
360
-
361
359
  # Prepare standardized event data
362
- formatted_input = self._format_input(contents)
360
+ formatted_input = self._format_input(contents, **kwargs)
363
361
  sanitized_input = sanitize_gemini(formatted_input)
364
362
 
365
363
  event_data = StreamingEventData(
@@ -381,10 +379,12 @@ class Models:
381
379
  # Use the common capture function
382
380
  capture_streaming_event(self._ph_client, event_data)
383
381
 
384
- def _format_input(self, contents):
382
+ def _format_input(self, contents, **kwargs):
385
383
  """Format input contents for PostHog tracking"""
386
384
 
387
- return format_gemini_input(contents)
385
+ # Create kwargs dict with contents for merge_system_prompt
386
+ input_kwargs = {"contents": contents, **kwargs}
387
+ return merge_system_prompt(input_kwargs, "gemini")
388
388
 
389
389
  def generate_content_stream(
390
390
  self,
@@ -220,6 +220,30 @@ def format_gemini_response(response: Any) -> List[FormattedMessage]:
220
220
  return output
221
221
 
222
222
 
223
+ def extract_gemini_system_instruction(config: Any) -> Optional[str]:
224
+ """
225
+ Extract system instruction from Gemini config parameter.
226
+
227
+ Args:
228
+ config: Config object or dict that may contain system instruction
229
+
230
+ Returns:
231
+ System instruction string if present, None otherwise
232
+ """
233
+ if config is None:
234
+ return None
235
+
236
+ # Handle different config formats
237
+ if hasattr(config, "system_instruction"):
238
+ return config.system_instruction
239
+ elif isinstance(config, dict) and "system_instruction" in config:
240
+ return config["system_instruction"]
241
+ elif isinstance(config, dict) and "systemInstruction" in config:
242
+ return config["systemInstruction"]
243
+
244
+ return None
245
+
246
+
223
247
  def extract_gemini_tools(kwargs: Dict[str, Any]) -> Optional[Any]:
224
248
  """
225
249
  Extract tool definitions from Gemini API kwargs.
@@ -237,6 +261,38 @@ def extract_gemini_tools(kwargs: Dict[str, Any]) -> Optional[Any]:
237
261
  return None
238
262
 
239
263
 
264
+ def format_gemini_input_with_system(
265
+ contents: Any, config: Any = None
266
+ ) -> List[FormattedMessage]:
267
+ """
268
+ Format Gemini input contents into standardized message format, including system instruction handling.
269
+
270
+ Args:
271
+ contents: Input contents in various possible formats
272
+ config: Config object or dict that may contain system instruction
273
+
274
+ Returns:
275
+ List of formatted messages with role and content fields, with system message prepended if needed
276
+ """
277
+ formatted_messages = format_gemini_input(contents)
278
+
279
+ # Check if system instruction is provided in config parameter
280
+ system_instruction = extract_gemini_system_instruction(config)
281
+
282
+ if system_instruction is not None:
283
+ has_system = any(msg.get("role") == "system" for msg in formatted_messages)
284
+ if not has_system:
285
+ from posthog.ai.types import FormattedMessage
286
+
287
+ system_message: FormattedMessage = {
288
+ "role": "system",
289
+ "content": system_instruction,
290
+ }
291
+ formatted_messages = [system_message] + list(formatted_messages)
292
+
293
+ return formatted_messages
294
+
295
+
240
296
  def format_gemini_input(contents: Any) -> List[FormattedMessage]:
241
297
  """
242
298
  Format Gemini input contents into standardized message format for PostHog tracking.
@@ -606,7 +606,6 @@ def format_openai_streaming_input(
606
606
  Returns:
607
607
  Formatted input ready for PostHog tracking
608
608
  """
609
- if api_type == "chat":
610
- return kwargs.get("messages")
611
- else: # responses API
612
- return kwargs.get("input")
609
+ from posthog.ai.utils import merge_system_prompt
610
+
611
+ return merge_system_prompt(kwargs, "openai")
posthog/ai/utils.py CHANGED
@@ -1,9 +1,9 @@
1
1
  import time
2
2
  import uuid
3
- from typing import Any, Callable, Dict, Optional
3
+ from typing import Any, Callable, Dict, List, Optional, cast
4
4
 
5
5
  from posthog.client import Client as PostHogClient
6
- from posthog.ai.types import StreamingEventData, TokenUsage
6
+ from posthog.ai.types import FormattedMessage, StreamingEventData, TokenUsage
7
7
  from posthog.ai.sanitization import (
8
8
  sanitize_openai,
9
9
  sanitize_anthropic,
@@ -158,7 +158,9 @@ def extract_available_tool_calls(provider: str, kwargs: Dict[str, Any]):
158
158
  return None
159
159
 
160
160
 
161
- def merge_system_prompt(kwargs: Dict[str, Any], provider: str):
161
+ def merge_system_prompt(
162
+ kwargs: Dict[str, Any], provider: str
163
+ ) -> List[FormattedMessage]:
162
164
  """
163
165
  Merge system prompts and format messages for the given provider.
164
166
  """
@@ -169,10 +171,11 @@ def merge_system_prompt(kwargs: Dict[str, Any], provider: str):
169
171
  system = kwargs.get("system")
170
172
  return format_anthropic_input(messages, system)
171
173
  elif provider == "gemini":
172
- from posthog.ai.gemini.gemini_converter import format_gemini_input
174
+ from posthog.ai.gemini.gemini_converter import format_gemini_input_with_system
173
175
 
174
176
  contents = kwargs.get("contents", [])
175
- return format_gemini_input(contents)
177
+ config = kwargs.get("config")
178
+ return format_gemini_input_with_system(contents, config)
176
179
  elif provider == "openai":
177
180
  from posthog.ai.openai.openai_converter import format_openai_input
178
181
 
@@ -187,9 +190,11 @@ def merge_system_prompt(kwargs: Dict[str, Any], provider: str):
187
190
  if kwargs.get("system") is not None:
188
191
  has_system = any(msg.get("role") == "system" for msg in messages)
189
192
  if not has_system:
190
- messages = [
191
- {"role": "system", "content": kwargs.get("system")}
192
- ] + messages
193
+ system_msg = cast(
194
+ FormattedMessage,
195
+ {"role": "system", "content": kwargs.get("system")},
196
+ )
197
+ messages = [system_msg] + messages
193
198
 
194
199
  # For Responses API, add instructions to the system prompt if provided
195
200
  if kwargs.get("instructions") is not None:
@@ -207,9 +212,11 @@ def merge_system_prompt(kwargs: Dict[str, Any], provider: str):
207
212
  )
208
213
  else:
209
214
  # Create a new system message with instructions
210
- messages = [
211
- {"role": "system", "content": kwargs.get("instructions")}
212
- ] + messages
215
+ instruction_msg = cast(
216
+ FormattedMessage,
217
+ {"role": "system", "content": kwargs.get("instructions")},
218
+ )
219
+ messages = [instruction_msg] + messages
213
220
 
214
221
  return messages
215
222
 
posthog/version.py CHANGED
@@ -1,4 +1,4 @@
1
- VERSION = "6.7.3"
1
+ VERSION = "6.7.4"
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: posthog
3
- Version: 6.7.3
3
+ Version: 6.7.4
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 @@ posthog/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
11
  posthog/request.py,sha256=CaONBN7a5RD8xiSShVMgHEd9XxKWM6ZQTLZypiqABhA,6168
12
12
  posthog/types.py,sha256=Dl3aFGX9XUR0wMmK12r2s5Hjan9jL4HpQ9GHpVcEq5U,10207
13
13
  posthog/utils.py,sha256=-0w-OLcCaoldkbBebPzQyBzLJSo9G9yBOg8NDVz7La8,16088
14
- posthog/version.py,sha256=pd4U7G30qoIwQl2HZ9MpxMew8kahEVgR-yT4Kp42zcI,87
14
+ posthog/version.py,sha256=RJbegcgNmUJvUzvz3PhJ7kXrYNUcMXmEqH9X-ctDffs,87
15
15
  posthog/ai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
16
  posthog/ai/sanitization.py,sha256=owipZ4eJYtd4JTI-CM_klatclXaeaIec3XJBOUfsOnQ,5770
17
17
  posthog/ai/types.py,sha256=ceubs4K9xf8vQx7wokq1NL9hPtxyS7D7sUOuT7Lx1lM,3237
18
- posthog/ai/utils.py,sha256=1DE_CUefvKssxs5NoFRPTFR8VH4PsF9Rhj2yjP6bQmg,19917
18
+ posthog/ai/utils.py,sha256=p6PjxEdBz0RddY0zmItC0sb2qdCzG28XQKEpKbOWDU0,20229
19
19
  posthog/ai/anthropic/__init__.py,sha256=8nTvETZzkfW-P3zBMmp06GOHs0N-xyOGu7Oa4di_lno,669
20
20
  posthog/ai/anthropic/anthropic.py,sha256=UWyM6ryl5_VNQImaBi1RHN7tKXwkqaxy4yaXyPSkDp8,8669
21
21
  posthog/ai/anthropic/anthropic_async.py,sha256=H60nuSWq7dueRMGg-oD66X_lOB_XgKufUh8zzcMoOO8,10115
22
22
  posthog/ai/anthropic/anthropic_converter.py,sha256=_YY8jnOSdiD7fI3ACmTHc-w6dMFCKfvzVZrsvxt0UEM,11758
23
23
  posthog/ai/anthropic/anthropic_providers.py,sha256=Q_v7U4wgieIkvii-Bqh4pLx5pEgbrHmgsCG8lUkKb_0,2103
24
24
  posthog/ai/gemini/__init__.py,sha256=JV_9-gBR87leHgZW4XAYZP7LSl4YaXeuhqDUpA8HygA,383
25
- posthog/ai/gemini/gemini.py,sha256=ZszN-0DLvs1R_ZSglDRqcWzhzRLnDPxVh-Vv3vkBTg8,14828
26
- posthog/ai/gemini/gemini_converter.py,sha256=48JC13ELns9wdT0jh-0OlWnpNCcshGZz1swCEzMvlic,14145
25
+ posthog/ai/gemini/gemini.py,sha256=-c2MnBeask6SrAbFZ7XXZ_OMcuglTBRdnFe_ROVgXWQ,14972
26
+ posthog/ai/gemini/gemini_converter.py,sha256=0LM5OXsCWjg8eazi06VI5Fc4LSGxSUvg-_XpZVll78A,16009
27
27
  posthog/ai/langchain/__init__.py,sha256=9CqAwLynTGj3ASAR80C3PmdTdrYGmu99tz0JL-HPFgI,70
28
28
  posthog/ai/langchain/callbacks.py,sha256=vkcOUch82N6BJPDTZTmTHVrRu0ZpLdFZqgt_LEqwGPg,29491
29
29
  posthog/ai/openai/__init__.py,sha256=u4OuUT7k1NgFj0TrxjuyegOg7a_UA8nAU6a-Hszr0OM,490
30
30
  posthog/ai/openai/openai.py,sha256=ts95vdvWH7h0TX4FpLLK_wU_7H0MP3eZBEg0S-lsCKw,20127
31
31
  posthog/ai/openai/openai_async.py,sha256=Ulcv_R8vgYq0wewrVG7QrIl0aiL66F6Vmyp97OoWaiQ,21757
32
- posthog/ai/openai/openai_converter.py,sha256=2JyA3yhv0v-YTKaRJpY1frGiR6KsI4GwY13fs65VGQo,20475
32
+ posthog/ai/openai/openai_converter.py,sha256=B38g_RgN1n7mbxbDDAZFcu50RO4UHCtJQAmpvDVry7o,20451
33
33
  posthog/ai/openai/openai_providers.py,sha256=zQIFTXHS2-dBKQX7FZxTFo7rIj5iiN7VHm9_2RzuDs8,3941
34
34
  posthog/integrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
35
  posthog/integrations/django.py,sha256=opHfViMcWTKFh_qQTavrNJ6qI0cc7-35UFA7dz0Lhfo,6777
@@ -47,8 +47,8 @@ posthog/test/test_request.py,sha256=l19WVyZQc4Iqmh_bpnAFOj4nGRpDK1iO-o5aJDQfFdo,
47
47
  posthog/test/test_size_limited_dict.py,sha256=Wom7BkzpHmusHilZy0SV3PNzhw7ucuQgqrx86jf8euo,765
48
48
  posthog/test/test_types.py,sha256=csLuBiz6RMV36cpg9LVIor4Khq6MfjjGxYXodx5VttY,7586
49
49
  posthog/test/test_utils.py,sha256=NUs2bgqrVuMdnKRq52syizgglt5_7wxxZl3dDMun-Tg,9602
50
- posthog-6.7.3.dist-info/licenses/LICENSE,sha256=wGf9JBotDkSygFj43m49oiKlFnpMnn97keiZKF-40vE,2450
51
- posthog-6.7.3.dist-info/METADATA,sha256=Yi44cRG2CaxBgCa4bNXO3vO8bZRYeBdRs6o3D_qyvzQ,6015
52
- posthog-6.7.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
53
- posthog-6.7.3.dist-info/top_level.txt,sha256=7FBLsRjIUHVKQsXIhozuI3k-mun1tapp8iZO9EmUPEw,8
54
- posthog-6.7.3.dist-info/RECORD,,
50
+ posthog-6.7.4.dist-info/licenses/LICENSE,sha256=wGf9JBotDkSygFj43m49oiKlFnpMnn97keiZKF-40vE,2450
51
+ posthog-6.7.4.dist-info/METADATA,sha256=GP-ydX6HKmTgg7Ez-49Av0knBrHXqkVGK4EW08ARj7s,6015
52
+ posthog-6.7.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
53
+ posthog-6.7.4.dist-info/top_level.txt,sha256=7FBLsRjIUHVKQsXIhozuI3k-mun1tapp8iZO9EmUPEw,8
54
+ posthog-6.7.4.dist-info/RECORD,,