From 9ab9b06c96c0416b031d84a3291dcd818af140be Mon Sep 17 00:00:00 2001 From: Eli Reisman Date: Tue, 6 Oct 2026 20:49:12 -0700 Subject: [PATCH 01/11] docs: 8.0 migration guide, README and openfeature-provider pin --- .sampo/changesets/capture-v1-major.md | 2 +- .../openfeature-provider-posthog-8.md | 5 + AGENTS.md | 2 +- README.md | 4 +- docs/migration-7.x-to-8.0.md | 134 ++++++++++++++++++ openfeature-provider/pyproject.toml | 2 +- 6 files changed, 145 insertions(+), 4 deletions(-) create mode 100644 .sampo/changesets/openfeature-provider-posthog-8.md create mode 100644 docs/migration-7.x-to-8.0.md diff --git a/.sampo/changesets/capture-v1-major.md b/.sampo/changesets/capture-v1-major.md index 9271aee97..b34e2a802 100644 --- a/.sampo/changesets/capture-v1-major.md +++ b/.sampo/changesets/capture-v1-major.md @@ -2,4 +2,4 @@ pypi/posthog: major --- -Capture v1 is the only capture path. Events and AI events send to the capture v1 endpoints, and the legacy v0 capture path is removed. See the migration guide for breaking changes. +Capture v1 is the only capture path. Events and AI events send to the capture v1 endpoints, and the legacy v0 capture path is removed. See the [migration guide](https://github.com/PostHog/posthog-python/blob/main/docs/migration-7.x-to-8.0.md) for breaking changes. diff --git a/.sampo/changesets/openfeature-provider-posthog-8.md b/.sampo/changesets/openfeature-provider-posthog-8.md new file mode 100644 index 000000000..5e9a804bf --- /dev/null +++ b/.sampo/changesets/openfeature-provider-posthog-8.md @@ -0,0 +1,5 @@ +--- +pypi/openfeature-provider-posthog: minor +--- + +Requires posthog 8.0 or later. Upgrade both packages together. diff --git a/AGENTS.md b/AGENTS.md index 4d67e9efc..0e40e181c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -23,7 +23,7 @@ Follow [Public API changes](./CONTRIBUTING.md#public-api-changes). As an agent, Before changing capture configuration, serialization, routing, or retries, read the relevant implementation and tests. -Capture v1 is the only capture protocol (`capture` posts to `/i/v1/analytics/events`, `capture_ai` to `/i/v1/ai/events`); strictly typed v1 options and `$set`/`$set_once` relocation; compression (gzip, zlib-wrapped deflate, optional zstd, default none), set per lane by `capture_compression` and `capture_ai_compression`; partial-only per-event retries with stable identity; accumulated drop reporting even on 2xx; terminal v1 `429`; `Retry-After` as a minimum bounded by the shared 30s ceiling; and inline blocking retries with `sync_mode=True`. +Capture v1 is the only capture protocol (`capture` posts to `/i/v1/analytics/events`, `capture_ai` to `/i/v1/ai/events`); per-event `options` sent as given, layered over `super_options` and context options, with legacy `$` option properties hoisted once after `before_send`; `$set`/`$set_once` relocation; compression (gzip, zlib-wrapped deflate, optional zstd, default none), set per lane by `capture_compression` and `capture_ai_compression`; partial-only per-event retries with stable identity; accumulated drop reporting even on 2xx; terminal v1 `429`; `Retry-After` as a minimum bounded by the shared 30s ceiling; and inline blocking retries with `sync_mode=True`. ## Mirror and build safety diff --git a/README.md b/README.md index 364101f01..f14f16be6 100644 --- a/README.md +++ b/README.md @@ -18,13 +18,15 @@ SDK usage examples and code snippets live in the official documentation so they | SDK Version | Python Versions Supported | Notes | | ------------- | ---------------------------- | -------------------------- | -| 7.3.1+ | 3.10, 3.11, 3.12, 3.13, 3.14 | Added Python 3.14 support | +| 8.0.0+ | 3.10, 3.11, 3.12, 3.13, 3.14 | Capture v1 only, see the [migration guide](docs/migration-7.x-to-8.0.md) | +| 7.3.1 - 7.x | 3.10, 3.11, 3.12, 3.13, 3.14 | Added Python 3.14 support | | 7.0.0 - 7.0.1 | 3.10, 3.11, 3.12, 3.13 | Dropped Python 3.9 support | | 4.0.1 - 6.x | 3.9, 3.10, 3.11, 3.12, 3.13 | Python 3.9+ required | ## Documentation - [Python library docs](https://posthog.com/docs/libraries/python) +- [Migrating from 7.x to 8.0](docs/migration-7.x-to-8.0.md) - [Django framework docs](https://posthog.com/docs/libraries/django) - [Flask framework docs](https://posthog.com/docs/libraries/flask) - [OpenFeature provider docs](https://posthog.com/docs/feature-flags/installation/openfeature) — use PostHog flags through the [OpenFeature](https://openfeature.dev) Python SDK diff --git a/docs/migration-7.x-to-8.0.md b/docs/migration-7.x-to-8.0.md new file mode 100644 index 000000000..03d3806ba --- /dev/null +++ b/docs/migration-7.x-to-8.0.md @@ -0,0 +1,134 @@ +# Migrating from posthog 7.x to 8.0 + +posthog 8.0 sends every event through capture v1 and removes the legacy capture path. +Most apps upgrade without code changes. +Read the checklist first, then the sections that apply to you. + +## Checklist + +You need to change code if your app does any of these: + +- passes `Client` or `AsyncPosthog` constructor arguments by position after `host` +- sets `capture_mode`, `POSTHOG_CAPTURE_MODE` or `gzip` +- imports `CaptureV1Error`, `posthog.capture_v1`, `request.batch_post`, `EVENTS_ENDPOINT` or `AI_EVENTS_ENDPOINT` +- sends events to a self-hosted PostHog that does not serve the capture v1 endpoints +- reuses one event `uuid` for more than one event +- sets `$process_person_profile` to turn person processing on for events without a distinct ID +- passes strings such as `"true"` for `$cookieless_mode`, `$ignore_sent_at` or `$process_person_profile` +- expects `super_properties` to override properties passed to a single call +- tests the AI integrations with a mock client and asserts on `capture` +- passes its own client object to the AI integrations + +## Endpoints + +| Method | Endpoint | +| --- | --- | +| `capture`, `set`, `set_once`, `alias`, `group_identify`, `capture_exception` | `/i/v1/analytics/events` | +| `capture_ai`, `capture_ai_immediate`, and every built-in AI integration | `/i/v1/ai/events` | + +If you send events to a self-hosted PostHog, check that it serves both endpoints before you upgrade. +An endpoint that is not served drops every event sent to it. + +## Removed options and APIs + +| Removed | Use instead | +| --- | --- | +| `capture_mode`, `CaptureMode`, `POSTHOG_CAPTURE_MODE` | Nothing. Capture v1 is the only path. | +| `gzip=True` | `capture_compression=CaptureCompression.GZIP`. The default is no compression. `POSTHOG_CAPTURE_COMPRESSION` still works. | +| `CaptureV1Error` | `CaptureError`. There is no alias. | +| `posthog.capture_v1` | `posthog.capture_event` and `posthog.capture_send` | +| `request.batch_post`, `async_batch_post`, `EVENTS_ENDPOINT`, `AI_EVENTS_ENDPOINT` | Nothing. Send events through a client. | +| The `gzip` parameter of `request.post` and `request.flags` | Nothing. | +| The `backoff` dependency | Add it to your own requirements if your code imports it. | + +Constructor arguments after `host` are keyword-only: + +```python +# 7.x +Posthog("", "https://us.i.posthog.com", True) + +# 8.0 +Posthog("", host="https://us.i.posthog.com", debug=True) +``` + +## Errors and `on_error` + +Capture v1 returns a result for every event, even when the request succeeds. +An event can be dropped, for example by billing limits or quotas, inside a 2xx response. +8.0 reports those events as failures. + +- Every capture failure is a `CaptureError`. It has these fields: + - `status`: the HTTP status, or `0` when the request never got a response + - `endpoint`, `request_id` and `attempts` + - `drops` and `retry_exhausted`: the uuids that were dropped or ran out of retries + - `event_results`: a `CaptureEventResult(result, details)` for each uuid, from `posthog.capture_send` + - `verdict_summary()`: counts such as `drop/billing=1, retry/not_persisted=1` +- `on_error(error, batch)` receives every failure, including in `sync_mode` and from `AsyncPosthog.capture_immediate`. +- Without `on_error`, the SDK logs one line per failed batch, for example `2 event(s) not persisted by /i/v1/analytics/events: ...`. The line never contains event content or the server's response text. +- In `sync_mode`, a failed `capture()` calls `on_error` and returns `None`. +- If a capture inside `on_error` fails, the SDK logs the failure and does not call `on_error` again. +- A `429` response is not retried. +- Retries start at 100 ms and double each attempt, up to 30 seconds. The `Consumer` default is 3 retries, down from 10. + +## Event uuids + +- Each event needs its own uuid. Capture rejects a whole batch that contains the same uuid twice, so the other events in that batch are lost too. +- The SDK accepts a uuid with hyphens, 32 hex digits, `{...}` braces or a `urn:uuid:` prefix, in any case. It sends the lowercase hyphenated form and returns that form from `capture`. +- Any other value is replaced with a generated uuid, and the SDK logs an error. This applies to `AsyncPosthog` too. +- Generated uuids are UUIDv7. + +## Event options + +Capture v1 sends processing options in an `options` object, next to `properties`. + +- `capture`, `capture_ai`, `set`, `set_once`, `alias`, `group_identify` and `capture_exception` take an `options` argument: + + ```python + posthog.capture("signed_up", distinct_id="user-1", options={"cookieless_mode": True}) + ``` + +- `super_options` sets options on every event, like `super_properties`. +- `set_context_option(key, value)` sets an option for the current context, like `tag()`. +- Options are sent as given. PostHog validates them. +- The legacy properties `$cookieless_mode`, `$ignore_sent_at`, `$product_tour_id` and `$process_person_profile` still work. They move into options after `before_send`. Their values are no longer converted, so pass `True` or `False`, not `"true"`. +- An option wins over its legacy property. A `None` option counts as unset. + +Values apply in this order, and each layer overrides the ones before it: + +1. options the SDK sets, such as turning off person processing for events without a distinct ID +2. `super_options` and `super_properties` +3. context options and tags +4. the `options` and `properties` of the call +5. `before_send` + +Two changes follow from this order: + +- Properties passed to a call now override `super_properties`. `super_properties` can no longer change `$lib`, `$lib_version` or `$geoip_disable`. +- An event without a distinct ID gets `options.process_person_profile = false`. A `$process_person_profile: true` property no longer turns person processing back on. Set the option instead. + +## AI capture + +- `capture_ai` and every built-in AI integration send to `/i/v1/ai/events`. The AI endpoint accepts events up to 8 MiB. +- `enable_full_ai_capture` now controls only content: string truncation and media redaction. It no longer chooses the endpoint. `_use_ai_lane` and `_enable_multimodal_capture` still work as aliases. +- The AI integrations call your client's `capture_ai`, and pass `options=`. A client object without `capture_ai` gets `capture` calls instead. A custom client must accept the `options` keyword argument. +- Tests that pass a `Mock` client to an AI integration must assert on `mock.capture_ai`, not `mock.capture`. +- When an AI integration or MCP falls back to a trace, run or session ID, it turns person processing off with a per-event option. A `$process_person_profile: true` property no longer turns it back on. `before_send` can still change it. +- MCP's `PostHogCaptureEvent` has an `options` key. +- New `Client` and `AsyncPosthog` arguments for the AI lane: + - `capture_ai_compression`: default none. `POSTHOG_CAPTURE_COMPRESSION` does not apply to it. + - `capture_ai_max_queue_size`: default 1000 + - `capture_ai_timeout`: default 30 seconds + - `capture_ai_max_event_bytes`: default 8 MiB plus 64 KiB. You can only lower it. +- `AsyncPosthog` has `capture_ai` and `capture_ai_immediate`. + +## Batching and size limits + +- An analytics event over 900 KiB is dropped and logged. This now applies in `sync_mode` too. +- An AI event over `capture_ai_max_event_bytes` is dropped and logged. +- A batch stops before an event that would take it past 5 MiB. A larger event is sent alone. +- `flush()` and `shutdown()` drain the analytics and AI queues at the same time. + +## OpenFeature provider + +`openfeature-provider-posthog` 0.2.0 requires posthog 8.0 or later. +Upgrade both packages together. diff --git a/openfeature-provider/pyproject.toml b/openfeature-provider/pyproject.toml index 4987c5008..cb90119b1 100644 --- a/openfeature-provider/pyproject.toml +++ b/openfeature-provider/pyproject.toml @@ -27,7 +27,7 @@ dependencies = [ # single-call API. Pinned to the posthog major this provider is released and # tested against (both ship from this repo); bump the upper bound when moving # the provider to a new posthog major. - "posthog>=7.0.0,<8.0.0", + "posthog>=8.0.0,<9.0.0", "openfeature-sdk>=0.8.0", ] From effc11327aa513684b45aa61334d0ece51e9ede9 Mon Sep 17 00:00:00 2001 From: Eli Reisman Date: Tue, 6 Oct 2026 21:54:42 -0700 Subject: [PATCH 02/11] chore: let openfeature provider accept posthog 7 and 8 --- .sampo/changesets/openfeature-provider-posthog-8.md | 2 +- docs/migration-7.x-to-8.0.md | 5 +++-- openfeature-provider/pyproject.toml | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.sampo/changesets/openfeature-provider-posthog-8.md b/.sampo/changesets/openfeature-provider-posthog-8.md index 5e9a804bf..02e3f2e00 100644 --- a/.sampo/changesets/openfeature-provider-posthog-8.md +++ b/.sampo/changesets/openfeature-provider-posthog-8.md @@ -2,4 +2,4 @@ pypi/openfeature-provider-posthog: minor --- -Requires posthog 8.0 or later. Upgrade both packages together. +Supports posthog 8.x as well as 7.x. diff --git a/docs/migration-7.x-to-8.0.md b/docs/migration-7.x-to-8.0.md index 03d3806ba..82961df8f 100644 --- a/docs/migration-7.x-to-8.0.md +++ b/docs/migration-7.x-to-8.0.md @@ -120,6 +120,7 @@ Two changes follow from this order: - `capture_ai_timeout`: default 30 seconds - `capture_ai_max_event_bytes`: default 8 MiB plus 64 KiB. You can only lower it. - `AsyncPosthog` has `capture_ai` and `capture_ai_immediate`. +- `AsyncPosthog` accepts `privacy_mode` and `enable_full_ai_capture`, with the same meaning as on `Client`. An AI integration given an `AsyncPosthog` client used to always truncate and redact media. ## Batching and size limits @@ -130,5 +131,5 @@ Two changes follow from this order: ## OpenFeature provider -`openfeature-provider-posthog` 0.2.0 requires posthog 8.0 or later. -Upgrade both packages together. +`openfeature-provider-posthog` 0.2.0 works with posthog 7.x and 8.x. +Older provider versions require posthog below 8.0, so upgrade the provider before or with posthog. diff --git a/openfeature-provider/pyproject.toml b/openfeature-provider/pyproject.toml index cb90119b1..2fb16aaac 100644 --- a/openfeature-provider/pyproject.toml +++ b/openfeature-provider/pyproject.toml @@ -27,7 +27,7 @@ dependencies = [ # single-call API. Pinned to the posthog major this provider is released and # tested against (both ship from this repo); bump the upper bound when moving # the provider to a new posthog major. - "posthog>=8.0.0,<9.0.0", + "posthog>=7.0.0,<9.0.0", "openfeature-sdk>=0.8.0", ] From 5b9ee445641db4aaf3a20b1369630a76ca36dfde Mon Sep 17 00:00:00 2001 From: Eli Reisman Date: Wed, 7 Oct 2026 10:41:59 -0700 Subject: [PATCH 03/11] docs: say options beat legacy properties at every layer --- docs/migration-7.x-to-8.0.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/migration-7.x-to-8.0.md b/docs/migration-7.x-to-8.0.md index 82961df8f..0c9cb9130 100644 --- a/docs/migration-7.x-to-8.0.md +++ b/docs/migration-7.x-to-8.0.md @@ -91,7 +91,8 @@ Capture v1 sends processing options in an `options` object, next to `properties` - `set_context_option(key, value)` sets an option for the current context, like `tag()`. - Options are sent as given. PostHog validates them. - The legacy properties `$cookieless_mode`, `$ignore_sent_at`, `$product_tour_id` and `$process_person_profile` still work. They move into options after `before_send`. Their values are no longer converted, so pass `True` or `False`, not `"true"`. -- An option wins over its legacy property. A `None` option counts as unset. +- An option set at any layer wins over its legacy property set at any layer. For example, `super_options={"cookieless_mode": True}` wins over an event's `$cookieless_mode: False`. When you move a default to options, move the per-event overrides of that key to options too. +- A `None` option counts as unset. Values apply in this order, and each layer overrides the ones before it: From 35125704fb4a7d8c106c2e29e636a2d5e12c6912 Mon Sep 17 00:00:00 2001 From: Eli Reisman Date: Wed, 7 Oct 2026 17:06:51 -0700 Subject: [PATCH 04/11] docs: name the CaptureError import and the posthoganalytics mirror --- docs/migration-7.x-to-8.0.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/migration-7.x-to-8.0.md b/docs/migration-7.x-to-8.0.md index 0c9cb9130..93516d128 100644 --- a/docs/migration-7.x-to-8.0.md +++ b/docs/migration-7.x-to-8.0.md @@ -35,12 +35,14 @@ An endpoint that is not served drops every event sent to it. | --- | --- | | `capture_mode`, `CaptureMode`, `POSTHOG_CAPTURE_MODE` | Nothing. Capture v1 is the only path. | | `gzip=True` | `capture_compression=CaptureCompression.GZIP`. The default is no compression. `POSTHOG_CAPTURE_COMPRESSION` still works. | -| `CaptureV1Error` | `CaptureError`. There is no alias. | +| `CaptureV1Error` (`from posthog.capture_v1 import CaptureV1Error`) | `CaptureError` (`from posthog import CaptureError`). There is no alias. | | `posthog.capture_v1` | `posthog.capture_event` and `posthog.capture_send` | | `request.batch_post`, `async_batch_post`, `EVENTS_ENDPOINT`, `AI_EVENTS_ENDPOINT` | Nothing. Send events through a client. | | The `gzip` parameter of `request.post` and `request.flags` | Nothing. | | The `backoff` dependency | Add it to your own requirements if your code imports it. | +The `posthoganalytics` package has the same changes. For example, import `CaptureError` from `posthoganalytics`. + Constructor arguments after `host` are keyword-only: ```python From 6b5b8efedade3ff3431ff998058e9274ef839fc2 Mon Sep 17 00:00:00 2001 From: Eli Reisman Date: Thu, 8 Oct 2026 09:23:50 -0700 Subject: [PATCH 05/11] docs: note that sync_mode failures re-raise in debug mode --- docs/migration-7.x-to-8.0.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/migration-7.x-to-8.0.md b/docs/migration-7.x-to-8.0.md index 93516d128..3a6cf60a3 100644 --- a/docs/migration-7.x-to-8.0.md +++ b/docs/migration-7.x-to-8.0.md @@ -67,7 +67,7 @@ An event can be dropped, for example by billing limits or quotas, inside a 2xx r - `verdict_summary()`: counts such as `drop/billing=1, retry/not_persisted=1` - `on_error(error, batch)` receives every failure, including in `sync_mode` and from `AsyncPosthog.capture_immediate`. - Without `on_error`, the SDK logs one line per failed batch, for example `2 event(s) not persisted by /i/v1/analytics/events: ...`. The line never contains event content or the server's response text. -- In `sync_mode`, a failed `capture()` calls `on_error` and returns `None`. +- In `sync_mode`, a failed `capture()` calls `on_error` and returns `None`. With `debug=True` it calls `on_error` and then re-raises the error. - If a capture inside `on_error` fails, the SDK logs the failure and does not call `on_error` again. - A `429` response is not retried. - Retries start at 100 ms and double each attempt, up to 30 seconds. The `Consumer` default is 3 retries, down from 10. From 4164ad4633110419d308e9d87afea74c9f701f1e Mon Sep 17 00:00:00 2001 From: Eli Reisman Date: Thu, 8 Oct 2026 11:09:45 -0700 Subject: [PATCH 06/11] docs: describe the late fill of context and super values --- docs/migration-7.x-to-8.0.md | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/docs/migration-7.x-to-8.0.md b/docs/migration-7.x-to-8.0.md index 3a6cf60a3..1a7cd6594 100644 --- a/docs/migration-7.x-to-8.0.md +++ b/docs/migration-7.x-to-8.0.md @@ -16,6 +16,7 @@ You need to change code if your app does any of these: - sets `$process_person_profile` to turn person processing on for events without a distinct ID - passes strings such as `"true"` for `$cookieless_mode`, `$ignore_sent_at` or `$process_person_profile` - expects `super_properties` to override properties passed to a single call +- reads or removes context tags, `super_properties` or `$release_id` in `before_send` - tests the AI integrations with a mock client and asserts on `capture` - passes its own client object to the AI integrations @@ -92,21 +93,23 @@ Capture v1 sends processing options in an `options` object, next to `properties` - `super_options` sets options on every event, like `super_properties`. - `set_context_option(key, value)` sets an option for the current context, like `tag()`. - Options are sent as given. PostHog validates them. -- The legacy properties `$cookieless_mode`, `$ignore_sent_at`, `$product_tour_id` and `$process_person_profile` still work. They move into options after `before_send`. Their values are no longer converted, so pass `True` or `False`, not `"true"`. +- The legacy properties `$cookieless_mode`, `$ignore_sent_at`, `$product_tour_id` and `$process_person_profile` still work. They fill the matching option only when it is unset, and the SDK always removes them from `properties`. Their values are no longer converted, so pass `True` or `False`, not `"true"`. - An option set at any layer wins over its legacy property set at any layer. For example, `super_options={"cookieless_mode": True}` wins over an event's `$cookieless_mode: False`. When you move a default to options, move the per-event overrides of that key to options too. -- A `None` option counts as unset. +- A `None` option counts as unset, so a later step can fill it. -Values apply in this order, and each layer overrides the ones before it: +Values apply in this order. After `before_send`, each step fills only the options and properties that the steps before it left unset: -1. options the SDK sets, such as turning off person processing for events without a distinct ID -2. `super_options` and `super_properties` +1. the `options` and `properties` of the call +2. `before_send` 3. context options and tags -4. the `options` and `properties` of the call -5. `before_send` +4. `super_options` and `super_properties` +5. values the SDK sets: `options.process_person_profile = false` for events without a distinct ID, and `$release_id` from `POSTHOG_RELEASE_ID` +6. legacy properties, which fill unset options and are then removed -Two changes follow from this order: +Three changes follow from this order: - Properties passed to a call now override `super_properties`. `super_properties` can no longer change `$lib`, `$lib_version` or `$geoip_disable`. +- `before_send` no longer sees context tags, context options, `super_properties`, `super_options` or `$release_id`. They fill in after it runs, so it cannot read or remove them, and its own changes win over them. To keep a super property off one event, set that key in the call's `properties` or in `before_send`. - An event without a distinct ID gets `options.process_person_profile = false`. A `$process_person_profile: true` property no longer turns person processing back on. Set the option instead. ## AI capture From 9dd5ee06ccdce89aa56c9278d37deed01449a712 Mon Sep 17 00:00:00 2001 From: Eli Reisman Date: Thu, 8 Oct 2026 14:10:29 -0700 Subject: [PATCH 07/11] docs: describe the fill of context and super values before before_send --- docs/migration-7.x-to-8.0.md | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/docs/migration-7.x-to-8.0.md b/docs/migration-7.x-to-8.0.md index 1a7cd6594..4143cb6e7 100644 --- a/docs/migration-7.x-to-8.0.md +++ b/docs/migration-7.x-to-8.0.md @@ -15,8 +15,7 @@ You need to change code if your app does any of these: - reuses one event `uuid` for more than one event - sets `$process_person_profile` to turn person processing on for events without a distinct ID - passes strings such as `"true"` for `$cookieless_mode`, `$ignore_sent_at` or `$process_person_profile` -- expects `super_properties` to override properties passed to a single call -- reads or removes context tags, `super_properties` or `$release_id` in `before_send` +- expects `super_properties` to override properties passed to a single call, including `$set` and `$groups` - tests the AI integrations with a mock client and asserts on `capture` - passes its own client object to the AI integrations @@ -97,20 +96,26 @@ Capture v1 sends processing options in an `options` object, next to `properties` - An option set at any layer wins over its legacy property set at any layer. For example, `super_options={"cookieless_mode": True}` wins over an event's `$cookieless_mode: False`. When you move a default to options, move the per-event overrides of that key to options too. - A `None` option counts as unset, so a later step can fill it. -Values apply in this order. After `before_send`, each step fills only the options and properties that the steps before it left unset: +Values apply in this order. Steps 2 to 4 fill only the options and properties that the steps before them left unset: 1. the `options` and `properties` of the call -2. `before_send` -3. context options and tags -4. `super_options` and `super_properties` -5. values the SDK sets: `options.process_person_profile = false` for events without a distinct ID, and `$release_id` from `POSTHOG_RELEASE_ID` +2. context options and tags +3. `super_options` and `super_properties` +4. values the SDK sets: `options.process_person_profile = false` for events without a distinct ID, and `$release_id` from `POSTHOG_RELEASE_ID` +5. `before_send`, which sees the result of steps 1 to 4 and can change or remove any of it 6. legacy properties, which fill unset options and are then removed -Three changes follow from this order: +A property set to `None` counts as set, so no later step fills it. +`$set`, `$set_once`, `$groups` and `$group_set` fill one level deep. +When the call and a later step both set one of them to a dict, the later step adds only the keys the call left out. +For example, `super_properties={"$set": {"plan": "free", "source": "web"}}` and a call with `properties={"$set": {"plan": "pro"}}` send `{"plan": "pro", "source": "web"}`. + +These changes follow from this order: - Properties passed to a call now override `super_properties`. `super_properties` can no longer change `$lib`, `$lib_version` or `$geoip_disable`. -- `before_send` no longer sees context tags, context options, `super_properties`, `super_options` or `$release_id`. They fill in after it runs, so it cannot read or remove them, and its own changes win over them. To keep a super property off one event, set that key in the call's `properties` or in `before_send`. +- A `$set`, `$set_once`, `$groups` or `$group_set` in `super_properties` no longer replaces the whole value of the call. The two merge, and the call wins key by key. - An event without a distinct ID gets `options.process_person_profile = false`. A `$process_person_profile: true` property no longer turns person processing back on. Set the option instead. +- To change an option in `before_send`, edit `options`. A legacy property that `before_send` adds does not replace an option that is already set. ## AI capture From d2160fce493a1bed89efbcf6d7eb3609df5a4564 Mon Sep 17 00:00:00 2001 From: Eli Reisman Date: Thu, 8 Oct 2026 15:49:14 -0700 Subject: [PATCH 08/11] docs: SDK values are defaults that caller values override --- docs/migration-7.x-to-8.0.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/migration-7.x-to-8.0.md b/docs/migration-7.x-to-8.0.md index 4143cb6e7..e3b075716 100644 --- a/docs/migration-7.x-to-8.0.md +++ b/docs/migration-7.x-to-8.0.md @@ -16,6 +16,7 @@ You need to change code if your app does any of these: - sets `$process_person_profile` to turn person processing on for events without a distinct ID - passes strings such as `"true"` for `$cookieless_mode`, `$ignore_sent_at` or `$process_person_profile` - expects `super_properties` to override properties passed to a single call, including `$set` and `$groups` +- sets `$is_server`, `$geoip_disable` or a system property such as `$os` and expects the SDK's value to win - tests the AI integrations with a mock client and asserts on `capture` - passes its own client object to the AI integrations @@ -101,7 +102,7 @@ Values apply in this order. Steps 2 to 4 fill only the options and properties th 1. the `options` and `properties` of the call 2. context options and tags 3. `super_options` and `super_properties` -4. values the SDK sets: `options.process_person_profile = false` for events without a distinct ID, and `$release_id` from `POSTHOG_RELEASE_ID` +4. values the SDK sets: `$is_server` from `is_server`, `$geoip_disable` from `disable_geoip`, system properties such as `$os` and `$python_version`, `options.process_person_profile = false` for events without a distinct ID, and `$release_id` from `POSTHOG_RELEASE_ID` 5. `before_send`, which sees the result of steps 1 to 4 and can change or remove any of it 6. legacy properties, which fill unset options and are then removed @@ -112,7 +113,9 @@ For example, `super_properties={"$set": {"plan": "free", "source": "web"}}` and These changes follow from this order: -- Properties passed to a call now override `super_properties`. `super_properties` can no longer change `$lib`, `$lib_version` or `$geoip_disable`. +- Properties passed to a call now override `super_properties`. `super_properties` can no longer change `$lib` or `$lib_version`. +- A `$is_server`, `$geoip_disable` or system property such as `$os` that you set in a call or a context tag now wins over the SDK's value. In 7.x the SDK overwrote it. `super_properties` win over the SDK's value too, so `super_properties={"$geoip_disable": False}` turns GeoIP lookup on for events, even with `disable_geoip=True`. +- The `groups` argument merges into a `$groups` property of the call, and wins key by key. In 7.x it replaced the property. MCP events merge their identity's groups into a custom `$groups` property the same way. - A `$set`, `$set_once`, `$groups` or `$group_set` in `super_properties` no longer replaces the whole value of the call. The two merge, and the call wins key by key. - An event without a distinct ID gets `options.process_person_profile = false`. A `$process_person_profile: true` property no longer turns person processing back on. Set the option instead. - To change an option in `before_send`, edit `options`. A legacy property that `before_send` adds does not replace an option that is already set. From c99d276db4dd49a83c45c053826d3f8f64bdedf7 Mon Sep 17 00:00:00 2001 From: Eli Reisman Date: Fri, 9 Oct 2026 17:30:11 -0700 Subject: [PATCH 09/11] docs: use real quota tags; a billing limit is a 402 Capture never sends billing_limit_exceeded or billing as a per-event tag. Product quotas drop single events in a 2xx with tags such as llm_events_over_quota; a request over the billing limit gets a 402 with no per-event results. --- docs/migration-7.x-to-8.0.md | 5 +++-- posthog/capture_send.py | 4 ++-- posthog/test/test_capture_send.py | 26 +++++++++++++++----------- 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/docs/migration-7.x-to-8.0.md b/docs/migration-7.x-to-8.0.md index e3b075716..c64600d10 100644 --- a/docs/migration-7.x-to-8.0.md +++ b/docs/migration-7.x-to-8.0.md @@ -57,15 +57,16 @@ Posthog("", host="https://us.i.posthog.com", debug=True) ## Errors and `on_error` Capture v1 returns a result for every event, even when the request succeeds. -An event can be dropped, for example by billing limits or quotas, inside a 2xx response. +An event can be dropped inside a 2xx response, for example when it is over a product quota. 8.0 reports those events as failures. +When the whole request is over the billing limit, capture returns a `402` with no per-event results instead. - Every capture failure is a `CaptureError`. It has these fields: - `status`: the HTTP status, or `0` when the request never got a response - `endpoint`, `request_id` and `attempts` - `drops` and `retry_exhausted`: the uuids that were dropped or ran out of retries - `event_results`: a `CaptureEventResult(result, details)` for each uuid, from `posthog.capture_send` - - `verdict_summary()`: counts such as `drop/billing=1, retry/not_persisted=1` + - `verdict_summary()`: counts such as `drop/llm_events_over_quota=1, retry/not_persisted=1` - `on_error(error, batch)` receives every failure, including in `sync_mode` and from `AsyncPosthog.capture_immediate`. - Without `on_error`, the SDK logs one line per failed batch, for example `2 event(s) not persisted by /i/v1/analytics/events: ...`. The line never contains event content or the server's response text. - In `sync_mode`, a failed `capture()` calls `on_error` and returns `None`. With `debug=True` it calls `on_error` and then re-raises the error. diff --git a/posthog/capture_send.py b/posthog/capture_send.py index 3c5f916d7..eabf343be 100644 --- a/posthog/capture_send.py +++ b/posthog/capture_send.py @@ -91,7 +91,7 @@ class CaptureEventResult: ``result`` is ``ok``, ``warning``, ``drop`` or ``retry``, or a value newer than this SDK, which counts as success. ``details`` is the server's reason - tag, such as ``billing_limit_exceeded``. + tag, such as ``llm_events_over_quota``. """ result: Optional[str] @@ -156,7 +156,7 @@ def __init__( def verdict_summary(self) -> str: """Count the undelivered events by verdict and reason, for example - ``drop/billing_limit_exceeded=2, retry/not_persisted=1``. + ``drop/llm_events_over_quota=2, retry/not_persisted=1``. Counts the events in ``drops`` and ``retry_exhausted``: events the server dropped, and events still pending retry after the last 2xx diff --git a/posthog/test/test_capture_send.py b/posthog/test/test_capture_send.py index 1f8c95062..9d87fa5b1 100644 --- a/posthog/test/test_capture_send.py +++ b/posthog/test/test_capture_send.py @@ -366,13 +366,15 @@ def test_drop_accumulated_across_attempts_surfaces_on_later_success(self) -> Non stub, exc = self._run_expecting_error( batch, [ - _results_response({"u-drop": ("drop", "billing"), "u-retry": "retry"}), + _results_response( + {"u-drop": ("drop", "llm_events_over_quota"), "u-retry": "retry"} + ), _results_response({"u-retry": "ok"}), ], ) self.assertEqual(len(stub.calls), 2) self.assertEqual(stub.calls[1]["uuids"], ["u-retry"]) # only retry resent - self.assertEqual(exc.drops, [("u-drop", "billing")]) + self.assertEqual(exc.drops, [("u-drop", "llm_events_over_quota")]) self.assertEqual(exc.retry_exhausted, []) def test_retry_exhausted_raises_with_uuids(self) -> None: @@ -394,7 +396,7 @@ def test_retry_exhausted_carries_earlier_drops(self) -> None: _results_response( { "u-ok": "ok", - "u-drop": ("drop", "billing"), + "u-drop": ("drop", "llm_events_over_quota"), "u-retry": "retry", } ), @@ -405,16 +407,18 @@ def test_retry_exhausted_carries_earlier_drops(self) -> None: self.assertEqual(len(stub.calls), 2) self.assertEqual(exc.endpoint, _CAPTURE_V1_PATH) self.assertEqual(exc.retry_exhausted, ["u-retry"]) - self.assertEqual(exc.drops, [("u-drop", "billing")]) + self.assertEqual(exc.drops, [("u-drop", "llm_events_over_quota")]) self.assertEqual( exc.event_results, { "u-ok": CaptureEventResult("ok"), - "u-drop": CaptureEventResult("drop", "billing"), + "u-drop": CaptureEventResult("drop", "llm_events_over_quota"), "u-retry": CaptureEventResult("retry", "not_persisted"), }, ) - self.assertEqual(exc.verdict_summary(), "drop/billing=1, retry/not_persisted=1") + self.assertEqual( + exc.verdict_summary(), "drop/llm_events_over_quota=1, retry/not_persisted=1" + ) @parameterized.expand( [ @@ -432,11 +436,11 @@ def test_results_match_non_canonical_uuid(self, _name, sent_uuid) -> None: [_msg(sent_uuid)], [ _results_response({canonical: "retry"}), - _results_response({canonical: ("drop", "billing")}), + _results_response({canonical: ("drop", "llm_events_over_quota")}), ], ) self.assertEqual(len(stub.calls), 2) - self.assertEqual(exc.drops, [(canonical, "billing")]) + self.assertEqual(exc.drops, [(canonical, "llm_events_over_quota")]) def test_malformed_2xx_is_terminal(self) -> None: stub, exc = self._run_expecting_error( @@ -536,17 +540,17 @@ class TestCaptureLossMessage(unittest.TestCase): 200, "2 event(s) not delivered", endpoint=_CAPTURE_V1_PATH, - drops=[("u-drop", "billing")], + drops=[("u-drop", "llm_events_over_quota")], retry_exhausted=["u-retry"], event_results={ "u-ok": CaptureEventResult("ok"), - "u-drop": CaptureEventResult("drop", "billing"), + "u-drop": CaptureEventResult("drop", "llm_events_over_quota"), "u-retry": CaptureEventResult("retry", "not_persisted"), }, ), 3, "2 event(s) not persisted by /i/v1/analytics/events: 1 dropped, " - "1 out of retries (drop/billing=1, retry/not_persisted=1)", + "1 out of retries (drop/llm_events_over_quota=1, retry/not_persisted=1)", ), ( "request_failure_after_partial_success", From 9292172de2047a234a9e68335fef2c60cb30f3bc Mon Sep 17 00:00:00 2001 From: Eli Reisman Date: Fri, 9 Oct 2026 20:16:16 -0700 Subject: [PATCH 10/11] docs: complete the 8.0 migration guide --- docs/migration-7.x-to-8.0.md | 51 +++++++++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 12 deletions(-) diff --git a/docs/migration-7.x-to-8.0.md b/docs/migration-7.x-to-8.0.md index c64600d10..a141465fc 100644 --- a/docs/migration-7.x-to-8.0.md +++ b/docs/migration-7.x-to-8.0.md @@ -34,7 +34,7 @@ An endpoint that is not served drops every event sent to it. | Removed | Use instead | | --- | --- | -| `capture_mode`, `CaptureMode`, `POSTHOG_CAPTURE_MODE` | Nothing. Capture v1 is the only path. | +| `capture_mode`, `CaptureMode`, `POSTHOG_CAPTURE_MODE` | Nothing. Capture v1 is the only path. Setting `posthog.capture_mode` on the module has no effect. | | `gzip=True` | `capture_compression=CaptureCompression.GZIP`. The default is no compression. `POSTHOG_CAPTURE_COMPRESSION` still works. | | `CaptureV1Error` (`from posthog.capture_v1 import CaptureV1Error`) | `CaptureError` (`from posthog import CaptureError`). There is no alias. | | `posthog.capture_v1` | `posthog.capture_event` and `posthog.capture_send` | @@ -61,18 +61,21 @@ An event can be dropped inside a 2xx response, for example when it is over a pro 8.0 reports those events as failures. When the whole request is over the billing limit, capture returns a `402` with no per-event results instead. -- Every capture failure is a `CaptureError`. It has these fields: - - `status`: the HTTP status, or `0` when the request never got a response +- Every capture failure is a `CaptureError`, a subclass of `APIError`. It has these fields: + - `status`: the HTTP status, or `0` when the request never got a response. With `0`, `__cause__` holds the network error. In 7.x, `on_error` received the raw `requests` exception. - `endpoint`, `request_id` and `attempts` - - `drops` and `retry_exhausted`: the uuids that were dropped or ran out of retries - - `event_results`: a `CaptureEventResult(result, details)` for each uuid, from `posthog.capture_send` + - `drops`: `(uuid, details)` pairs for the events capture dropped in a 2xx response + - `retry_exhausted`: the uuids still waiting for a retry after the last attempt + - `event_results`: a `CaptureEventResult(result, details)` for each uuid that got a result in a 2xx response, from `posthog.capture_send`. An event missing from it never got a result. - `verdict_summary()`: counts such as `drop/llm_events_over_quota=1, retry/not_persisted=1` - `on_error(error, batch)` receives every failure, including in `sync_mode` and from `AsyncPosthog.capture_immediate`. +- With `on_error` set, the SDK logs nothing for a failed batch. - Without `on_error`, the SDK logs one line per failed batch, for example `2 event(s) not persisted by /i/v1/analytics/events: ...`. The line never contains event content or the server's response text. +- The 7.x lines `error uploading: ...`, `async capture upload failed (...)` and `Immediate async capture failed (...)` are gone. Update any log-based alerts that match them. - In `sync_mode`, a failed `capture()` calls `on_error` and returns `None`. With `debug=True` it calls `on_error` and then re-raises the error. - If a capture inside `on_error` fails, the SDK logs the failure and does not call `on_error` again. -- A `429` response is not retried. -- Retries start at 100 ms and double each attempt, up to 30 seconds. The `Consumer` default is 3 retries, down from 10. +- The SDK retries `408`, `500`, `502`, `503` and `504` responses and network errors. Other error statuses fail the batch at once, including `429` and the other 5xx codes. 7.x also retried `429` and every 5xx. Capture itself does not return `429`; a proxy in front of it can. +- Retries start at 100 ms and double each attempt, up to 30 seconds. A `Retry-After` header sets a minimum wait, up to 30 seconds. The `Consumer` default is 3 retries, down from 10. ## Event uuids @@ -81,6 +84,15 @@ When the whole request is over the billing limit, capture returns a `402` with n - Any other value is replaced with a generated uuid, and the SDK logs an error. This applies to `AsyncPosthog` too. - Generated uuids are UUIDv7. +## Session and window IDs + +Capture v1 sends `$session_id` and `$window_id` as top-level event fields, not as properties. +The SDK moves them out of `properties` for you. + +- A string is sent as given, including `""`. +- `None` counts as unset and is removed. +- Any other value is removed and not sent, because capture would reject the whole batch. 7.x sent it as a property. + ## Event options Capture v1 sends processing options in an `options` object, next to `properties`. @@ -91,10 +103,20 @@ Capture v1 sends processing options in an `options` object, next to `properties` posthog.capture("signed_up", distinct_id="user-1", options={"cookieless_mode": True}) ``` -- `super_options` sets options on every event, like `super_properties`. -- `set_context_option(key, value)` sets an option for the current context, like `tag()`. +- `super_options`, a new `Client`, `AsyncPosthog` and module setting, sets options on every event, like `super_properties`. +- `set_context_option(key, value)` sets an option for the current context, like `tag()`. `get_context_options()` returns the options of the current context. Both exist on `Client` and as module functions. `AsyncPosthog` has no context methods, like it has no `tag()`, so call the module functions. They apply to every client in the context. +- Context tags and options reach `capture`, `capture_ai` and `capture_exception`. Context options also reach `set` and `set_once`. Neither reaches `alias` or `group_identify`. `super_properties` and `super_options` reach every method. - Options are sent as given. PostHog validates them. -- The legacy properties `$cookieless_mode`, `$ignore_sent_at`, `$product_tour_id` and `$process_person_profile` still work. They fill the matching option only when it is unset, and the SDK always removes them from `properties`. Their values are no longer converted, so pass `True` or `False`, not `"true"`. +- An `options` value that is not a dict is logged as an error and ignored. The event is still sent. +- The legacy properties still work. They fill the matching option only when it is unset, and the SDK always removes them from `properties`. Their values are no longer converted, so pass `True` or `False`, not `"true"`. + + | Legacy property | Option | + | --- | --- | + | `$cookieless_mode` | `cookieless_mode` | + | `$ignore_sent_at` | `disable_skew_correction` | + | `$product_tour_id` | `product_tour_id` | + | `$process_person_profile` | `process_person_profile` | + - An option set at any layer wins over its legacy property set at any layer. For example, `super_options={"cookieless_mode": True}` wins over an event's `$cookieless_mode: False`. When you move a default to options, move the per-event overrides of that key to options too. - A `None` option counts as unset, so a later step can fill it. @@ -120,21 +142,26 @@ These changes follow from this order: - A `$set`, `$set_once`, `$groups` or `$group_set` in `super_properties` no longer replaces the whole value of the call. The two merge, and the call wins key by key. - An event without a distinct ID gets `options.process_person_profile = false`. A `$process_person_profile: true` property no longer turns person processing back on. Set the option instead. - To change an option in `before_send`, edit `options`. A legacy property that `before_send` adds does not replace an option that is already set. +- When the server enables minimal `$feature_flag_called` events, those events now keep `$window_id`, `$cookieless_mode`, `$ignore_sent_at` and `$product_tour_id`, so their options still apply. ## AI capture - `capture_ai` and every built-in AI integration send to `/i/v1/ai/events`. The AI endpoint accepts events up to 8 MiB. - `enable_full_ai_capture` now controls only content: string truncation and media redaction. It no longer chooses the endpoint. `_use_ai_lane` and `_enable_multimodal_capture` still work as aliases. -- The AI integrations call your client's `capture_ai`, and pass `options=`. A client object without `capture_ai` gets `capture` calls instead. A custom client must accept the `options` keyword argument. +- The AI integrations call your client's `capture_ai`. A client object without `capture_ai` gets `capture` calls instead. A custom client must accept the `options` keyword argument. - Tests that pass a `Mock` client to an AI integration must assert on `mock.capture_ai`, not `mock.capture`. -- When an AI integration or MCP falls back to a trace, run or session ID, it turns person processing off with a per-event option. A `$process_person_profile: true` property no longer turns it back on. `before_send` can still change it. +- When an AI integration or MCP falls back to a trace, run or session ID, it turns person processing off with a per-event option. This is the only option the integrations pass. It wins over `super_options`, context options and a `$process_person_profile: true` property. `before_send` can still change it. +- The AI integrations have no `options` argument. To set options on their events, use `set_context_option`, `super_options`, or a legacy property such as `$cookieless_mode` in `posthog_properties`. - MCP's `PostHogCaptureEvent` has an `options` key. - New `Client` and `AsyncPosthog` arguments for the AI lane: - `capture_ai_compression`: default none. `POSTHOG_CAPTURE_COMPRESSION` does not apply to it. - `capture_ai_max_queue_size`: default 1000 - `capture_ai_timeout`: default 30 seconds - `capture_ai_max_event_bytes`: default 8 MiB plus 64 KiB. You can only lower it. +- The AI lane does not use `max_queue_size`, `timeout` or `capture_compression`. An invalid `capture_ai_*` value raises `ValueError` when you create the client. +- The module-level client has no AI lane settings. To change them, create a `Client`. - `AsyncPosthog` has `capture_ai` and `capture_ai_immediate`. +- `AsyncPosthog` sends events with `requests` in a worker thread. It uses `httpx` only for feature flags and remote config. - `AsyncPosthog` accepts `privacy_mode` and `enable_full_ai_capture`, with the same meaning as on `Client`. An AI integration given an `AsyncPosthog` client used to always truncate and redact media. ## Batching and size limits From bdf67ef214c61a0ec1c5ac00720433535344d4a8 Mon Sep 17 00:00:00 2001 From: Eli Reisman Date: Fri, 9 Oct 2026 20:16:16 -0700 Subject: [PATCH 11/11] chore(examples): run the pinned AI examples against the local SDK --- examples/example-ai-crewai/pyproject.toml | 5 +- examples/example-ai-crewai/uv.lock | 154 ++++++++++++------ examples/example-ai-dspy/pyproject.toml | 5 +- examples/example-ai-dspy/uv.lock | 109 ++++++++----- examples/example-ai-litellm/pyproject.toml | 5 +- examples/example-ai-litellm/uv.lock | 109 ++++++++----- .../example-ai-openai-agents/pyproject.toml | 5 +- examples/example-ai-openai-agents/uv.lock | 109 ++++++++----- 8 files changed, 317 insertions(+), 184 deletions(-) diff --git a/examples/example-ai-crewai/pyproject.toml b/examples/example-ai-crewai/pyproject.toml index 0c6cb57f8..9e655dc11 100644 --- a/examples/example-ai-crewai/pyproject.toml +++ b/examples/example-ai-crewai/pyproject.toml @@ -3,7 +3,10 @@ name = "example-ai-crewai" version = "0.1.0" requires-python = ">=3.10" dependencies = [ - "posthog==7.9.12", + "posthog", "crewai>=0.80.0", "litellm>=1.88.6", ] + +[tool.uv.sources] +posthog = { path = "../.." } diff --git a/examples/example-ai-crewai/uv.lock b/examples/example-ai-crewai/uv.lock index 7485aa704..8bcec0e08 100644 --- a/examples/example-ai-crewai/uv.lock +++ b/examples/example-ai-crewai/uv.lock @@ -10,7 +10,7 @@ resolution-markers = [ ] [options] -exclude-newer = "0001-01-01T00:00:00Z" # This has no effect and is included for backwards compatibility when using relative exclude-newer values. +exclude-newer = "2026-10-03T03:16:06.117367Z" exclude-newer-span = "P7D" [[package]] @@ -260,15 +260,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3e/83/65ecd7449fbdc5b91de15e6a9fcb4766076da8c4b117d3eb1194582e4de4/auth0_python-5.3.0-py3-none-any.whl", hash = "sha256:251fb2f524f7bc4f88b69e48b0444d05f71769aef4f61076f2ba3573ca69dae6", size = 2038474, upload-time = "2026-04-09T13:27:03.808Z" }, ] -[[package]] -name = "backoff" -version = "2.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/47/d7/5bbeb12c44d7c4f2fb5b56abce497eb5ed9f34d85701de869acedd602619/backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba", size = 17001, upload-time = "2022-10-05T19:19:32.061Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/73/b6e24bd22e6720ca8ee9a85a0c4a2971af8497d8f3193fa05390cbd46e09/backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8", size = 15148, upload-time = "2022-10-05T19:19:30.546Z" }, -] - [[package]] name = "bcrypt" version = "5.0.0" @@ -794,7 +785,7 @@ dependencies = [ requires-dist = [ { name = "crewai", specifier = ">=0.80.0" }, { name = "litellm", specifier = ">=1.88.6" }, - { name = "posthog", specifier = "==7.9.12" }, + { name = "posthog", directory = "../../" }, ] [[package]] @@ -802,7 +793,7 @@ name = "exceptiongroup" version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } wheels = [ @@ -1306,17 +1297,17 @@ resolution-markers = [ "python_full_version < '3.11'", ] dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "decorator" }, - { name = "exceptiongroup" }, - { name = "jedi" }, - { name = "matplotlib-inline" }, - { name = "pexpect", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, - { name = "prompt-toolkit" }, - { name = "pygments" }, - { name = "stack-data" }, - { name = "traitlets" }, - { name = "typing-extensions" }, + { name = "colorama", marker = "python_full_version < '3.11' and sys_platform == 'win32'" }, + { name = "decorator", marker = "python_full_version < '3.11'" }, + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "jedi", marker = "python_full_version < '3.11'" }, + { name = "matplotlib-inline", marker = "python_full_version < '3.11'" }, + { name = "pexpect", marker = "python_full_version < '3.11' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "prompt-toolkit", marker = "python_full_version < '3.11'" }, + { name = "pygments", marker = "python_full_version < '3.11'" }, + { name = "stack-data", marker = "python_full_version < '3.11'" }, + { name = "traitlets", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/40/18/f8598d287006885e7136451fdea0755af4ebcbfe342836f24deefaed1164/ipython-8.39.0.tar.gz", hash = "sha256:4110ae96012c379b8b6db898a07e186c40a2a1ef5d57a7fa83166047d9da7624", size = 5513971, upload-time = "2026-03-27T10:02:13.94Z" } wheels = [ @@ -1331,17 +1322,17 @@ resolution-markers = [ "python_full_version == '3.11.*'", ] dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "decorator" }, - { name = "ipython-pygments-lexers" }, - { name = "jedi" }, - { name = "matplotlib-inline" }, - { name = "pexpect", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, - { name = "prompt-toolkit" }, - { name = "pygments" }, - { name = "stack-data" }, - { name = "traitlets" }, - { name = "typing-extensions" }, + { name = "colorama", marker = "python_full_version == '3.11.*' and sys_platform == 'win32'" }, + { name = "decorator", marker = "python_full_version == '3.11.*'" }, + { name = "ipython-pygments-lexers", marker = "python_full_version == '3.11.*'" }, + { name = "jedi", marker = "python_full_version == '3.11.*'" }, + { name = "matplotlib-inline", marker = "python_full_version == '3.11.*'" }, + { name = "pexpect", marker = "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "prompt-toolkit", marker = "python_full_version == '3.11.*'" }, + { name = "pygments", marker = "python_full_version == '3.11.*'" }, + { name = "stack-data", marker = "python_full_version == '3.11.*'" }, + { name = "traitlets", marker = "python_full_version == '3.11.*'" }, + { name = "typing-extensions", marker = "python_full_version == '3.11.*'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/c5/25/daae0e764047b0a2480c7bbb25d48f4f509b5818636562eeac145d06dfee/ipython-9.10.1.tar.gz", hash = "sha256:e170e9b2a44312484415bdb750492699bf329233b03f2557a9692cce6466ada4", size = 4426663, upload-time = "2026-03-27T09:53:26.244Z" } wheels = [ @@ -1358,16 +1349,16 @@ resolution-markers = [ "python_full_version == '3.12.*'", ] dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "decorator" }, - { name = "ipython-pygments-lexers" }, - { name = "jedi" }, - { name = "matplotlib-inline" }, - { name = "pexpect", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, - { name = "prompt-toolkit" }, - { name = "pygments" }, - { name = "stack-data" }, - { name = "traitlets" }, + { name = "colorama", marker = "python_full_version >= '3.12' and sys_platform == 'win32'" }, + { name = "decorator", marker = "python_full_version >= '3.12'" }, + { name = "ipython-pygments-lexers", marker = "python_full_version >= '3.12'" }, + { name = "jedi", marker = "python_full_version >= '3.12'" }, + { name = "matplotlib-inline", marker = "python_full_version >= '3.12'" }, + { name = "pexpect", marker = "python_full_version >= '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "prompt-toolkit", marker = "python_full_version >= '3.12'" }, + { name = "pygments", marker = "python_full_version >= '3.12'" }, + { name = "stack-data", marker = "python_full_version >= '3.12'" }, + { name = "traitlets", marker = "python_full_version >= '3.12'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/3a/73/7114f80a8f9cabdb13c27732dce24af945b2923dcab80723602f7c8bc2d8/ipython-9.12.0.tar.gz", hash = "sha256:01daa83f504b693ba523b5a407246cabde4eb4513285a3c6acaff11a66735ee4", size = 4428879, upload-time = "2026-03-27T09:42:45.312Z" } wheels = [ @@ -1379,7 +1370,7 @@ name = "ipython-pygments-lexers" version = "1.1.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pygments" }, + { name = "pygments", marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ef/4c/5dd1d8af08107f88c7f741ead7a40854b8ac24ddf9ae850afbcf698aa552/ipython_pygments_lexers-1.1.1.tar.gz", hash = "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81", size = 8393, upload-time = "2025-01-17T11:24:34.505Z" } wheels = [ @@ -2620,20 +2611,75 @@ wheels = [ [[package]] name = "posthog" -version = "7.9.12" -source = { registry = "https://pypi.org/simple" } +version = "7.64.1" +source = { directory = "../../" } dependencies = [ - { name = "backoff" }, { name = "distro" }, - { name = "python-dateutil" }, { name = "requests" }, - { name = "six" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/1c/a7/2865487853061fbd62383492237b546d2d8f7c1846272350d2b9e14138cd/posthog-7.9.12.tar.gz", hash = "sha256:ebabf2eb2e1c1fbf22b0759df4644623fa43cc6c9dcbe9fd429b7937d14251ec", size = 176828, upload-time = "2026-03-12T09:01:15.184Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/65/a9/7a803aed5a5649cf78ea7b31e90d0080181ba21f739243e1741a1e607f1f/posthog-7.9.12-py3-none-any.whl", hash = "sha256:7175bd1698a566bfea98a016c64e3456399f8046aeeca8f1d04ae5bf6c5a38d0", size = 202469, upload-time = "2026-03-12T09:01:13.38Z" }, -] + +[package.metadata] +requires-dist = [ + { name = "anthropic", marker = "extra == 'test'", specifier = ">=0.72" }, + { name = "claude-agent-sdk", marker = "extra == 'test'" }, + { name = "coverage", marker = "extra == 'test'" }, + { name = "distro", specifier = ">=1.5.0" }, + { name = "django", marker = "extra == 'test'", specifier = ">=5.2.15,<6.0" }, + { name = "django-stubs", marker = "extra == 'dev'" }, + { name = "fastmcp", marker = "extra == 'test'", specifier = ">=2.0" }, + { name = "freezegun", marker = "extra == 'test'", specifier = "==1.5.1" }, + { name = "gevent", marker = "implementation_name == 'cpython' and extra == 'test'", specifier = ">=25.4.1" }, + { name = "google-genai", marker = "extra == 'test'" }, + { name = "griffe", marker = "extra == 'dev'" }, + { name = "httpx", marker = "extra == 'async'", specifier = ">=0.27.0,<1.0" }, + { name = "jsonschema", marker = "extra == 'test'", specifier = ">=4.0" }, + { name = "langchain", marker = "extra == 'langchain'", specifier = ">=1.3.9" }, + { name = "langchain", marker = "extra == 'test'", specifier = ">=1.3.9" }, + { name = "langchain-anthropic", marker = "extra == 'test'", specifier = ">=1.0" }, + { name = "langchain-community", marker = "extra == 'test'", specifier = ">=0.4" }, + { name = "langchain-core", marker = "extra == 'test'", specifier = ">=1.0" }, + { name = "langchain-openai", marker = "extra == 'test'", specifier = ">=1.1.14" }, + { name = "langgraph", marker = "extra == 'test'", specifier = ">=1.0" }, + { name = "langgraph-checkpoint", marker = "extra == 'test'", specifier = ">=4.1.1" }, + { name = "lxml", marker = "extra == 'dev'" }, + { name = "mcp", marker = "extra == 'test'", specifier = ">=1.28.1,<2" }, + { name = "mypy", marker = "extra == 'dev'" }, + { name = "mypy-baseline", marker = "extra == 'dev'" }, + { name = "openai-agents", marker = "extra == 'test'", specifier = ">=0.18" }, + { name = "opentelemetry-exporter-otlp-proto-http", marker = "extra == 'otel'", specifier = ">=1.20.0" }, + { name = "opentelemetry-exporter-otlp-proto-http", marker = "extra == 'test'", specifier = ">=1.20.0" }, + { name = "opentelemetry-sdk", marker = "extra == 'otel'", specifier = ">=1.20.0" }, + { name = "opentelemetry-sdk", marker = "extra == 'test'", specifier = ">=1.20.0" }, + { name = "packaging", marker = "extra == 'dev'" }, + { name = "parameterized", marker = "extra == 'test'", specifier = ">=0.8.1" }, + { name = "pre-commit", marker = "extra == 'dev'" }, + { name = "pydantic", marker = "extra == 'dev'", specifier = ">=2.12.0" }, + { name = "pydantic", marker = "extra == 'test'", specifier = ">=2.12.0" }, + { name = "pytest", marker = "extra == 'test'" }, + { name = "pytest-asyncio", marker = "extra == 'test'" }, + { name = "pytest-bdd", marker = "extra == 'test'", specifier = ">=8.1.0" }, + { name = "pytest-timeout", marker = "extra == 'test'" }, + { name = "python-dateutil", marker = "extra == 'test'", specifier = ">=2.9.0.post0" }, + { name = "requests", specifier = ">=2.7,<3.0" }, + { name = "ruff", marker = "extra == 'dev'" }, + { name = "setuptools", marker = "extra == 'dev'" }, + { name = "tiktoken", marker = "extra == 'test'", specifier = ">=0.12.0" }, + { name = "tomli", marker = "extra == 'dev'" }, + { name = "tomli-w", marker = "extra == 'dev'" }, + { name = "twine", marker = "extra == 'dev'" }, + { name = "types-requests", marker = "extra == 'dev'" }, + { name = "types-setuptools", marker = "extra == 'dev'" }, + { name = "typing-extensions", specifier = ">=4.2.0" }, + { name = "tzdata", marker = "extra == 'test'" }, + { name = "wheel", marker = "extra == 'dev'" }, + { name = "zstandard", marker = "extra == 'test'", specifier = ">=0.23.0" }, + { name = "zstandard", marker = "extra == 'zstd'", specifier = ">=0.23.0" }, +] +provides-extras = ["langchain", "async", "zstd", "otel", "dev", "test"] + +[package.metadata.requires-dev] +dev = [{ name = "claude-agent-sdk", specifier = ">=0.1.50" }] [[package]] name = "prompt-toolkit" diff --git a/examples/example-ai-dspy/pyproject.toml b/examples/example-ai-dspy/pyproject.toml index a78ea7b6d..48e96ffad 100644 --- a/examples/example-ai-dspy/pyproject.toml +++ b/examples/example-ai-dspy/pyproject.toml @@ -3,7 +3,10 @@ name = "example-ai-dspy" version = "0.1.0" requires-python = ">=3.10" dependencies = [ - "posthog==7.9.12", + "posthog", "dspy>=2.5.0", "litellm==1.88.6", ] + +[tool.uv.sources] +posthog = { path = "../.." } diff --git a/examples/example-ai-dspy/uv.lock b/examples/example-ai-dspy/uv.lock index 8b14811a9..a4228f087 100644 --- a/examples/example-ai-dspy/uv.lock +++ b/examples/example-ai-dspy/uv.lock @@ -7,7 +7,7 @@ resolution-markers = [ ] [options] -exclude-newer = "0001-01-01T00:00:00Z" # This has no effect and is included for backwards compatibility when using relative exclude-newer values. +exclude-newer = "2026-10-03T03:16:08.499203Z" exclude-newer-span = "P7D" [[package]] @@ -246,15 +246,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, ] -[[package]] -name = "backoff" -version = "2.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/47/d7/5bbeb12c44d7c4f2fb5b56abce497eb5ed9f34d85701de869acedd602619/backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba", size = 17001, upload-time = "2022-10-05T19:19:32.061Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/73/b6e24bd22e6720ca8ee9a85a0c4a2971af8497d8f3193fa05390cbd46e09/backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8", size = 15148, upload-time = "2022-10-05T19:19:30.546Z" }, -] - [[package]] name = "cachetools" version = "7.0.5" @@ -482,7 +473,7 @@ dependencies = [ requires-dist = [ { name = "dspy", specifier = ">=2.5.0" }, { name = "litellm", specifier = "==1.88.6" }, - { name = "posthog", specifier = "==7.9.12" }, + { name = "posthog", directory = "../../" }, ] [[package]] @@ -490,7 +481,7 @@ name = "exceptiongroup" version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } wheels = [ @@ -1616,20 +1607,75 @@ wheels = [ [[package]] name = "posthog" -version = "7.9.12" -source = { registry = "https://pypi.org/simple" } +version = "7.64.1" +source = { directory = "../../" } dependencies = [ - { name = "backoff" }, { name = "distro" }, - { name = "python-dateutil" }, { name = "requests" }, - { name = "six" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/1c/a7/2865487853061fbd62383492237b546d2d8f7c1846272350d2b9e14138cd/posthog-7.9.12.tar.gz", hash = "sha256:ebabf2eb2e1c1fbf22b0759df4644623fa43cc6c9dcbe9fd429b7937d14251ec", size = 176828, upload-time = "2026-03-12T09:01:15.184Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/65/a9/7a803aed5a5649cf78ea7b31e90d0080181ba21f739243e1741a1e607f1f/posthog-7.9.12-py3-none-any.whl", hash = "sha256:7175bd1698a566bfea98a016c64e3456399f8046aeeca8f1d04ae5bf6c5a38d0", size = 202469, upload-time = "2026-03-12T09:01:13.38Z" }, -] + +[package.metadata] +requires-dist = [ + { name = "anthropic", marker = "extra == 'test'", specifier = ">=0.72" }, + { name = "claude-agent-sdk", marker = "extra == 'test'" }, + { name = "coverage", marker = "extra == 'test'" }, + { name = "distro", specifier = ">=1.5.0" }, + { name = "django", marker = "extra == 'test'", specifier = ">=5.2.15,<6.0" }, + { name = "django-stubs", marker = "extra == 'dev'" }, + { name = "fastmcp", marker = "extra == 'test'", specifier = ">=2.0" }, + { name = "freezegun", marker = "extra == 'test'", specifier = "==1.5.1" }, + { name = "gevent", marker = "implementation_name == 'cpython' and extra == 'test'", specifier = ">=25.4.1" }, + { name = "google-genai", marker = "extra == 'test'" }, + { name = "griffe", marker = "extra == 'dev'" }, + { name = "httpx", marker = "extra == 'async'", specifier = ">=0.27.0,<1.0" }, + { name = "jsonschema", marker = "extra == 'test'", specifier = ">=4.0" }, + { name = "langchain", marker = "extra == 'langchain'", specifier = ">=1.3.9" }, + { name = "langchain", marker = "extra == 'test'", specifier = ">=1.3.9" }, + { name = "langchain-anthropic", marker = "extra == 'test'", specifier = ">=1.0" }, + { name = "langchain-community", marker = "extra == 'test'", specifier = ">=0.4" }, + { name = "langchain-core", marker = "extra == 'test'", specifier = ">=1.0" }, + { name = "langchain-openai", marker = "extra == 'test'", specifier = ">=1.1.14" }, + { name = "langgraph", marker = "extra == 'test'", specifier = ">=1.0" }, + { name = "langgraph-checkpoint", marker = "extra == 'test'", specifier = ">=4.1.1" }, + { name = "lxml", marker = "extra == 'dev'" }, + { name = "mcp", marker = "extra == 'test'", specifier = ">=1.28.1,<2" }, + { name = "mypy", marker = "extra == 'dev'" }, + { name = "mypy-baseline", marker = "extra == 'dev'" }, + { name = "openai-agents", marker = "extra == 'test'", specifier = ">=0.18" }, + { name = "opentelemetry-exporter-otlp-proto-http", marker = "extra == 'otel'", specifier = ">=1.20.0" }, + { name = "opentelemetry-exporter-otlp-proto-http", marker = "extra == 'test'", specifier = ">=1.20.0" }, + { name = "opentelemetry-sdk", marker = "extra == 'otel'", specifier = ">=1.20.0" }, + { name = "opentelemetry-sdk", marker = "extra == 'test'", specifier = ">=1.20.0" }, + { name = "packaging", marker = "extra == 'dev'" }, + { name = "parameterized", marker = "extra == 'test'", specifier = ">=0.8.1" }, + { name = "pre-commit", marker = "extra == 'dev'" }, + { name = "pydantic", marker = "extra == 'dev'", specifier = ">=2.12.0" }, + { name = "pydantic", marker = "extra == 'test'", specifier = ">=2.12.0" }, + { name = "pytest", marker = "extra == 'test'" }, + { name = "pytest-asyncio", marker = "extra == 'test'" }, + { name = "pytest-bdd", marker = "extra == 'test'", specifier = ">=8.1.0" }, + { name = "pytest-timeout", marker = "extra == 'test'" }, + { name = "python-dateutil", marker = "extra == 'test'", specifier = ">=2.9.0.post0" }, + { name = "requests", specifier = ">=2.7,<3.0" }, + { name = "ruff", marker = "extra == 'dev'" }, + { name = "setuptools", marker = "extra == 'dev'" }, + { name = "tiktoken", marker = "extra == 'test'", specifier = ">=0.12.0" }, + { name = "tomli", marker = "extra == 'dev'" }, + { name = "tomli-w", marker = "extra == 'dev'" }, + { name = "twine", marker = "extra == 'dev'" }, + { name = "types-requests", marker = "extra == 'dev'" }, + { name = "types-setuptools", marker = "extra == 'dev'" }, + { name = "typing-extensions", specifier = ">=4.2.0" }, + { name = "tzdata", marker = "extra == 'test'" }, + { name = "wheel", marker = "extra == 'dev'" }, + { name = "zstandard", marker = "extra == 'test'", specifier = ">=0.23.0" }, + { name = "zstandard", marker = "extra == 'zstd'", specifier = ">=0.23.0" }, +] +provides-extras = ["langchain", "async", "zstd", "otel", "dev", "test"] + +[package.metadata.requires-dev] +dev = [{ name = "claude-agent-sdk", specifier = ">=0.1.50" }] [[package]] name = "propcache" @@ -1887,18 +1933,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, ] -[[package]] -name = "python-dateutil" -version = "2.9.0.post0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "six" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, -] - [[package]] name = "python-dotenv" version = "1.2.3" @@ -2266,15 +2300,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, ] -[[package]] -name = "six" -version = "1.17.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, -] - [[package]] name = "sniffio" version = "1.3.1" diff --git a/examples/example-ai-litellm/pyproject.toml b/examples/example-ai-litellm/pyproject.toml index 0691dbfb5..d3e0b35da 100644 --- a/examples/example-ai-litellm/pyproject.toml +++ b/examples/example-ai-litellm/pyproject.toml @@ -3,6 +3,9 @@ name = "example-ai-litellm" version = "0.1.0" requires-python = ">=3.10" dependencies = [ - "posthog==7.9.12", + "posthog", "litellm==1.88.6", ] + +[tool.uv.sources] +posthog = { path = "../.." } diff --git a/examples/example-ai-litellm/uv.lock b/examples/example-ai-litellm/uv.lock index a17bcd98f..5c9ae49f0 100644 --- a/examples/example-ai-litellm/uv.lock +++ b/examples/example-ai-litellm/uv.lock @@ -3,7 +3,7 @@ revision = 3 requires-python = ">=3.10" [options] -exclude-newer = "0001-01-01T00:00:00Z" # This has no effect and is included for backwards compatibility when using relative exclude-newer values. +exclude-newer = "2026-10-03T03:16:09.116916Z" exclude-newer-span = "P7D" [[package]] @@ -215,15 +215,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, ] -[[package]] -name = "backoff" -version = "2.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/47/d7/5bbeb12c44d7c4f2fb5b56abce497eb5ed9f34d85701de869acedd602619/backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba", size = 17001, upload-time = "2022-10-05T19:19:32.061Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/73/b6e24bd22e6720ca8ee9a85a0c4a2971af8497d8f3193fa05390cbd46e09/backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8", size = 15148, upload-time = "2022-10-05T19:19:30.546Z" }, -] - [[package]] name = "certifi" version = "2026.2.25" @@ -380,7 +371,7 @@ dependencies = [ [package.metadata] requires-dist = [ { name = "litellm", specifier = "==1.88.6" }, - { name = "posthog", specifier = "==7.9.12" }, + { name = "posthog", directory = "../../" }, ] [[package]] @@ -388,7 +379,7 @@ name = "exceptiongroup" version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } wheels = [ @@ -1183,20 +1174,75 @@ wheels = [ [[package]] name = "posthog" -version = "7.9.12" -source = { registry = "https://pypi.org/simple" } +version = "7.64.1" +source = { directory = "../../" } dependencies = [ - { name = "backoff" }, { name = "distro" }, - { name = "python-dateutil" }, { name = "requests" }, - { name = "six" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/1c/a7/2865487853061fbd62383492237b546d2d8f7c1846272350d2b9e14138cd/posthog-7.9.12.tar.gz", hash = "sha256:ebabf2eb2e1c1fbf22b0759df4644623fa43cc6c9dcbe9fd429b7937d14251ec", size = 176828, upload-time = "2026-03-12T09:01:15.184Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/65/a9/7a803aed5a5649cf78ea7b31e90d0080181ba21f739243e1741a1e607f1f/posthog-7.9.12-py3-none-any.whl", hash = "sha256:7175bd1698a566bfea98a016c64e3456399f8046aeeca8f1d04ae5bf6c5a38d0", size = 202469, upload-time = "2026-03-12T09:01:13.38Z" }, -] + +[package.metadata] +requires-dist = [ + { name = "anthropic", marker = "extra == 'test'", specifier = ">=0.72" }, + { name = "claude-agent-sdk", marker = "extra == 'test'" }, + { name = "coverage", marker = "extra == 'test'" }, + { name = "distro", specifier = ">=1.5.0" }, + { name = "django", marker = "extra == 'test'", specifier = ">=5.2.15,<6.0" }, + { name = "django-stubs", marker = "extra == 'dev'" }, + { name = "fastmcp", marker = "extra == 'test'", specifier = ">=2.0" }, + { name = "freezegun", marker = "extra == 'test'", specifier = "==1.5.1" }, + { name = "gevent", marker = "implementation_name == 'cpython' and extra == 'test'", specifier = ">=25.4.1" }, + { name = "google-genai", marker = "extra == 'test'" }, + { name = "griffe", marker = "extra == 'dev'" }, + { name = "httpx", marker = "extra == 'async'", specifier = ">=0.27.0,<1.0" }, + { name = "jsonschema", marker = "extra == 'test'", specifier = ">=4.0" }, + { name = "langchain", marker = "extra == 'langchain'", specifier = ">=1.3.9" }, + { name = "langchain", marker = "extra == 'test'", specifier = ">=1.3.9" }, + { name = "langchain-anthropic", marker = "extra == 'test'", specifier = ">=1.0" }, + { name = "langchain-community", marker = "extra == 'test'", specifier = ">=0.4" }, + { name = "langchain-core", marker = "extra == 'test'", specifier = ">=1.0" }, + { name = "langchain-openai", marker = "extra == 'test'", specifier = ">=1.1.14" }, + { name = "langgraph", marker = "extra == 'test'", specifier = ">=1.0" }, + { name = "langgraph-checkpoint", marker = "extra == 'test'", specifier = ">=4.1.1" }, + { name = "lxml", marker = "extra == 'dev'" }, + { name = "mcp", marker = "extra == 'test'", specifier = ">=1.28.1,<2" }, + { name = "mypy", marker = "extra == 'dev'" }, + { name = "mypy-baseline", marker = "extra == 'dev'" }, + { name = "openai-agents", marker = "extra == 'test'", specifier = ">=0.18" }, + { name = "opentelemetry-exporter-otlp-proto-http", marker = "extra == 'otel'", specifier = ">=1.20.0" }, + { name = "opentelemetry-exporter-otlp-proto-http", marker = "extra == 'test'", specifier = ">=1.20.0" }, + { name = "opentelemetry-sdk", marker = "extra == 'otel'", specifier = ">=1.20.0" }, + { name = "opentelemetry-sdk", marker = "extra == 'test'", specifier = ">=1.20.0" }, + { name = "packaging", marker = "extra == 'dev'" }, + { name = "parameterized", marker = "extra == 'test'", specifier = ">=0.8.1" }, + { name = "pre-commit", marker = "extra == 'dev'" }, + { name = "pydantic", marker = "extra == 'dev'", specifier = ">=2.12.0" }, + { name = "pydantic", marker = "extra == 'test'", specifier = ">=2.12.0" }, + { name = "pytest", marker = "extra == 'test'" }, + { name = "pytest-asyncio", marker = "extra == 'test'" }, + { name = "pytest-bdd", marker = "extra == 'test'", specifier = ">=8.1.0" }, + { name = "pytest-timeout", marker = "extra == 'test'" }, + { name = "python-dateutil", marker = "extra == 'test'", specifier = ">=2.9.0.post0" }, + { name = "requests", specifier = ">=2.7,<3.0" }, + { name = "ruff", marker = "extra == 'dev'" }, + { name = "setuptools", marker = "extra == 'dev'" }, + { name = "tiktoken", marker = "extra == 'test'", specifier = ">=0.12.0" }, + { name = "tomli", marker = "extra == 'dev'" }, + { name = "tomli-w", marker = "extra == 'dev'" }, + { name = "twine", marker = "extra == 'dev'" }, + { name = "types-requests", marker = "extra == 'dev'" }, + { name = "types-setuptools", marker = "extra == 'dev'" }, + { name = "typing-extensions", specifier = ">=4.2.0" }, + { name = "tzdata", marker = "extra == 'test'" }, + { name = "wheel", marker = "extra == 'dev'" }, + { name = "zstandard", marker = "extra == 'test'", specifier = ">=0.23.0" }, + { name = "zstandard", marker = "extra == 'zstd'", specifier = ">=0.23.0" }, +] +provides-extras = ["langchain", "async", "zstd", "otel", "dev", "test"] + +[package.metadata.requires-dev] +dev = [{ name = "claude-agent-sdk", specifier = ">=0.1.50" }] [[package]] name = "propcache" @@ -1454,18 +1500,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, ] -[[package]] -name = "python-dateutil" -version = "2.9.0.post0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "six" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, -] - [[package]] name = "python-dotenv" version = "1.2.3" @@ -1833,15 +1867,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, ] -[[package]] -name = "six" -version = "1.17.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, -] - [[package]] name = "sniffio" version = "1.3.1" diff --git a/examples/example-ai-openai-agents/pyproject.toml b/examples/example-ai-openai-agents/pyproject.toml index 647400643..06ad91cd7 100644 --- a/examples/example-ai-openai-agents/pyproject.toml +++ b/examples/example-ai-openai-agents/pyproject.toml @@ -3,6 +3,9 @@ name = "example-ai-openai-agents" version = "0.1.0" requires-python = ">=3.10" dependencies = [ - "posthog==7.9.12", + "posthog", "openai-agents==0.13.1", ] + +[tool.uv.sources] +posthog = { path = "../.." } diff --git a/examples/example-ai-openai-agents/uv.lock b/examples/example-ai-openai-agents/uv.lock index 63ad72f88..0a084773f 100644 --- a/examples/example-ai-openai-agents/uv.lock +++ b/examples/example-ai-openai-agents/uv.lock @@ -9,7 +9,7 @@ resolution-markers = [ ] [options] -exclude-newer = "0001-01-01T00:00:00Z" # This has no effect and is included for backwards compatibility when using relative exclude-newer values. +exclude-newer = "2026-10-03T03:16:09.242998Z" exclude-newer-span = "P7D" [[package]] @@ -44,15 +44,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, ] -[[package]] -name = "backoff" -version = "2.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/47/d7/5bbeb12c44d7c4f2fb5b56abce497eb5ed9f34d85701de869acedd602619/backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba", size = 17001, upload-time = "2022-10-05T19:19:32.061Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/73/b6e24bd22e6720ca8ee9a85a0c4a2971af8497d8f3193fa05390cbd46e09/backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8", size = 15148, upload-time = "2022-10-05T19:19:30.546Z" }, -] - [[package]] name = "certifi" version = "2026.2.25" @@ -376,7 +367,7 @@ dependencies = [ [package.metadata] requires-dist = [ { name = "openai-agents", specifier = "==0.13.1" }, - { name = "posthog", specifier = "==7.9.12" }, + { name = "posthog", directory = "../../" }, ] [[package]] @@ -384,7 +375,7 @@ name = "exceptiongroup" version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } wheels = [ @@ -646,20 +637,75 @@ wheels = [ [[package]] name = "posthog" -version = "7.9.12" -source = { registry = "https://pypi.org/simple" } +version = "7.64.1" +source = { directory = "../../" } dependencies = [ - { name = "backoff" }, { name = "distro" }, - { name = "python-dateutil" }, { name = "requests" }, - { name = "six" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/1c/a7/2865487853061fbd62383492237b546d2d8f7c1846272350d2b9e14138cd/posthog-7.9.12.tar.gz", hash = "sha256:ebabf2eb2e1c1fbf22b0759df4644623fa43cc6c9dcbe9fd429b7937d14251ec", size = 176828, upload-time = "2026-03-12T09:01:15.184Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/65/a9/7a803aed5a5649cf78ea7b31e90d0080181ba21f739243e1741a1e607f1f/posthog-7.9.12-py3-none-any.whl", hash = "sha256:7175bd1698a566bfea98a016c64e3456399f8046aeeca8f1d04ae5bf6c5a38d0", size = 202469, upload-time = "2026-03-12T09:01:13.38Z" }, -] + +[package.metadata] +requires-dist = [ + { name = "anthropic", marker = "extra == 'test'", specifier = ">=0.72" }, + { name = "claude-agent-sdk", marker = "extra == 'test'" }, + { name = "coverage", marker = "extra == 'test'" }, + { name = "distro", specifier = ">=1.5.0" }, + { name = "django", marker = "extra == 'test'", specifier = ">=5.2.15,<6.0" }, + { name = "django-stubs", marker = "extra == 'dev'" }, + { name = "fastmcp", marker = "extra == 'test'", specifier = ">=2.0" }, + { name = "freezegun", marker = "extra == 'test'", specifier = "==1.5.1" }, + { name = "gevent", marker = "implementation_name == 'cpython' and extra == 'test'", specifier = ">=25.4.1" }, + { name = "google-genai", marker = "extra == 'test'" }, + { name = "griffe", marker = "extra == 'dev'" }, + { name = "httpx", marker = "extra == 'async'", specifier = ">=0.27.0,<1.0" }, + { name = "jsonschema", marker = "extra == 'test'", specifier = ">=4.0" }, + { name = "langchain", marker = "extra == 'langchain'", specifier = ">=1.3.9" }, + { name = "langchain", marker = "extra == 'test'", specifier = ">=1.3.9" }, + { name = "langchain-anthropic", marker = "extra == 'test'", specifier = ">=1.0" }, + { name = "langchain-community", marker = "extra == 'test'", specifier = ">=0.4" }, + { name = "langchain-core", marker = "extra == 'test'", specifier = ">=1.0" }, + { name = "langchain-openai", marker = "extra == 'test'", specifier = ">=1.1.14" }, + { name = "langgraph", marker = "extra == 'test'", specifier = ">=1.0" }, + { name = "langgraph-checkpoint", marker = "extra == 'test'", specifier = ">=4.1.1" }, + { name = "lxml", marker = "extra == 'dev'" }, + { name = "mcp", marker = "extra == 'test'", specifier = ">=1.28.1,<2" }, + { name = "mypy", marker = "extra == 'dev'" }, + { name = "mypy-baseline", marker = "extra == 'dev'" }, + { name = "openai-agents", marker = "extra == 'test'", specifier = ">=0.18" }, + { name = "opentelemetry-exporter-otlp-proto-http", marker = "extra == 'otel'", specifier = ">=1.20.0" }, + { name = "opentelemetry-exporter-otlp-proto-http", marker = "extra == 'test'", specifier = ">=1.20.0" }, + { name = "opentelemetry-sdk", marker = "extra == 'otel'", specifier = ">=1.20.0" }, + { name = "opentelemetry-sdk", marker = "extra == 'test'", specifier = ">=1.20.0" }, + { name = "packaging", marker = "extra == 'dev'" }, + { name = "parameterized", marker = "extra == 'test'", specifier = ">=0.8.1" }, + { name = "pre-commit", marker = "extra == 'dev'" }, + { name = "pydantic", marker = "extra == 'dev'", specifier = ">=2.12.0" }, + { name = "pydantic", marker = "extra == 'test'", specifier = ">=2.12.0" }, + { name = "pytest", marker = "extra == 'test'" }, + { name = "pytest-asyncio", marker = "extra == 'test'" }, + { name = "pytest-bdd", marker = "extra == 'test'", specifier = ">=8.1.0" }, + { name = "pytest-timeout", marker = "extra == 'test'" }, + { name = "python-dateutil", marker = "extra == 'test'", specifier = ">=2.9.0.post0" }, + { name = "requests", specifier = ">=2.7,<3.0" }, + { name = "ruff", marker = "extra == 'dev'" }, + { name = "setuptools", marker = "extra == 'dev'" }, + { name = "tiktoken", marker = "extra == 'test'", specifier = ">=0.12.0" }, + { name = "tomli", marker = "extra == 'dev'" }, + { name = "tomli-w", marker = "extra == 'dev'" }, + { name = "twine", marker = "extra == 'dev'" }, + { name = "types-requests", marker = "extra == 'dev'" }, + { name = "types-setuptools", marker = "extra == 'dev'" }, + { name = "typing-extensions", specifier = ">=4.2.0" }, + { name = "tzdata", marker = "extra == 'test'" }, + { name = "wheel", marker = "extra == 'dev'" }, + { name = "zstandard", marker = "extra == 'test'", specifier = ">=0.23.0" }, + { name = "zstandard", marker = "extra == 'zstd'", specifier = ">=0.23.0" }, +] +provides-extras = ["langchain", "async", "zstd", "otel", "dev", "test"] + +[package.metadata.requires-dev] +dev = [{ name = "claude-agent-sdk", specifier = ">=0.1.50" }] [[package]] name = "pycparser" @@ -834,18 +880,6 @@ crypto = [ { name = "cryptography" }, ] -[[package]] -name = "python-dateutil" -version = "2.9.0.post0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "six" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, -] - [[package]] name = "python-dotenv" version = "1.2.3" @@ -1037,15 +1071,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/b7/b95708304cd49b7b6f82fdd039f1748b66ec2b21d6a45180910802f1abf1/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:ac37f9f516c51e5753f27dfdef11a88330f04de2d564be3991384b2f3535d02e", size = 562191, upload-time = "2025-11-30T20:24:36.853Z" }, ] -[[package]] -name = "six" -version = "1.17.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, -] - [[package]] name = "sniffio" version = "1.3.1"