posthoganalytics 7.5.1__py3-none-any.whl → 7.7.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.
- posthoganalytics/__init__.py +35 -0
- posthoganalytics/ai/openai_agents/__init__.py +76 -0
- posthoganalytics/ai/openai_agents/processor.py +863 -0
- posthoganalytics/client.py +38 -0
- posthoganalytics/contexts.py +44 -0
- posthoganalytics/test/ai/__init__.py +0 -0
- posthoganalytics/test/ai/openai_agents/__init__.py +1 -0
- posthoganalytics/test/ai/openai_agents/test_processor.py +810 -0
- posthoganalytics/test/ai/test_sanitization.py +522 -0
- posthoganalytics/test/ai/test_system_prompts.py +363 -0
- posthoganalytics/test/test_client.py +115 -0
- posthoganalytics/version.py +1 -1
- {posthoganalytics-7.5.1.dist-info → posthoganalytics-7.7.0.dist-info}/METADATA +1 -1
- {posthoganalytics-7.5.1.dist-info → posthoganalytics-7.7.0.dist-info}/RECORD +17 -10
- {posthoganalytics-7.5.1.dist-info → posthoganalytics-7.7.0.dist-info}/WHEEL +0 -0
- {posthoganalytics-7.5.1.dist-info → posthoganalytics-7.7.0.dist-info}/licenses/LICENSE +0 -0
- {posthoganalytics-7.5.1.dist-info → posthoganalytics-7.7.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tests for system prompt capture across all LLM providers.
|
|
3
|
+
|
|
4
|
+
This test suite ensures that system prompts are correctly captured in analytics
|
|
5
|
+
regardless of how they're passed to the providers:
|
|
6
|
+
- As first message in messages/contents array (standard format)
|
|
7
|
+
- As separate system parameter (Anthropic, OpenAI)
|
|
8
|
+
- As instructions parameter (OpenAI Responses API)
|
|
9
|
+
- As system_instruction parameter (Gemini)
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import time
|
|
13
|
+
import unittest
|
|
14
|
+
from unittest.mock import MagicMock, patch
|
|
15
|
+
|
|
16
|
+
from posthoganalytics.client import Client
|
|
17
|
+
from posthoganalytics.test.test_utils import FAKE_TEST_API_KEY
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class TestSystemPromptCapture(unittest.TestCase):
|
|
21
|
+
"""Test system prompt capture for all providers."""
|
|
22
|
+
|
|
23
|
+
def setUp(self):
|
|
24
|
+
super().setUp()
|
|
25
|
+
self.test_system_prompt = "You are a helpful AI assistant."
|
|
26
|
+
self.test_user_message = "Hello, how are you?"
|
|
27
|
+
self.test_response = "I'm doing well, thank you!"
|
|
28
|
+
|
|
29
|
+
# Create mock PostHog client
|
|
30
|
+
self.client = Client(FAKE_TEST_API_KEY)
|
|
31
|
+
self.client._enqueue = MagicMock()
|
|
32
|
+
self.client.privacy_mode = False
|
|
33
|
+
|
|
34
|
+
def _assert_system_prompt_captured(self, captured_input):
|
|
35
|
+
"""Helper to assert system prompt is correctly captured."""
|
|
36
|
+
self.assertEqual(
|
|
37
|
+
len(captured_input), 2, "Should have 2 messages (system + user)"
|
|
38
|
+
)
|
|
39
|
+
self.assertEqual(
|
|
40
|
+
captured_input[0]["role"], "system", "First message should be system"
|
|
41
|
+
)
|
|
42
|
+
self.assertEqual(
|
|
43
|
+
captured_input[0]["content"],
|
|
44
|
+
self.test_system_prompt,
|
|
45
|
+
"System content should match",
|
|
46
|
+
)
|
|
47
|
+
self.assertEqual(
|
|
48
|
+
captured_input[1]["role"], "user", "Second message should be user"
|
|
49
|
+
)
|
|
50
|
+
self.assertEqual(
|
|
51
|
+
captured_input[1]["content"],
|
|
52
|
+
self.test_user_message,
|
|
53
|
+
"User content should match",
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
# OpenAI Tests
|
|
57
|
+
def test_openai_messages_array_system_prompt(self):
|
|
58
|
+
"""Test OpenAI with system prompt in messages array."""
|
|
59
|
+
try:
|
|
60
|
+
from openai.types.chat import ChatCompletion, ChatCompletionMessage
|
|
61
|
+
from openai.types.chat.chat_completion import Choice
|
|
62
|
+
from openai.types.completion_usage import CompletionUsage
|
|
63
|
+
|
|
64
|
+
from posthoganalytics.ai.openai import OpenAI
|
|
65
|
+
except ImportError:
|
|
66
|
+
self.skipTest("OpenAI package not available")
|
|
67
|
+
|
|
68
|
+
mock_response = ChatCompletion(
|
|
69
|
+
id="test",
|
|
70
|
+
model="gpt-4",
|
|
71
|
+
object="chat.completion",
|
|
72
|
+
created=int(time.time()),
|
|
73
|
+
choices=[
|
|
74
|
+
Choice(
|
|
75
|
+
finish_reason="stop",
|
|
76
|
+
index=0,
|
|
77
|
+
message=ChatCompletionMessage(
|
|
78
|
+
content=self.test_response, role="assistant"
|
|
79
|
+
),
|
|
80
|
+
)
|
|
81
|
+
],
|
|
82
|
+
usage=CompletionUsage(
|
|
83
|
+
completion_tokens=10, prompt_tokens=20, total_tokens=30
|
|
84
|
+
),
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
with patch(
|
|
88
|
+
"openai.resources.chat.completions.Completions.create",
|
|
89
|
+
return_value=mock_response,
|
|
90
|
+
):
|
|
91
|
+
client = OpenAI(posthog_client=self.client, api_key="test")
|
|
92
|
+
|
|
93
|
+
messages = [
|
|
94
|
+
{"role": "system", "content": self.test_system_prompt},
|
|
95
|
+
{"role": "user", "content": self.test_user_message},
|
|
96
|
+
]
|
|
97
|
+
|
|
98
|
+
client.chat.completions.create(
|
|
99
|
+
model="gpt-4", messages=messages, posthog_distinct_id="test-user"
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
self.assertEqual(len(self.client._enqueue.call_args_list), 1)
|
|
103
|
+
properties = self.client._enqueue.call_args_list[0][0][0]["properties"]
|
|
104
|
+
self._assert_system_prompt_captured(properties["$ai_input"])
|
|
105
|
+
|
|
106
|
+
def test_openai_separate_system_parameter(self):
|
|
107
|
+
"""Test OpenAI with system prompt as separate parameter."""
|
|
108
|
+
try:
|
|
109
|
+
from openai.types.chat import ChatCompletion, ChatCompletionMessage
|
|
110
|
+
from openai.types.chat.chat_completion import Choice
|
|
111
|
+
from openai.types.completion_usage import CompletionUsage
|
|
112
|
+
|
|
113
|
+
from posthoganalytics.ai.openai import OpenAI
|
|
114
|
+
except ImportError:
|
|
115
|
+
self.skipTest("OpenAI package not available")
|
|
116
|
+
|
|
117
|
+
mock_response = ChatCompletion(
|
|
118
|
+
id="test",
|
|
119
|
+
model="gpt-4",
|
|
120
|
+
object="chat.completion",
|
|
121
|
+
created=int(time.time()),
|
|
122
|
+
choices=[
|
|
123
|
+
Choice(
|
|
124
|
+
finish_reason="stop",
|
|
125
|
+
index=0,
|
|
126
|
+
message=ChatCompletionMessage(
|
|
127
|
+
content=self.test_response, role="assistant"
|
|
128
|
+
),
|
|
129
|
+
)
|
|
130
|
+
],
|
|
131
|
+
usage=CompletionUsage(
|
|
132
|
+
completion_tokens=10, prompt_tokens=20, total_tokens=30
|
|
133
|
+
),
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
with patch(
|
|
137
|
+
"openai.resources.chat.completions.Completions.create",
|
|
138
|
+
return_value=mock_response,
|
|
139
|
+
):
|
|
140
|
+
client = OpenAI(posthog_client=self.client, api_key="test")
|
|
141
|
+
|
|
142
|
+
messages = [{"role": "user", "content": self.test_user_message}]
|
|
143
|
+
|
|
144
|
+
client.chat.completions.create(
|
|
145
|
+
model="gpt-4",
|
|
146
|
+
messages=messages,
|
|
147
|
+
system=self.test_system_prompt,
|
|
148
|
+
posthog_distinct_id="test-user",
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
self.assertEqual(len(self.client._enqueue.call_args_list), 1)
|
|
152
|
+
properties = self.client._enqueue.call_args_list[0][0][0]["properties"]
|
|
153
|
+
self._assert_system_prompt_captured(properties["$ai_input"])
|
|
154
|
+
|
|
155
|
+
def test_openai_streaming_system_parameter(self):
|
|
156
|
+
"""Test OpenAI streaming with system parameter."""
|
|
157
|
+
try:
|
|
158
|
+
from openai.types.chat.chat_completion_chunk import (
|
|
159
|
+
ChatCompletionChunk,
|
|
160
|
+
ChoiceDelta,
|
|
161
|
+
)
|
|
162
|
+
from openai.types.chat.chat_completion_chunk import Choice as ChoiceChunk
|
|
163
|
+
from openai.types.completion_usage import CompletionUsage
|
|
164
|
+
|
|
165
|
+
from posthoganalytics.ai.openai import OpenAI
|
|
166
|
+
except ImportError:
|
|
167
|
+
self.skipTest("OpenAI package not available")
|
|
168
|
+
|
|
169
|
+
chunk1 = ChatCompletionChunk(
|
|
170
|
+
id="test",
|
|
171
|
+
model="gpt-4",
|
|
172
|
+
object="chat.completion.chunk",
|
|
173
|
+
created=int(time.time()),
|
|
174
|
+
choices=[
|
|
175
|
+
ChoiceChunk(
|
|
176
|
+
finish_reason=None,
|
|
177
|
+
index=0,
|
|
178
|
+
delta=ChoiceDelta(content="Hello", role="assistant"),
|
|
179
|
+
)
|
|
180
|
+
],
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
chunk2 = ChatCompletionChunk(
|
|
184
|
+
id="test",
|
|
185
|
+
model="gpt-4",
|
|
186
|
+
object="chat.completion.chunk",
|
|
187
|
+
created=int(time.time()),
|
|
188
|
+
choices=[
|
|
189
|
+
ChoiceChunk(
|
|
190
|
+
finish_reason="stop",
|
|
191
|
+
index=0,
|
|
192
|
+
delta=ChoiceDelta(content=" there!", role=None),
|
|
193
|
+
)
|
|
194
|
+
],
|
|
195
|
+
usage=CompletionUsage(
|
|
196
|
+
completion_tokens=10, prompt_tokens=20, total_tokens=30
|
|
197
|
+
),
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
with patch(
|
|
201
|
+
"openai.resources.chat.completions.Completions.create",
|
|
202
|
+
return_value=[chunk1, chunk2],
|
|
203
|
+
):
|
|
204
|
+
client = OpenAI(posthog_client=self.client, api_key="test")
|
|
205
|
+
|
|
206
|
+
messages = [{"role": "user", "content": self.test_user_message}]
|
|
207
|
+
|
|
208
|
+
response_generator = client.chat.completions.create(
|
|
209
|
+
model="gpt-4",
|
|
210
|
+
messages=messages,
|
|
211
|
+
system=self.test_system_prompt,
|
|
212
|
+
stream=True,
|
|
213
|
+
posthog_distinct_id="test-user",
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
list(response_generator) # Consume generator
|
|
217
|
+
|
|
218
|
+
self.assertEqual(len(self.client._enqueue.call_args_list), 1)
|
|
219
|
+
properties = self.client._enqueue.call_args_list[0][0][0]["properties"]
|
|
220
|
+
self._assert_system_prompt_captured(properties["$ai_input"])
|
|
221
|
+
|
|
222
|
+
# Anthropic Tests
|
|
223
|
+
def test_anthropic_messages_array_system_prompt(self):
|
|
224
|
+
"""Test Anthropic with system prompt in messages array."""
|
|
225
|
+
try:
|
|
226
|
+
from posthoganalytics.ai.anthropic import Anthropic
|
|
227
|
+
except ImportError:
|
|
228
|
+
self.skipTest("Anthropic package not available")
|
|
229
|
+
|
|
230
|
+
with patch("anthropic.resources.messages.Messages.create") as mock_create:
|
|
231
|
+
mock_response = MagicMock()
|
|
232
|
+
mock_response.usage.input_tokens = 20
|
|
233
|
+
mock_response.usage.output_tokens = 10
|
|
234
|
+
mock_response.usage.cache_read_input_tokens = None
|
|
235
|
+
mock_response.usage.cache_creation_input_tokens = None
|
|
236
|
+
mock_create.return_value = mock_response
|
|
237
|
+
|
|
238
|
+
client = Anthropic(posthog_client=self.client, api_key="test")
|
|
239
|
+
|
|
240
|
+
messages = [
|
|
241
|
+
{"role": "system", "content": self.test_system_prompt},
|
|
242
|
+
{"role": "user", "content": self.test_user_message},
|
|
243
|
+
]
|
|
244
|
+
|
|
245
|
+
client.messages.create(
|
|
246
|
+
model="claude-3-5-sonnet-20241022",
|
|
247
|
+
messages=messages,
|
|
248
|
+
posthog_distinct_id="test-user",
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
self.assertEqual(len(self.client._enqueue.call_args_list), 1)
|
|
252
|
+
properties = self.client._enqueue.call_args_list[0][0][0]["properties"]
|
|
253
|
+
self._assert_system_prompt_captured(properties["$ai_input"])
|
|
254
|
+
|
|
255
|
+
def test_anthropic_separate_system_parameter(self):
|
|
256
|
+
"""Test Anthropic with system prompt as separate parameter."""
|
|
257
|
+
try:
|
|
258
|
+
from posthoganalytics.ai.anthropic import Anthropic
|
|
259
|
+
except ImportError:
|
|
260
|
+
self.skipTest("Anthropic package not available")
|
|
261
|
+
|
|
262
|
+
with patch("anthropic.resources.messages.Messages.create") as mock_create:
|
|
263
|
+
mock_response = MagicMock()
|
|
264
|
+
mock_response.usage.input_tokens = 20
|
|
265
|
+
mock_response.usage.output_tokens = 10
|
|
266
|
+
mock_response.usage.cache_read_input_tokens = None
|
|
267
|
+
mock_response.usage.cache_creation_input_tokens = None
|
|
268
|
+
mock_create.return_value = mock_response
|
|
269
|
+
|
|
270
|
+
client = Anthropic(posthog_client=self.client, api_key="test")
|
|
271
|
+
|
|
272
|
+
messages = [{"role": "user", "content": self.test_user_message}]
|
|
273
|
+
|
|
274
|
+
client.messages.create(
|
|
275
|
+
model="claude-3-5-sonnet-20241022",
|
|
276
|
+
messages=messages,
|
|
277
|
+
system=self.test_system_prompt,
|
|
278
|
+
posthog_distinct_id="test-user",
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
self.assertEqual(len(self.client._enqueue.call_args_list), 1)
|
|
282
|
+
properties = self.client._enqueue.call_args_list[0][0][0]["properties"]
|
|
283
|
+
self._assert_system_prompt_captured(properties["$ai_input"])
|
|
284
|
+
|
|
285
|
+
# Gemini Tests
|
|
286
|
+
def test_gemini_contents_array_system_prompt(self):
|
|
287
|
+
"""Test Gemini with system prompt in contents array."""
|
|
288
|
+
try:
|
|
289
|
+
from posthoganalytics.ai.gemini import Client
|
|
290
|
+
except ImportError:
|
|
291
|
+
self.skipTest("Gemini package not available")
|
|
292
|
+
|
|
293
|
+
with patch("google.genai.Client") as mock_genai_class:
|
|
294
|
+
mock_response = MagicMock()
|
|
295
|
+
mock_response.candidates = [MagicMock()]
|
|
296
|
+
mock_response.candidates[0].content.parts = [MagicMock()]
|
|
297
|
+
mock_response.candidates[0].content.parts[0].text = self.test_response
|
|
298
|
+
mock_response.usage_metadata.prompt_token_count = 20
|
|
299
|
+
mock_response.usage_metadata.candidates_token_count = 10
|
|
300
|
+
mock_response.usage_metadata.cached_content_token_count = None
|
|
301
|
+
mock_response.usage_metadata.thoughts_token_count = None
|
|
302
|
+
|
|
303
|
+
mock_client_instance = MagicMock()
|
|
304
|
+
mock_models_instance = MagicMock()
|
|
305
|
+
mock_models_instance.generate_content.return_value = mock_response
|
|
306
|
+
mock_client_instance.models = mock_models_instance
|
|
307
|
+
mock_genai_class.return_value = mock_client_instance
|
|
308
|
+
|
|
309
|
+
client = Client(posthog_client=self.client, api_key="test")
|
|
310
|
+
|
|
311
|
+
contents = [
|
|
312
|
+
{"role": "system", "content": self.test_system_prompt},
|
|
313
|
+
{"role": "user", "content": self.test_user_message},
|
|
314
|
+
]
|
|
315
|
+
|
|
316
|
+
client.models.generate_content(
|
|
317
|
+
model="gemini-2.0-flash",
|
|
318
|
+
contents=contents,
|
|
319
|
+
posthog_distinct_id="test-user",
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
self.assertEqual(len(self.client._enqueue.call_args_list), 1)
|
|
323
|
+
properties = self.client._enqueue.call_args_list[0][0][0]["properties"]
|
|
324
|
+
self._assert_system_prompt_captured(properties["$ai_input"])
|
|
325
|
+
|
|
326
|
+
def test_gemini_system_instruction_parameter(self):
|
|
327
|
+
"""Test Gemini with system_instruction in config parameter."""
|
|
328
|
+
try:
|
|
329
|
+
from posthoganalytics.ai.gemini import Client
|
|
330
|
+
except ImportError:
|
|
331
|
+
self.skipTest("Gemini package not available")
|
|
332
|
+
|
|
333
|
+
with patch("google.genai.Client") as mock_genai_class:
|
|
334
|
+
mock_response = MagicMock()
|
|
335
|
+
mock_response.candidates = [MagicMock()]
|
|
336
|
+
mock_response.candidates[0].content.parts = [MagicMock()]
|
|
337
|
+
mock_response.candidates[0].content.parts[0].text = self.test_response
|
|
338
|
+
mock_response.usage_metadata.prompt_token_count = 20
|
|
339
|
+
mock_response.usage_metadata.candidates_token_count = 10
|
|
340
|
+
mock_response.usage_metadata.cached_content_token_count = None
|
|
341
|
+
mock_response.usage_metadata.thoughts_token_count = None
|
|
342
|
+
|
|
343
|
+
mock_client_instance = MagicMock()
|
|
344
|
+
mock_models_instance = MagicMock()
|
|
345
|
+
mock_models_instance.generate_content.return_value = mock_response
|
|
346
|
+
mock_client_instance.models = mock_models_instance
|
|
347
|
+
mock_genai_class.return_value = mock_client_instance
|
|
348
|
+
|
|
349
|
+
client = Client(posthog_client=self.client, api_key="test")
|
|
350
|
+
|
|
351
|
+
contents = [{"role": "user", "content": self.test_user_message}]
|
|
352
|
+
config = {"system_instruction": self.test_system_prompt}
|
|
353
|
+
|
|
354
|
+
client.models.generate_content(
|
|
355
|
+
model="gemini-2.0-flash",
|
|
356
|
+
contents=contents,
|
|
357
|
+
config=config,
|
|
358
|
+
posthog_distinct_id="test-user",
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
self.assertEqual(len(self.client._enqueue.call_args_list), 1)
|
|
362
|
+
properties = self.client._enqueue.call_args_list[0][0][0]["properties"]
|
|
363
|
+
self._assert_system_prompt_captured(properties["$ai_input"])
|
|
@@ -648,6 +648,7 @@ class TestClient(unittest.TestCase):
|
|
|
648
648
|
person_properties={},
|
|
649
649
|
group_properties={},
|
|
650
650
|
geoip_disable=True,
|
|
651
|
+
device_id=None,
|
|
651
652
|
)
|
|
652
653
|
|
|
653
654
|
@mock.patch("posthog.client.flags")
|
|
@@ -712,6 +713,7 @@ class TestClient(unittest.TestCase):
|
|
|
712
713
|
person_properties={},
|
|
713
714
|
group_properties={},
|
|
714
715
|
geoip_disable=False,
|
|
716
|
+
device_id=None,
|
|
715
717
|
)
|
|
716
718
|
|
|
717
719
|
@mock.patch("posthog.client.flags")
|
|
@@ -1911,6 +1913,7 @@ class TestClient(unittest.TestCase):
|
|
|
1911
1913
|
person_properties={"distinct_id": "some_id"},
|
|
1912
1914
|
group_properties={},
|
|
1913
1915
|
geoip_disable=True,
|
|
1916
|
+
device_id=None,
|
|
1914
1917
|
flag_keys_to_evaluate=["random_key"],
|
|
1915
1918
|
)
|
|
1916
1919
|
patch_flags.reset_mock()
|
|
@@ -1926,6 +1929,7 @@ class TestClient(unittest.TestCase):
|
|
|
1926
1929
|
person_properties={"distinct_id": "feature_enabled_distinct_id"},
|
|
1927
1930
|
group_properties={},
|
|
1928
1931
|
geoip_disable=True,
|
|
1932
|
+
device_id=None,
|
|
1929
1933
|
flag_keys_to_evaluate=["random_key"],
|
|
1930
1934
|
)
|
|
1931
1935
|
patch_flags.reset_mock()
|
|
@@ -1939,6 +1943,7 @@ class TestClient(unittest.TestCase):
|
|
|
1939
1943
|
person_properties={"distinct_id": "all_flags_payloads_id"},
|
|
1940
1944
|
group_properties={},
|
|
1941
1945
|
geoip_disable=False,
|
|
1946
|
+
device_id=None,
|
|
1942
1947
|
)
|
|
1943
1948
|
|
|
1944
1949
|
@mock.patch("posthog.client.Poller")
|
|
@@ -1987,6 +1992,7 @@ class TestClient(unittest.TestCase):
|
|
|
1987
1992
|
"instance": {"$group_key": "app.posthog.com"},
|
|
1988
1993
|
},
|
|
1989
1994
|
geoip_disable=False,
|
|
1995
|
+
device_id=None,
|
|
1990
1996
|
flag_keys_to_evaluate=["random_key"],
|
|
1991
1997
|
)
|
|
1992
1998
|
|
|
@@ -2014,6 +2020,7 @@ class TestClient(unittest.TestCase):
|
|
|
2014
2020
|
"instance": {"$group_key": "app.posthog.com"},
|
|
2015
2021
|
},
|
|
2016
2022
|
geoip_disable=False,
|
|
2023
|
+
device_id=None,
|
|
2017
2024
|
flag_keys_to_evaluate=["random_key"],
|
|
2018
2025
|
)
|
|
2019
2026
|
|
|
@@ -2031,8 +2038,116 @@ class TestClient(unittest.TestCase):
|
|
|
2031
2038
|
person_properties={"distinct_id": "some_id"},
|
|
2032
2039
|
group_properties={},
|
|
2033
2040
|
geoip_disable=False,
|
|
2041
|
+
device_id=None,
|
|
2034
2042
|
)
|
|
2035
2043
|
|
|
2044
|
+
@parameterized.expand(
|
|
2045
|
+
[
|
|
2046
|
+
# method, method_args, expected_person_props, expected_flag_keys
|
|
2047
|
+
(
|
|
2048
|
+
"get_feature_flag",
|
|
2049
|
+
["random_key", "some_id"],
|
|
2050
|
+
{"distinct_id": "some_id"},
|
|
2051
|
+
["random_key"],
|
|
2052
|
+
),
|
|
2053
|
+
(
|
|
2054
|
+
"feature_enabled",
|
|
2055
|
+
["random_key", "some_id"],
|
|
2056
|
+
{"distinct_id": "some_id"},
|
|
2057
|
+
["random_key"],
|
|
2058
|
+
),
|
|
2059
|
+
(
|
|
2060
|
+
"get_all_flags_and_payloads",
|
|
2061
|
+
["some_id"],
|
|
2062
|
+
{"distinct_id": "some_id"},
|
|
2063
|
+
None,
|
|
2064
|
+
),
|
|
2065
|
+
("get_all_flags", ["some_id"], {"distinct_id": "some_id"}, None),
|
|
2066
|
+
("get_flags_decision", ["some_id"], {}, None),
|
|
2067
|
+
]
|
|
2068
|
+
)
|
|
2069
|
+
@mock.patch("posthog.client.flags")
|
|
2070
|
+
def test_device_id_is_passed_to_flags_request(
|
|
2071
|
+
self,
|
|
2072
|
+
method,
|
|
2073
|
+
method_args,
|
|
2074
|
+
expected_person_props,
|
|
2075
|
+
expected_flag_keys,
|
|
2076
|
+
patch_flags,
|
|
2077
|
+
):
|
|
2078
|
+
"""Test that device_id is properly passed to the flags request when provided."""
|
|
2079
|
+
patch_flags.return_value = {"featureFlags": {"beta-feature": "random-variant"}}
|
|
2080
|
+
client = Client(FAKE_TEST_API_KEY, on_error=self.set_fail)
|
|
2081
|
+
|
|
2082
|
+
getattr(client, method)(*method_args, device_id="test-device-123")
|
|
2083
|
+
|
|
2084
|
+
expected_call = {
|
|
2085
|
+
"distinct_id": "some_id",
|
|
2086
|
+
"groups": {},
|
|
2087
|
+
"person_properties": expected_person_props,
|
|
2088
|
+
"group_properties": {},
|
|
2089
|
+
"geoip_disable": True,
|
|
2090
|
+
"device_id": "test-device-123",
|
|
2091
|
+
}
|
|
2092
|
+
if expected_flag_keys:
|
|
2093
|
+
expected_call["flag_keys_to_evaluate"] = expected_flag_keys
|
|
2094
|
+
|
|
2095
|
+
patch_flags.assert_called_with(
|
|
2096
|
+
"random_key", "https://us.i.posthog.com", timeout=3, **expected_call
|
|
2097
|
+
)
|
|
2098
|
+
|
|
2099
|
+
@mock.patch("posthog.client.flags")
|
|
2100
|
+
def test_device_id_from_context_is_used_in_flags_request(self, patch_flags):
|
|
2101
|
+
"""Test that device_id from context is used in flags request when not explicitly provided."""
|
|
2102
|
+
from posthoganalytics.contexts import new_context, set_context_device_id
|
|
2103
|
+
|
|
2104
|
+
patch_flags.return_value = {
|
|
2105
|
+
"featureFlags": {
|
|
2106
|
+
"beta-feature": "random-variant",
|
|
2107
|
+
}
|
|
2108
|
+
}
|
|
2109
|
+
client = Client(
|
|
2110
|
+
FAKE_TEST_API_KEY,
|
|
2111
|
+
on_error=self.set_fail,
|
|
2112
|
+
)
|
|
2113
|
+
|
|
2114
|
+
# Test that device_id from context is used
|
|
2115
|
+
with new_context():
|
|
2116
|
+
set_context_device_id("context-device-id")
|
|
2117
|
+
client.get_feature_flag("random_key", "some_id")
|
|
2118
|
+
patch_flags.assert_called_with(
|
|
2119
|
+
"random_key",
|
|
2120
|
+
"https://us.i.posthog.com",
|
|
2121
|
+
timeout=3,
|
|
2122
|
+
distinct_id="some_id",
|
|
2123
|
+
groups={},
|
|
2124
|
+
person_properties={"distinct_id": "some_id"},
|
|
2125
|
+
group_properties={},
|
|
2126
|
+
geoip_disable=True,
|
|
2127
|
+
device_id="context-device-id",
|
|
2128
|
+
flag_keys_to_evaluate=["random_key"],
|
|
2129
|
+
)
|
|
2130
|
+
|
|
2131
|
+
# Test that explicit device_id overrides context
|
|
2132
|
+
patch_flags.reset_mock()
|
|
2133
|
+
with new_context():
|
|
2134
|
+
set_context_device_id("context-device-id")
|
|
2135
|
+
client.get_feature_flag(
|
|
2136
|
+
"random_key", "some_id", device_id="explicit-device-id"
|
|
2137
|
+
)
|
|
2138
|
+
patch_flags.assert_called_with(
|
|
2139
|
+
"random_key",
|
|
2140
|
+
"https://us.i.posthog.com",
|
|
2141
|
+
timeout=3,
|
|
2142
|
+
distinct_id="some_id",
|
|
2143
|
+
groups={},
|
|
2144
|
+
person_properties={"distinct_id": "some_id"},
|
|
2145
|
+
group_properties={},
|
|
2146
|
+
geoip_disable=True,
|
|
2147
|
+
device_id="explicit-device-id",
|
|
2148
|
+
flag_keys_to_evaluate=["random_key"],
|
|
2149
|
+
)
|
|
2150
|
+
|
|
2036
2151
|
@parameterized.expand(
|
|
2037
2152
|
[
|
|
2038
2153
|
# name, sys_platform, version_info, expected_runtime, expected_version, expected_os, expected_os_version, platform_method, platform_return, distro_info
|
posthoganalytics/version.py
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
posthoganalytics/__init__.py,sha256=
|
|
1
|
+
posthoganalytics/__init__.py,sha256=GSkVQIVucFkyQFa_yQOTzD7oUVXm0wpafF79AQMQvVU,30132
|
|
2
2
|
posthoganalytics/args.py,sha256=iZ2JWeANiAREJKhS-Qls9tIngjJOSfAVR8C4xFT5sHw,3307
|
|
3
|
-
posthoganalytics/client.py,sha256=
|
|
3
|
+
posthoganalytics/client.py,sha256=cL4YVShGxsUF6ymSlSyDq3Tcj8cAKVZcYxpsrHtSB3g,83935
|
|
4
4
|
posthoganalytics/consumer.py,sha256=vfc5KrCaWTwyizH0oxAH6gFBtbRwpSk_YMxqzSPoCNg,4747
|
|
5
|
-
posthoganalytics/contexts.py,sha256=
|
|
5
|
+
posthoganalytics/contexts.py,sha256=F9gJY28gbGLE8w8I9prm17wH0a5VEjTwg1Iz7PlIayc,13830
|
|
6
6
|
posthoganalytics/exception_capture.py,sha256=1VHBfffrXXrkK0PT8iVgKPpj_R1pGAzG5f3Qw0WF79w,1783
|
|
7
7
|
posthoganalytics/exception_utils.py,sha256=dO2XYCl78db-4_FHONS9aOybOnxEI5poX3XkfLthXgo,33747
|
|
8
8
|
posthoganalytics/feature_flags.py,sha256=yHjiH6LSvhQgurbsPCHUdGakZKvkzOLdqB8vL3iyhmw,22544
|
|
@@ -12,7 +12,7 @@ posthoganalytics/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
12
12
|
posthoganalytics/request.py,sha256=sv5dVU4jg4nFI4BpGCGvyoiz6yZZBLMqoBjhmzoomYo,11844
|
|
13
13
|
posthoganalytics/types.py,sha256=OxGHSmmhVYwA7ecmJXUznDCZ1c4gAGtERzSLSYlyQFM,11540
|
|
14
14
|
posthoganalytics/utils.py,sha256=-0w-OLcCaoldkbBebPzQyBzLJSo9G9yBOg8NDVz7La8,16088
|
|
15
|
-
posthoganalytics/version.py,sha256=
|
|
15
|
+
posthoganalytics/version.py,sha256=N71Rzf8IDjCmTh3rPlaceoLNhCzH4fhLpEfObPtcmmM,87
|
|
16
16
|
posthoganalytics/ai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
17
|
posthoganalytics/ai/sanitization.py,sha256=Dpx_5gKZfDS38KjmK1C0lvvjm9N8Pp_oIxusac888-g,6057
|
|
18
18
|
posthoganalytics/ai/types.py,sha256=arX98hR1PIPeJ3vFikxTlACIh1xPp6aEUw1gBLcKoB0,3273
|
|
@@ -33,11 +33,13 @@ posthoganalytics/ai/openai/openai.py,sha256=UTHmlOyy2yOKP3MDbQLV25BH0xe2miF_9z8l
|
|
|
33
33
|
posthoganalytics/ai/openai/openai_async.py,sha256=R1vbRrLDQuvZ3v9TOZAEuioeh21XJ_69zevhatIyVto,23709
|
|
34
34
|
posthoganalytics/ai/openai/openai_converter.py,sha256=2xl1ZkCGiM5BCTu4RPwtRVYZg5lVQNJsxUzFlHfkuIk,25846
|
|
35
35
|
posthoganalytics/ai/openai/openai_providers.py,sha256=RPVmj2V0_lAdno_ax5Ul2kwhBA9_rRgAdl_sCqrQc6M,4004
|
|
36
|
+
posthoganalytics/ai/openai_agents/__init__.py,sha256=i12Gy9SlB_7Oqwk8lp2-WFjXiy_NlTr5swvE_mCkMRc,2520
|
|
37
|
+
posthoganalytics/ai/openai_agents/processor.py,sha256=gL_PHj6foOi5wbAvW2B6oTQibVGg66a6k8nKVEXlf2o,31497
|
|
36
38
|
posthoganalytics/integrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
37
39
|
posthoganalytics/integrations/django.py,sha256=aJ_fLjeMqnnF01Zp8N3c9OeXvWwDL_X_o7aqhlw3e5U,12660
|
|
38
40
|
posthoganalytics/test/__init__.py,sha256=VYgM6xPbJbvS-xhIcDiBRs0MFC9V_jT65uNeerCz_rM,299
|
|
39
41
|
posthoganalytics/test/test_before_send.py,sha256=A1_UVMewhHAvO39rZDWfS606vG_X-q0KNXvh5DAKiB8,7930
|
|
40
|
-
posthoganalytics/test/test_client.py,sha256=
|
|
42
|
+
posthoganalytics/test/test_client.py,sha256=yjhyxZ1WE3pEKwj2L8v4fJofie9JOF6Vrs_DaSjCCBM,106989
|
|
41
43
|
posthoganalytics/test/test_consumer.py,sha256=OLCHnqZW1C-1KQptwpRmOJPL_mPRxo4G0Gr7v0ZGAIM,7018
|
|
42
44
|
posthoganalytics/test/test_contexts.py,sha256=Mb0XRMQNCdkK7lKWMPMc9fv1QCWvZzj8eqTd6SuD6C8,8104
|
|
43
45
|
posthoganalytics/test/test_exception_capture.py,sha256=s_hUphovgQoGeOwtElAJiZFxgGrvaNRhgSqLU9ZaBhc,13368
|
|
@@ -50,8 +52,13 @@ posthoganalytics/test/test_request.py,sha256=1wdNXbSpTVieYMAzEwicahvk6RfvvTiRdqG
|
|
|
50
52
|
posthoganalytics/test/test_size_limited_dict.py,sha256=-5IQjIEr_-Dql24M0HusdR_XroOMrtgiT0v6ZQCRvzo,774
|
|
51
53
|
posthoganalytics/test/test_types.py,sha256=bRPHdwVpP7hu7emsplU8UVyzSQptv6PaG5lAoOD_BtM,7595
|
|
52
54
|
posthoganalytics/test/test_utils.py,sha256=MTz7-Fvffz2a9IRwyKsVy_TnrvIihs-Ap3hhtqGSSAs,9732
|
|
53
|
-
posthoganalytics
|
|
54
|
-
posthoganalytics
|
|
55
|
-
posthoganalytics
|
|
56
|
-
posthoganalytics
|
|
57
|
-
posthoganalytics
|
|
55
|
+
posthoganalytics/test/ai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
56
|
+
posthoganalytics/test/ai/test_sanitization.py,sha256=Om1f22Z0q5Y4VSaDwPHgW3IYUZPoMj05ZYcTeCmOBlE,18155
|
|
57
|
+
posthoganalytics/test/ai/test_system_prompts.py,sha256=1IQVvcs-YMxPxXOmkbNCFOhvu_NIx4eAanz1XT-QJ3Y,14371
|
|
58
|
+
posthoganalytics/test/ai/openai_agents/__init__.py,sha256=VGLVcRkGkmj0d4MhjcwQ5IYxoaaMPlw0oR7eXSCcGXI,42
|
|
59
|
+
posthoganalytics/test/ai/openai_agents/test_processor.py,sha256=p65z82yiVjMQUR5coaBMMhzV6xB1CezzsvQD1GIi4o0,30800
|
|
60
|
+
posthoganalytics-7.7.0.dist-info/licenses/LICENSE,sha256=wGf9JBotDkSygFj43m49oiKlFnpMnn97keiZKF-40vE,2450
|
|
61
|
+
posthoganalytics-7.7.0.dist-info/METADATA,sha256=gcHpMd9BOdead_UNaQDqa_RrOMz6MTLBkQWr1i0bNZo,6368
|
|
62
|
+
posthoganalytics-7.7.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
63
|
+
posthoganalytics-7.7.0.dist-info/top_level.txt,sha256=8QsNIqIkBh1p2TXvKp0Em9ZLZKwe3uIqCETyW4s1GOE,17
|
|
64
|
+
posthoganalytics-7.7.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|