posthog 7.0.0__py3-none-any.whl → 7.3.1__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.
- posthog/__init__.py +10 -0
- posthog/ai/gemini/__init__.py +3 -0
- posthog/ai/gemini/gemini.py +1 -1
- posthog/ai/gemini/gemini_async.py +423 -0
- posthog/ai/gemini/gemini_converter.py +87 -21
- posthog/ai/openai/openai_converter.py +6 -0
- posthog/ai/sanitization.py +27 -5
- posthog/client.py +216 -49
- posthog/exception_utils.py +71 -15
- posthog/flag_definition_cache.py +127 -0
- posthog/request.py +152 -21
- posthog/test/test_client.py +121 -21
- posthog/test/test_exception_capture.py +120 -2
- posthog/test/test_feature_flag_result.py +441 -2
- posthog/test/test_feature_flags.py +157 -18
- posthog/test/test_flag_definition_cache.py +612 -0
- posthog/test/test_request.py +265 -0
- posthog/test/test_utils.py +4 -1
- posthog/types.py +40 -0
- posthog/version.py +1 -1
- {posthog-7.0.0.dist-info → posthog-7.3.1.dist-info}/METADATA +2 -1
- {posthog-7.0.0.dist-info → posthog-7.3.1.dist-info}/RECORD +25 -22
- {posthog-7.0.0.dist-info → posthog-7.3.1.dist-info}/WHEEL +0 -0
- {posthog-7.0.0.dist-info → posthog-7.3.1.dist-info}/licenses/LICENSE +0 -0
- {posthog-7.0.0.dist-info → posthog-7.3.1.dist-info}/top_level.txt +0 -0
posthog/test/test_request.py
CHANGED
|
@@ -6,16 +6,59 @@ import mock
|
|
|
6
6
|
import pytest
|
|
7
7
|
import requests
|
|
8
8
|
|
|
9
|
+
import posthog.request as request_module
|
|
9
10
|
from posthog.request import (
|
|
11
|
+
APIError,
|
|
10
12
|
DatetimeSerializer,
|
|
13
|
+
GetResponse,
|
|
14
|
+
KEEP_ALIVE_SOCKET_OPTIONS,
|
|
11
15
|
QuotaLimitError,
|
|
16
|
+
_mask_tokens_in_url,
|
|
12
17
|
batch_post,
|
|
13
18
|
decide,
|
|
14
19
|
determine_server_host,
|
|
20
|
+
disable_connection_reuse,
|
|
21
|
+
enable_keep_alive,
|
|
22
|
+
get,
|
|
23
|
+
set_socket_options,
|
|
15
24
|
)
|
|
16
25
|
from posthog.test.test_utils import TEST_API_KEY
|
|
17
26
|
|
|
18
27
|
|
|
28
|
+
@pytest.mark.parametrize(
|
|
29
|
+
"url, expected",
|
|
30
|
+
[
|
|
31
|
+
# Token with params after - masks keeping first 10 chars
|
|
32
|
+
(
|
|
33
|
+
"https://example.com/api/flags?token=phc_abc123xyz789&send_cohorts",
|
|
34
|
+
"https://example.com/api/flags?token=phc_abc123...&send_cohorts",
|
|
35
|
+
),
|
|
36
|
+
# Token at end of URL
|
|
37
|
+
(
|
|
38
|
+
"https://example.com/api/flags?token=phc_abc123xyz789",
|
|
39
|
+
"https://example.com/api/flags?token=phc_abc123...",
|
|
40
|
+
),
|
|
41
|
+
# No token - unchanged
|
|
42
|
+
(
|
|
43
|
+
"https://example.com/api/flags?other=value",
|
|
44
|
+
"https://example.com/api/flags?other=value",
|
|
45
|
+
),
|
|
46
|
+
# Short token (<10 chars) - unchanged
|
|
47
|
+
(
|
|
48
|
+
"https://example.com/api/flags?token=short",
|
|
49
|
+
"https://example.com/api/flags?token=short",
|
|
50
|
+
),
|
|
51
|
+
# Exactly 10 char token - gets ellipsis
|
|
52
|
+
(
|
|
53
|
+
"https://example.com/api/flags?token=1234567890",
|
|
54
|
+
"https://example.com/api/flags?token=1234567890...",
|
|
55
|
+
),
|
|
56
|
+
],
|
|
57
|
+
)
|
|
58
|
+
def test_mask_tokens_in_url(url, expected):
|
|
59
|
+
assert _mask_tokens_in_url(url) == expected
|
|
60
|
+
|
|
61
|
+
|
|
19
62
|
class TestRequests(unittest.TestCase):
|
|
20
63
|
def test_valid_request(self):
|
|
21
64
|
res = batch_post(
|
|
@@ -107,6 +150,184 @@ class TestRequests(unittest.TestCase):
|
|
|
107
150
|
self.assertEqual(response["featureFlags"], {"flag1": True})
|
|
108
151
|
|
|
109
152
|
|
|
153
|
+
class TestGet(unittest.TestCase):
|
|
154
|
+
"""Unit tests for the get() function HTTP-level behavior."""
|
|
155
|
+
|
|
156
|
+
@mock.patch("posthog.request._session.get")
|
|
157
|
+
def test_get_returns_data_and_etag(self, mock_get):
|
|
158
|
+
"""Test that get() returns GetResponse with data and etag from headers."""
|
|
159
|
+
mock_response = requests.Response()
|
|
160
|
+
mock_response.status_code = 200
|
|
161
|
+
mock_response.headers["ETag"] = '"abc123"'
|
|
162
|
+
mock_response._content = json.dumps({"flags": [{"key": "test-flag"}]}).encode(
|
|
163
|
+
"utf-8"
|
|
164
|
+
)
|
|
165
|
+
mock_get.return_value = mock_response
|
|
166
|
+
|
|
167
|
+
response = get("api_key", "/test-url", host="https://example.com")
|
|
168
|
+
|
|
169
|
+
self.assertIsInstance(response, GetResponse)
|
|
170
|
+
self.assertEqual(response.data, {"flags": [{"key": "test-flag"}]})
|
|
171
|
+
self.assertEqual(response.etag, '"abc123"')
|
|
172
|
+
self.assertFalse(response.not_modified)
|
|
173
|
+
|
|
174
|
+
@mock.patch("posthog.request._session.get")
|
|
175
|
+
def test_get_sends_if_none_match_header_when_etag_provided(self, mock_get):
|
|
176
|
+
"""Test that If-None-Match header is sent when etag parameter is provided."""
|
|
177
|
+
mock_response = requests.Response()
|
|
178
|
+
mock_response.status_code = 200
|
|
179
|
+
mock_response.headers["ETag"] = '"new-etag"'
|
|
180
|
+
mock_response._content = json.dumps({"flags": []}).encode("utf-8")
|
|
181
|
+
mock_get.return_value = mock_response
|
|
182
|
+
|
|
183
|
+
get("api_key", "/test-url", host="https://example.com", etag='"previous-etag"')
|
|
184
|
+
|
|
185
|
+
call_kwargs = mock_get.call_args[1]
|
|
186
|
+
self.assertEqual(call_kwargs["headers"]["If-None-Match"], '"previous-etag"')
|
|
187
|
+
|
|
188
|
+
@mock.patch("posthog.request._session.get")
|
|
189
|
+
def test_get_does_not_send_if_none_match_when_no_etag(self, mock_get):
|
|
190
|
+
"""Test that If-None-Match header is not sent when no etag provided."""
|
|
191
|
+
mock_response = requests.Response()
|
|
192
|
+
mock_response.status_code = 200
|
|
193
|
+
mock_response._content = json.dumps({"flags": []}).encode("utf-8")
|
|
194
|
+
mock_get.return_value = mock_response
|
|
195
|
+
|
|
196
|
+
get("api_key", "/test-url", host="https://example.com")
|
|
197
|
+
|
|
198
|
+
call_kwargs = mock_get.call_args[1]
|
|
199
|
+
self.assertNotIn("If-None-Match", call_kwargs["headers"])
|
|
200
|
+
|
|
201
|
+
@mock.patch("posthog.request._session.get")
|
|
202
|
+
def test_get_handles_304_not_modified(self, mock_get):
|
|
203
|
+
"""Test that 304 Not Modified response returns not_modified=True with no data."""
|
|
204
|
+
mock_response = requests.Response()
|
|
205
|
+
mock_response.status_code = 304
|
|
206
|
+
mock_response.headers["ETag"] = '"unchanged-etag"'
|
|
207
|
+
mock_get.return_value = mock_response
|
|
208
|
+
|
|
209
|
+
response = get(
|
|
210
|
+
"api_key", "/test-url", host="https://example.com", etag='"unchanged-etag"'
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
self.assertIsInstance(response, GetResponse)
|
|
214
|
+
self.assertIsNone(response.data)
|
|
215
|
+
self.assertEqual(response.etag, '"unchanged-etag"')
|
|
216
|
+
self.assertTrue(response.not_modified)
|
|
217
|
+
|
|
218
|
+
@mock.patch("posthog.request._session.get")
|
|
219
|
+
def test_get_304_without_etag_header_uses_request_etag(self, mock_get):
|
|
220
|
+
"""Test that 304 response without ETag header falls back to request etag."""
|
|
221
|
+
mock_response = requests.Response()
|
|
222
|
+
mock_response.status_code = 304
|
|
223
|
+
# Server doesn't return ETag header on 304
|
|
224
|
+
mock_get.return_value = mock_response
|
|
225
|
+
|
|
226
|
+
response = get(
|
|
227
|
+
"api_key", "/test-url", host="https://example.com", etag='"original-etag"'
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
self.assertTrue(response.not_modified)
|
|
231
|
+
self.assertEqual(response.etag, '"original-etag"')
|
|
232
|
+
|
|
233
|
+
@mock.patch("posthog.request._session.get")
|
|
234
|
+
def test_get_200_without_etag_header(self, mock_get):
|
|
235
|
+
"""Test that 200 response without ETag header returns None for etag."""
|
|
236
|
+
mock_response = requests.Response()
|
|
237
|
+
mock_response.status_code = 200
|
|
238
|
+
mock_response._content = json.dumps({"flags": []}).encode("utf-8")
|
|
239
|
+
# No ETag header
|
|
240
|
+
mock_get.return_value = mock_response
|
|
241
|
+
|
|
242
|
+
response = get("api_key", "/test-url", host="https://example.com")
|
|
243
|
+
|
|
244
|
+
self.assertFalse(response.not_modified)
|
|
245
|
+
self.assertIsNone(response.etag)
|
|
246
|
+
self.assertEqual(response.data, {"flags": []})
|
|
247
|
+
|
|
248
|
+
@mock.patch("posthog.request._session.get")
|
|
249
|
+
def test_get_error_response_raises_api_error(self, mock_get):
|
|
250
|
+
"""Test that error responses raise APIError."""
|
|
251
|
+
mock_response = requests.Response()
|
|
252
|
+
mock_response.status_code = 401
|
|
253
|
+
mock_response._content = json.dumps({"detail": "Unauthorized"}).encode("utf-8")
|
|
254
|
+
mock_get.return_value = mock_response
|
|
255
|
+
|
|
256
|
+
with self.assertRaises(APIError) as ctx:
|
|
257
|
+
get("bad_key", "/test-url", host="https://example.com")
|
|
258
|
+
|
|
259
|
+
self.assertEqual(ctx.exception.status, 401)
|
|
260
|
+
self.assertEqual(ctx.exception.message, "Unauthorized")
|
|
261
|
+
|
|
262
|
+
@mock.patch("posthog.request._session.get")
|
|
263
|
+
def test_get_sends_authorization_header(self, mock_get):
|
|
264
|
+
"""Test that Authorization header is sent with Bearer token."""
|
|
265
|
+
mock_response = requests.Response()
|
|
266
|
+
mock_response.status_code = 200
|
|
267
|
+
mock_response._content = json.dumps({}).encode("utf-8")
|
|
268
|
+
mock_get.return_value = mock_response
|
|
269
|
+
|
|
270
|
+
get("my-api-key", "/test-url", host="https://example.com")
|
|
271
|
+
|
|
272
|
+
call_kwargs = mock_get.call_args[1]
|
|
273
|
+
self.assertEqual(call_kwargs["headers"]["Authorization"], "Bearer my-api-key")
|
|
274
|
+
|
|
275
|
+
@mock.patch("posthog.request._session.get")
|
|
276
|
+
def test_get_sends_user_agent_header(self, mock_get):
|
|
277
|
+
"""Test that User-Agent header is sent."""
|
|
278
|
+
mock_response = requests.Response()
|
|
279
|
+
mock_response.status_code = 200
|
|
280
|
+
mock_response._content = json.dumps({}).encode("utf-8")
|
|
281
|
+
mock_get.return_value = mock_response
|
|
282
|
+
|
|
283
|
+
get("api_key", "/test-url", host="https://example.com")
|
|
284
|
+
|
|
285
|
+
call_kwargs = mock_get.call_args[1]
|
|
286
|
+
self.assertIn("User-Agent", call_kwargs["headers"])
|
|
287
|
+
self.assertTrue(
|
|
288
|
+
call_kwargs["headers"]["User-Agent"].startswith("posthog-python/")
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
@mock.patch("posthog.request._session.get")
|
|
292
|
+
def test_get_passes_timeout(self, mock_get):
|
|
293
|
+
"""Test that timeout parameter is passed to the request."""
|
|
294
|
+
mock_response = requests.Response()
|
|
295
|
+
mock_response.status_code = 200
|
|
296
|
+
mock_response._content = json.dumps({}).encode("utf-8")
|
|
297
|
+
mock_get.return_value = mock_response
|
|
298
|
+
|
|
299
|
+
get("api_key", "/test-url", host="https://example.com", timeout=30)
|
|
300
|
+
|
|
301
|
+
call_kwargs = mock_get.call_args[1]
|
|
302
|
+
self.assertEqual(call_kwargs["timeout"], 30)
|
|
303
|
+
|
|
304
|
+
@mock.patch("posthog.request._session.get")
|
|
305
|
+
def test_get_constructs_full_url(self, mock_get):
|
|
306
|
+
"""Test that host and url are combined correctly."""
|
|
307
|
+
mock_response = requests.Response()
|
|
308
|
+
mock_response.status_code = 200
|
|
309
|
+
mock_response._content = json.dumps({}).encode("utf-8")
|
|
310
|
+
mock_get.return_value = mock_response
|
|
311
|
+
|
|
312
|
+
get("api_key", "/api/flags", host="https://example.com")
|
|
313
|
+
|
|
314
|
+
call_args = mock_get.call_args[0]
|
|
315
|
+
self.assertEqual(call_args[0], "https://example.com/api/flags")
|
|
316
|
+
|
|
317
|
+
@mock.patch("posthog.request._session.get")
|
|
318
|
+
def test_get_removes_trailing_slash_from_host(self, mock_get):
|
|
319
|
+
"""Test that trailing slash is removed from host."""
|
|
320
|
+
mock_response = requests.Response()
|
|
321
|
+
mock_response.status_code = 200
|
|
322
|
+
mock_response._content = json.dumps({}).encode("utf-8")
|
|
323
|
+
mock_get.return_value = mock_response
|
|
324
|
+
|
|
325
|
+
get("api_key", "/api/flags", host="https://example.com/")
|
|
326
|
+
|
|
327
|
+
call_args = mock_get.call_args[0]
|
|
328
|
+
self.assertEqual(call_args[0], "https://example.com/api/flags")
|
|
329
|
+
|
|
330
|
+
|
|
110
331
|
@pytest.mark.parametrize(
|
|
111
332
|
"host, expected",
|
|
112
333
|
[
|
|
@@ -128,3 +349,47 @@ class TestRequests(unittest.TestCase):
|
|
|
128
349
|
)
|
|
129
350
|
def test_routing_to_custom_host(host, expected):
|
|
130
351
|
assert determine_server_host(host) == expected
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
def test_enable_keep_alive_sets_socket_options():
|
|
355
|
+
try:
|
|
356
|
+
enable_keep_alive()
|
|
357
|
+
from posthog.request import _session
|
|
358
|
+
|
|
359
|
+
adapter = _session.get_adapter("https://example.com")
|
|
360
|
+
assert adapter.socket_options == KEEP_ALIVE_SOCKET_OPTIONS
|
|
361
|
+
finally:
|
|
362
|
+
set_socket_options(None)
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
def test_set_socket_options_clears_with_none():
|
|
366
|
+
try:
|
|
367
|
+
enable_keep_alive()
|
|
368
|
+
set_socket_options(None)
|
|
369
|
+
from posthog.request import _session
|
|
370
|
+
|
|
371
|
+
adapter = _session.get_adapter("https://example.com")
|
|
372
|
+
assert adapter.socket_options is None
|
|
373
|
+
finally:
|
|
374
|
+
set_socket_options(None)
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
def test_disable_connection_reuse_creates_fresh_sessions():
|
|
378
|
+
try:
|
|
379
|
+
disable_connection_reuse()
|
|
380
|
+
session1 = request_module._get_session()
|
|
381
|
+
session2 = request_module._get_session()
|
|
382
|
+
assert session1 is not session2
|
|
383
|
+
finally:
|
|
384
|
+
request_module._pooling_enabled = True
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
def test_set_socket_options_is_idempotent():
|
|
388
|
+
try:
|
|
389
|
+
enable_keep_alive()
|
|
390
|
+
session1 = request_module._session
|
|
391
|
+
enable_keep_alive()
|
|
392
|
+
session2 = request_module._session
|
|
393
|
+
assert session1 is session2
|
|
394
|
+
finally:
|
|
395
|
+
set_socket_options(None)
|
posthog/test/test_utils.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import sys
|
|
1
2
|
import time
|
|
2
3
|
import unittest
|
|
3
4
|
from dataclasses import dataclass
|
|
@@ -122,7 +123,9 @@ class TestUtils(unittest.TestCase):
|
|
|
122
123
|
"bar": 2,
|
|
123
124
|
"baz": None,
|
|
124
125
|
}
|
|
125
|
-
|
|
126
|
+
# Pydantic V1 is not compatible with Python 3.14+
|
|
127
|
+
if sys.version_info < (3, 14):
|
|
128
|
+
assert utils.clean(ModelV1(foo=1, bar="2")) == {"foo": 1, "bar": "2"}
|
|
126
129
|
assert utils.clean(NestedModel(foo=ModelV2(foo="1", bar=2, baz="3"))) == {
|
|
127
130
|
"foo": {"foo": "1", "bar": 2, "baz": "3"}
|
|
128
131
|
}
|
posthog/types.py
CHANGED
|
@@ -123,6 +123,7 @@ class FlagsResponse(TypedDict, total=False):
|
|
|
123
123
|
errorsWhileComputingFlags: bool
|
|
124
124
|
requestId: str
|
|
125
125
|
quotaLimit: Optional[List[str]]
|
|
126
|
+
evaluatedAt: Optional[int]
|
|
126
127
|
|
|
127
128
|
|
|
128
129
|
class FlagsAndPayloads(TypedDict, total=True):
|
|
@@ -306,3 +307,42 @@ def to_payloads(response: FlagsResponse) -> Optional[dict[str, str]]:
|
|
|
306
307
|
and value.enabled
|
|
307
308
|
and value.metadata.payload is not None
|
|
308
309
|
}
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
class FeatureFlagError:
|
|
313
|
+
"""Error type constants for the $feature_flag_error property.
|
|
314
|
+
|
|
315
|
+
These values are sent in analytics events to track flag evaluation failures.
|
|
316
|
+
They should not be changed without considering impact on existing dashboards
|
|
317
|
+
and queries that filter on these values.
|
|
318
|
+
|
|
319
|
+
Error values:
|
|
320
|
+
ERRORS_WHILE_COMPUTING: Server returned errorsWhileComputingFlags=true
|
|
321
|
+
FLAG_MISSING: Requested flag not in API response
|
|
322
|
+
QUOTA_LIMITED: Rate/quota limit exceeded
|
|
323
|
+
TIMEOUT: Request timed out
|
|
324
|
+
CONNECTION_ERROR: Network connectivity issue
|
|
325
|
+
UNKNOWN_ERROR: Unexpected exceptions
|
|
326
|
+
|
|
327
|
+
For API errors with status codes, use the api_error() method which returns
|
|
328
|
+
a string like "api_error_500".
|
|
329
|
+
"""
|
|
330
|
+
|
|
331
|
+
ERRORS_WHILE_COMPUTING = "errors_while_computing_flags"
|
|
332
|
+
FLAG_MISSING = "flag_missing"
|
|
333
|
+
QUOTA_LIMITED = "quota_limited"
|
|
334
|
+
TIMEOUT = "timeout"
|
|
335
|
+
CONNECTION_ERROR = "connection_error"
|
|
336
|
+
UNKNOWN_ERROR = "unknown_error"
|
|
337
|
+
|
|
338
|
+
@staticmethod
|
|
339
|
+
def api_error(status: Union[int, str]) -> str:
|
|
340
|
+
"""Generate API error string with status code.
|
|
341
|
+
|
|
342
|
+
Args:
|
|
343
|
+
status: HTTP status code from the API error
|
|
344
|
+
|
|
345
|
+
Returns:
|
|
346
|
+
Error string like "api_error_500"
|
|
347
|
+
"""
|
|
348
|
+
return f"api_error_{status}"
|
posthog/version.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: posthog
|
|
3
|
-
Version: 7.
|
|
3
|
+
Version: 7.3.1
|
|
4
4
|
Summary: Integrate PostHog into any python application.
|
|
5
5
|
Home-page: https://github.com/posthog/posthog-python
|
|
6
6
|
Author: Posthog
|
|
@@ -19,6 +19,7 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.11
|
|
20
20
|
Classifier: Programming Language :: Python :: 3.12
|
|
21
21
|
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
22
23
|
Requires-Python: >=3.10
|
|
23
24
|
Description-Content-Type: text/markdown
|
|
24
25
|
License-File: LICENSE
|
|
@@ -1,19 +1,20 @@
|
|
|
1
|
-
posthog/__init__.py,sha256=
|
|
1
|
+
posthog/__init__.py,sha256=onbj2NNWstUUvWuwd8SdLQ2QIXMcB4JihsePbYj2Z64,28042
|
|
2
2
|
posthog/args.py,sha256=JUt0vbtF33IzLt3ARgsxMEYYnZo3RNS_LcK4-CjWaco,3298
|
|
3
|
-
posthog/client.py,sha256=
|
|
3
|
+
posthog/client.py,sha256=poyc3X-gZwQHQmgreSMkSkmWsAOMKlwThmK8nm9n3mI,82092
|
|
4
4
|
posthog/consumer.py,sha256=fdteMZ-deJGMpaQmHyznw_cwQG2Vvld1tmN9LUkZPrY,4608
|
|
5
5
|
posthog/contexts.py,sha256=22z4KySFCTwPbz4OYsd_8EJpoc2H91NiLq9cscSvFfw,12600
|
|
6
6
|
posthog/exception_capture.py,sha256=pmKtjQ6QY6zs4u_-ZA4H1gCyR3iI4sfqCQG_jwe_bKo,1774
|
|
7
|
-
posthog/exception_utils.py,sha256
|
|
7
|
+
posthog/exception_utils.py,sha256=T3WexL76u7_-SjAfb7WWaD-ozwnmQuZ_453yGKRYrOk,33738
|
|
8
8
|
posthog/feature_flags.py,sha256=4xAcYEpa97b5Lv9bIo5JHbCO6lhYBnH5EmJ2MrjbU3k,22517
|
|
9
|
+
posthog/flag_definition_cache.py,sha256=3LnB76BAU29FmypxagkqTpEW6MfjQQTw3E_xWJ679XQ,4697
|
|
9
10
|
posthog/poller.py,sha256=jBz5rfH_kn_bBz7wCB46Fpvso4ttx4uzqIZWvXBCFmQ,595
|
|
10
11
|
posthog/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
|
-
posthog/request.py,sha256=
|
|
12
|
-
posthog/types.py,sha256=
|
|
12
|
+
posthog/request.py,sha256=CfYneafeZH8bfQuAPHHjYIaWTLPl63YsZ66uBmn58vM,10349
|
|
13
|
+
posthog/types.py,sha256=OxGHSmmhVYwA7ecmJXUznDCZ1c4gAGtERzSLSYlyQFM,11540
|
|
13
14
|
posthog/utils.py,sha256=-0w-OLcCaoldkbBebPzQyBzLJSo9G9yBOg8NDVz7La8,16088
|
|
14
|
-
posthog/version.py,sha256=
|
|
15
|
+
posthog/version.py,sha256=ZP1gzAMj9_8ZAgoHQCpxcYzy3w0bR8IQuMtjzF3nWyg,87
|
|
15
16
|
posthog/ai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
|
-
posthog/ai/sanitization.py,sha256=
|
|
17
|
+
posthog/ai/sanitization.py,sha256=Dpx_5gKZfDS38KjmK1C0lvvjm9N8Pp_oIxusac888-g,6057
|
|
17
18
|
posthog/ai/types.py,sha256=arX98hR1PIPeJ3vFikxTlACIh1xPp6aEUw1gBLcKoB0,3273
|
|
18
19
|
posthog/ai/utils.py,sha256=pMqL_Aydf08EvUuSVJ1SsIpNwaom6qYIoLNOvMBNSHU,21475
|
|
19
20
|
posthog/ai/anthropic/__init__.py,sha256=8nTvETZzkfW-P3zBMmp06GOHs0N-xyOGu7Oa4di_lno,669
|
|
@@ -21,34 +22,36 @@ posthog/ai/anthropic/anthropic.py,sha256=UWyM6ryl5_VNQImaBi1RHN7tKXwkqaxy4yaXyPS
|
|
|
21
22
|
posthog/ai/anthropic/anthropic_async.py,sha256=ppWHiVp4hTl62Zr3jIwXXidOsqhrwx6iHM3ukG7WiPM,8789
|
|
22
23
|
posthog/ai/anthropic/anthropic_converter.py,sha256=prvaxt_R9kn9IbkxG_mLrw4kexT4i6T80U-6yhaZCNk,13053
|
|
23
24
|
posthog/ai/anthropic/anthropic_providers.py,sha256=Q_v7U4wgieIkvii-Bqh4pLx5pEgbrHmgsCG8lUkKb_0,2103
|
|
24
|
-
posthog/ai/gemini/__init__.py,sha256=
|
|
25
|
-
posthog/ai/gemini/gemini.py,sha256
|
|
26
|
-
posthog/ai/gemini/
|
|
25
|
+
posthog/ai/gemini/__init__.py,sha256=W1c2YcMah5wi4lTk7w8l9Yabw4l7jashBaelYirLadQ,470
|
|
26
|
+
posthog/ai/gemini/gemini.py,sha256=RmmHriUoc2APrZtQiw63hJzBka0H-B3jYH29clxpciw,14958
|
|
27
|
+
posthog/ai/gemini/gemini_async.py,sha256=0UFk6ZHuG_F7dkw028BTyGybTpPvIKSNfw0yI_LciOM,15108
|
|
28
|
+
posthog/ai/gemini/gemini_converter.py,sha256=JptTOV98s3V0jPKKHVRktze1i0RaSbkkOT0GLxkt2Gk,21801
|
|
27
29
|
posthog/ai/langchain/__init__.py,sha256=9CqAwLynTGj3ASAR80C3PmdTdrYGmu99tz0JL-HPFgI,70
|
|
28
30
|
posthog/ai/langchain/callbacks.py,sha256=EhjXTok2M2w5k5x9XFycl-bUMA1gdZx6FxHAY-HZ6bo,31367
|
|
29
31
|
posthog/ai/openai/__init__.py,sha256=u4OuUT7k1NgFj0TrxjuyegOg7a_UA8nAU6a-Hszr0OM,490
|
|
30
32
|
posthog/ai/openai/openai.py,sha256=ts95vdvWH7h0TX4FpLLK_wU_7H0MP3eZBEg0S-lsCKw,20127
|
|
31
33
|
posthog/ai/openai/openai_async.py,sha256=Ebd6_H3Zf3wGPycVJd_vOd3ZVoO3Mf3ZV339BExQd6Q,22436
|
|
32
|
-
posthog/ai/openai/openai_converter.py,sha256=
|
|
34
|
+
posthog/ai/openai/openai_converter.py,sha256=Miaqc9uzLRV_v-1zzn8Iqy8JsKb4l2Tjnw_1atp-8Xs,25828
|
|
33
35
|
posthog/ai/openai/openai_providers.py,sha256=zQIFTXHS2-dBKQX7FZxTFo7rIj5iiN7VHm9_2RzuDs8,3941
|
|
34
36
|
posthog/integrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
35
37
|
posthog/integrations/django.py,sha256=9X37yCF-T-MXUsxqkqjBWG3kdgOCyQYYNJQG_ZlwbRg,12633
|
|
36
38
|
posthog/test/__init__.py,sha256=VYgM6xPbJbvS-xhIcDiBRs0MFC9V_jT65uNeerCz_rM,299
|
|
37
39
|
posthog/test/test_before_send.py,sha256=3546WKlk8rF6bhvqhwcxAsjJovDw0Hf8yTvcYGbrhyI,7912
|
|
38
|
-
posthog/test/test_client.py,sha256=
|
|
40
|
+
posthog/test/test_client.py,sha256=cizqtsL7-13IDTZvSGg-FvEYY4Gcc1p_uzWl5CZwSl4,99805
|
|
39
41
|
posthog/test/test_consumer.py,sha256=HRDXSH0IPpCfo5yHs23n-0VzFyGSjWBKLEa8XNtU3_Y,7080
|
|
40
42
|
posthog/test/test_contexts.py,sha256=GDYpQNGhdzyA3--ia3WPao_4dqyLUpkWm1NMVm2L-So,7004
|
|
41
|
-
posthog/test/test_exception_capture.py,sha256=
|
|
43
|
+
posthog/test/test_exception_capture.py,sha256=nqG33mnxpMrSfPxXGoK6hz3yrQu3Yr-pV7r5_yYBZ68,13305
|
|
42
44
|
posthog/test/test_feature_flag.py,sha256=yIMJkoRtdJr91Y6Rb0PPlpZWBIR394TgWhccnlf-vYE,6815
|
|
43
|
-
posthog/test/test_feature_flag_result.py,sha256=
|
|
44
|
-
posthog/test/test_feature_flags.py,sha256=
|
|
45
|
+
posthog/test/test_feature_flag_result.py,sha256=KCQHismwddFWl-PHtRpZzcL5M45q_oUeHsST-zxzjkI,33321
|
|
46
|
+
posthog/test/test_feature_flags.py,sha256=5tfAYuzNjxgnxsQxlO7sDjmivUccTWf_26l_FEAJ3E8,222554
|
|
47
|
+
posthog/test/test_flag_definition_cache.py,sha256=_ssIKtrgNw3WsHv-GQNd_Nk-luDxAWgOVnAOMb8gWP8,21883
|
|
45
48
|
posthog/test/test_module.py,sha256=CERR0dTPGsAmd7YBxK0yKeB2Zr2b_Lv7hNQoeJauc9I,813
|
|
46
|
-
posthog/test/test_request.py,sha256=
|
|
49
|
+
posthog/test/test_request.py,sha256=PGJjA8tZ-9PrBZJI1EQGX03TocRFyCbjcRQatwoDvH4,14607
|
|
47
50
|
posthog/test/test_size_limited_dict.py,sha256=Wom7BkzpHmusHilZy0SV3PNzhw7ucuQgqrx86jf8euo,765
|
|
48
51
|
posthog/test/test_types.py,sha256=csLuBiz6RMV36cpg9LVIor4Khq6MfjjGxYXodx5VttY,7586
|
|
49
|
-
posthog/test/test_utils.py,sha256=
|
|
50
|
-
posthog-7.
|
|
51
|
-
posthog-7.
|
|
52
|
-
posthog-7.
|
|
53
|
-
posthog-7.
|
|
54
|
-
posthog-7.
|
|
52
|
+
posthog/test/test_utils.py,sha256=YqAnXaMHxzEV_D3AHhs-RXnZYzdEN7kdIlpOT6Ti6t0,9714
|
|
53
|
+
posthog-7.3.1.dist-info/licenses/LICENSE,sha256=wGf9JBotDkSygFj43m49oiKlFnpMnn97keiZKF-40vE,2450
|
|
54
|
+
posthog-7.3.1.dist-info/METADATA,sha256=9bg4WHQ_dz-3wd0JanCkXIrJ2NzH96YvCbKkmVY0_64,6010
|
|
55
|
+
posthog-7.3.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
56
|
+
posthog-7.3.1.dist-info/top_level.txt,sha256=7FBLsRjIUHVKQsXIhozuI3k-mun1tapp8iZO9EmUPEw,8
|
|
57
|
+
posthog-7.3.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|