diff --git a/.cursorrules b/.cursorrules index ad174a8..09d36ba 100644 --- a/.cursorrules +++ b/.cursorrules @@ -1,6 +1,6 @@ # FlightDeck — Cursor -Read **`AGENTS.md`** for mission, non-goals, public contracts, engineering rules, verification, and docs policy. **`CLAUDE.md`** is a short index for Claude Code / Cursor. In Cursor, **`.cursor/rules/flightdeck-ci-artifacts.mdc`** (`alwaysApply`) reminds you to commit **`src/flightdeck/server/static/`** and **`schemas/`** when CI gates apply. +Read **`AGENTS.md`** for mission, non-goals, public contracts, engineering rules, verification, and docs policy (including optional **`flightdeck.integrations`** boundaries). **`CLAUDE.md`** is a short index for Claude Code / Cursor. In Cursor, **`.cursor/rules/flightdeck-ci-artifacts.mdc`** (`alwaysApply`) reminds you to commit **`src/flightdeck/server/static/`** and **`schemas/`** when CI gates apply. Quick verify (uv): `uv sync --frozen --extra dev`, then `uv run python -m ruff check src tests`, `uv run python -m pytest`, `uv run flightdeck-quickstart-verify`, `uv run flightdeck --help` (pip/venv equivalents in **`DEVELOPMENT.md`**; on Windows, **`py -3`** if needed). After **Pydantic / wire model** edits: `uv run python scripts/generate_schemas.py` then `git diff --exit-code schemas/`. After **`web/`** edits that affect the production bundle: `cd web && npm ci && npm run build && cd .. && git diff --exit-code src/flightdeck/server/static/` (must be clean—commit all changes under **`static/`**); when UI behavior changes, `cd web && npx playwright install chromium && npm run test:e2e` (see **`web/README.md`**). diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d704393..579312c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -79,6 +79,32 @@ jobs: - name: CLI smoke run: uv run flightdeck --help + integrations: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ["3.14"] + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up uv + uses: astral-sh/setup-uv@v5 + with: + enable-cache: true + python-version: ${{ matrix.python-version }} + + - name: Sync with integration extras + run: uv sync --frozen --extra dev --extra integrations-ci + + - name: Lint + run: uv run python -m ruff check src tests + + - name: Integration tests + run: uv run python -m pytest tests/test_integrations.py tests/test_integrations_langchain.py + test-windows: runs-on: windows-latest strategy: diff --git a/AGENTS.md b/AGENTS.md index 087f1c7..a35506a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -25,12 +25,14 @@ Economic and operational safety for AI releases. Do not add: - prompt IDEs -- agent frameworks +- in-product **agent orchestration frameworks** (FlightDeck does not execute LangGraph/CrewAI-style graphs as core product behavior or become the default runtime for user workflows) - dashboards before CLI workflow is proven - gateways/proxies by default - compliance scanners - fine-tuning ops -- broad plugin systems +- **broad plugin systems** (no dynamic plugin registry, no implicit third-party load-by-name in core) + +**Allowed (adoption glue):** optional, explicitly declared **`[project.optional-dependencies]`** extras that install thin **`flightdeck.integrations.*`** helpers. Those modules only map third-party telemetry into **`RunEvent`** / HTTP ingest and strengthen **developer onboarding** and **runtime evidence**; they do not replace the CLI ledger or ship an embedded orchestrator. ## Public contracts @@ -40,6 +42,7 @@ Treat these as **stable API** unless a change explicitly marks an experimental p - **On-disk / wire:** `release.yaml`, run events, pricing imports, policy shape — **`schemas/`** (generated; drift caught in CI) plus compatibility notes in **`RELEASE_NOTES.md`**. - **v1 / GA direction** (migrations, checksums, trust boundaries, defaults): **`RELEASE_NOTES.md`** and shipped CLI/schema behavior. - **Backlog and milestone status:** **`ROADMAP.md`**. +- **Optional Python `flightdeck.integrations`:** SemVer-tracked but **experimental** until **`RELEASE_NOTES.md`** / **`CHANGELOG.md`** state otherwise. Not the same stability bar as CLI + **`RunEvent`** wire JSON unless explicitly promoted; prefer **`POST /v1/events`** and **`schemas/v1/run_event.schema.json`** as the normative integration surface. ## Engineering rules @@ -95,7 +98,7 @@ Fallback (activated **venv** or global tools): the same steps with **`python -m On **Windows**, use `py -3` in place of `python` if that is how your environment is set up. If pytest temp dirs fail with permissions, see **`DEVELOPMENT.md`** / **`tests/conftest.py`**. -**CI bar** (mirrors **`.github/workflows/ci.yml`** on **CPython 3.14**): see the workflow for the exact sequence; includes **`uv sync --frozen --extra dev`**, **`web/`** **`npm ci`** + **`npm run build`** + **`git diff --exit-code`** on **`static/`**, Playwright **`npm run test:e2e`**, **ruff**, **pytest**, schema drift check, **`flightdeck-quickstart-verify`**, **`flightdeck --help`**. +**CI bar** (mirrors **`.github/workflows/ci.yml`** on **CPython 3.14**): see the workflow for the exact sequence; includes **`uv sync --frozen --extra dev`**, **`web/`** **`npm ci`** + **`npm run build`** + **`git diff --exit-code`** on **`static/`**, Playwright **`npm run test:e2e`**, **ruff**, **pytest**, schema drift check, **`flightdeck-quickstart-verify`**, **`flightdeck --help`**. When you change **`pyproject.toml`** optional extras (including **`flightdeck.integrations`** extras), run **`uv lock`** and commit **`uv.lock`**. The workflow may include a separate **integrations** job that **`uv sync`**s **`dev`** plus selected integration extras and runs targeted tests. Use a repo-local temp directory if the OS temp directory is restricted. diff --git a/CHANGELOG.md b/CHANGELOG.md index c4c4733..504bc13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ This project follows [Semantic Versioning](https://semver.org/). From **v1.0.0** ## Unreleased +### Added + +- **Experimental `flightdeck.integrations`:** optional extras **`integrations-langchain`**, **`integrations-temporal`**, **`integrations-openai-agents`**, and meta **`integrations-ci`** (CI job); thin mappers from OpenAI chat completions, Anthropic messages, OpenAI Agents–style results, LangChain callbacks, CrewAI-style manual totals, and Temporal-oriented **`labels`**. Docs: **`docs/sdk-integrations.md`**; examples: **`examples/integration/adoption/`**. Contributor policy updates in **`AGENTS.md`** / **`CLAUDE.md`**. + ### Changed - **Web Runs:** forensics — empty / offset / truncation messaging, export copy, trace band rows or **Group by trace_id**, **View** drawer (structured fields + full JSON, **session_id** / **span_id**, focus trap + return focus, **`aria-haspopup="dialog"`**), trace/status columns; **run-query** failures show a typed error card with **Retry**. diff --git a/CLAUDE.md b/CLAUDE.md index 7016612..cea0311 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -9,6 +9,7 @@ Canonical repository (full history and maintainer workflows): **[github.com/flig | Topic | Location | |--------|------| | Agent / contributor rules | `AGENTS.md` | +| Runtime integrations / adoption hooks (optional `flightdeck.integrations`, extras, boundaries) | `AGENTS.md` (non-goals), [docs/sdk-integrations.md](docs/sdk-integrations.md), `src/flightdeck/integrations/`, [examples/integration/adoption/](examples/integration/adoption/) | | Cursor IDE rules (CI artifacts, web static) | `.cursor/rules/flightdeck-ci-artifacts.mdc` | | Setup and local demo | `DEVELOPMENT.md` | | CLI flags and exit codes | [README.md](https://github.com/flightdeckdev/flightdeck/blob/main/README.md) (canonical repo) | @@ -35,6 +36,8 @@ If you changed **Pydantic / wire models** affecting **`schemas/`**: **`uv run py If you changed **`web/`** (React UI): from **`web/`**, run **`npm ci`** then **`npm run build`**, then from the repo root **`git diff --exit-code src/flightdeck/server/static/`** must be clean—commit all updates under that path (CI fails otherwise). When behavior changes, run **`npm run test:e2e`** from **`web/`**. +If you changed **`pyproject.toml`** integration extras or **`src/flightdeck/integrations/`** tests: run **`uv lock`**, commit **`uv.lock`**, and **`uv sync --frozen --extra dev --extra integrations-ci`** (see **`DEVELOPMENT.md`**) to match the CI integrations job. + Details: **`AGENTS.md`** (Verification), **`DEVELOPMENT.md`**, and **`.cursor/rules/flightdeck-ci-artifacts.mdc`**. With **pip** + venv: use **`python -m …`** equivalents in **`DEVELOPMENT.md`**. @@ -43,4 +46,4 @@ With **pip** + venv: use **`python -m …`** equivalents in **`DEVELOPMENT.md`** ## Repo shape -Python package under `src/flightdeck/`. Tests in `tests/`. Examples in `examples/quickstart/`. JSON Schemas under `schemas/` (regenerate with **`uv run python scripts/generate_schemas.py`** when models change). Browser UI source in **`web/`**; production bundle committed under **`src/flightdeck/server/static/`** (rebuild with **`npm run build`** in **`web/`**). After **`pyproject.toml`** dependency edits, run **`uv lock`** and commit **`uv.lock`**. +Python package under `src/flightdeck/` (optional **`flightdeck.integrations`** under the same tree). Tests in `tests/`. Examples in `examples/quickstart/` and **`examples/integration/adoption/`**. JSON Schemas under `schemas/` (regenerate with **`uv run python scripts/generate_schemas.py`** when models change). Browser UI source in **`web/`**; production bundle committed under **`src/flightdeck/server/static/`** (rebuild with **`npm run build`** in **`web/`**). After **`pyproject.toml`** dependency edits, run **`uv lock`** and commit **`uv.lock`**. diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 6001138..0102253 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -18,9 +18,10 @@ uv sync --extra dev This creates **`.venv/`** (gitignored), installs **`flightdeck`** editable plus **pytest** and **ruff**, and pins versions from **`uv.lock`**. -Optional extras (telemetry, SDK helpers, PostgreSQL driver): e.g. -**`uv sync --extra dev --extra telemetry`** or **`uv sync --extra dev --extra postgres`** -for **`database_url`** / optional **`tests/test_storage_postgres.py`** runs (**`FLIGHTDECK_TEST_POSTGRES_URL`**). +Optional extras (telemetry, SDK helpers, PostgreSQL driver, integration test deps): e.g. +**`uv sync --extra dev --extra telemetry`**, **`uv sync --extra dev --extra postgres`**, or +**`uv sync --extra dev --extra integrations-ci`** (LangChain / Temporal / OpenAI Agents for +**`tests/test_integrations_langchain.py`** and the same lock resolution CI job uses). Local driver test helper (Docker optional): **`scripts/run_postgres_tests.ps1`** (see script header). ### Package extras @@ -32,9 +33,15 @@ Local driver test helper (Docker optional): **`scripts/run_postgres_tests.ps1`** | `anthropic` | `anthropic>=0.20` | Same, for the Anthropic Python client | | `telemetry` | `opentelemetry-api`, `-sdk`, `-exporter-otlp` | Forward-looking OTLP integration; FlightDeck core does **not** import OpenTelemetry at runtime | | `all` | `openai` + `anthropic` + `telemetry` | All optional packages in one shot | +| `integrations-langchain` | `langchain-core` | Optional **`FlightDeckLangChainCallbackHandler`** (see **`docs/sdk-integrations.md`**) | +| `integrations-temporal` | `temporalio` | Optional worker-side typing helpers; core does not import Temporal | +| `integrations-openai-agents` | `openai-agents` | Optional mapping from Agents SDK run results | +| `integrations-ci` | `langchain-core` + `temporalio` + `openai-agents` | CI / local parity for integration tests (**`uv lock`** resolves this group) | FlightDeck's core package (`flightdeck-ai`) does not import OpenAI, Anthropic, or OpenTelemetry at runtime. These extras exist so your project can declare a single dependency (`flightdeck-ai[openai]`) and get a compatible version of both without resolving conflicts manually. +**`flightdeck.integrations`** (experimental — see **`AGENTS.md`** and **`docs/sdk-integrations.md`**) ships thin mappers behind the **`integrations-*`** extras above. There is **no** declared **`crewai`** extra: install CrewAI in your own environment if you use **`crewai_bridge`**, to avoid pulling a very large dependency tree into **`uv.lock`**. + **Note on listed core dependencies:** `pyproject.toml` currently lists `sqlalchemy`, `aiosqlite`, and `rich` as direct (non-optional) dependencies, but `src/flightdeck/` does not import any of them — the package uses the standard-library `sqlite3` module and plain `click` output. These entries are carried over from earlier prototypes and are scheduled for removal in a future cleanup release. ## Setup (pip — fallback) diff --git a/README.md b/README.md index 74c0a69..e1cd89e 100644 --- a/README.md +++ b/README.md @@ -148,13 +148,14 @@ flightdeck release history --agent agent_support --env local The static event files in `examples/quickstart` use placeholder release IDs so the repo can ship stable examples. Substitute them before ingestion, or run **`uv run flightdeck-quickstart-verify`** / **`python -m flightdeck.quickstart_smoke`** (venv) or **`./scripts/smoke.sh`** from Git Bash/WSL on Windows. -**Examples:** [examples/quickstart/](examples/quickstart/) · [examples/ci/](examples/ci/) (policy gate + Actions) · [examples/deploy/](examples/deploy/) (`serve` via Docker/Compose) · [examples/integration/](examples/integration/) (HTTP event emitter). +**Examples:** [examples/quickstart/](examples/quickstart/) · [examples/ci/](examples/ci/) (policy gate + Actions) · [examples/deploy/](examples/deploy/) (`serve` via Docker/Compose) · [examples/integration/](examples/integration/) (HTTP event emitter) · [examples/integration/adoption/](examples/integration/adoption/) (framework hooks). ## Documentation - [CLI reference](docs/cli.md) — all commands, flags, arguments, and exit codes - [HTTP API reference](docs/http-api.md) — all `/v1/*` routes, request/response shapes, auth, `RunEvent` field reference - [Python SDK](docs/sdk.md) — `FlightdeckClient` / `AsyncFlightdeckClient` usage guide +- [Runtime integrations (experimental)](docs/sdk-integrations.md) — optional `flightdeck.integrations` mappers (LangChain, OpenAI Agents, Temporal, etc.) - [Operations and policy](docs/operations-and-policy.md) — diff, promote, rollback internals; policy model and confidence tiers - [Release artifacts and pricing](docs/release-artifact.md) — `release.yaml` format, bundle layout, checksum algorithm, workspace config, pricing tables - [Pricing catalog](docs/pricing-catalog.md) — optional `pricing_catalog_path`, catalog vs imported tables, troubleshooting diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 65e08d4..1362a11 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -4,6 +4,15 @@ High-level notes for **shipping FlightDeck**. Detailed history: **[CHANGELOG.md] Narrative docs (including the CLI reference) are maintained on **[github.com/flightdeckdev/flightdeck](https://github.com/flightdeckdev/flightdeck)** `main`; this file and **`schemas/`** ship in minimal clones. +## Upcoming — Optional Python integrations (experimental) + +Patch-line documentation: optional **`flightdeck.integrations`** mappers behind **`integrations-*`** +extras (see **`pyproject.toml`**, **`docs/sdk-integrations.md`**, **`examples/integration/adoption/`**). +**Stable contracts unchanged:** **`RunEvent`** JSON / **`POST /v1/events`**. **`AGENTS.md`** clarifies +that these adapters are adoption glue, not in-product orchestration or a plugin registry. CI adds +an **`integrations`** job (**`uv sync --frozen --extra dev --extra integrations-ci`**) for LangChain +callback coverage. + ## v1.1.2 — Forensics filters, JSONL export, productization closure slice Patch release (see **[CHANGELOG.md](CHANGELOG.md)**): optional **`trace_id`** filter on **`GET /v1/runs`**, **`flightdeck runs list --trace-id`**, and SDK **`list_runs(trace_id=…)`** (exact match on **`RunEvent.request.trace_id`**); **`flightdeck runs export`** writes the same filtered slice as JSONL (stdout or **`-o`**, **`--limit`** up to **500**, stderr warning when truncated). **Stable contracts:** additive HTTP query param and CLI command only. diff --git a/docs/sdk-integrations.md b/docs/sdk-integrations.md new file mode 100644 index 0000000..1169281 --- /dev/null +++ b/docs/sdk-integrations.md @@ -0,0 +1,45 @@ +# FlightDeck runtime integrations (experimental) + +Optional Python helpers under **`flightdeck.integrations`** map third-party LLM and workflow +telemetry into **`RunEvent`** models for **`FlightdeckClient.ingest_run_events`** or JSONL ingest. +They strengthen **developer onboarding** and **runtime evidence**; they are **not** a second +product surface for orchestration. + +## Stability and contracts + +- **Normative wire shape:** **`schemas/v1/run_event.schema.json`** and **`POST /v1/events`** + (same as **`flightdeck runs ingest`**). Treat the HTTP payload as the stable contract. +- **`flightdeck.integrations`:** SemVer-tracked but **experimental** until **`RELEASE_NOTES.md`** + / **`CHANGELOG.md`** state otherwise. Helpers may change between minor releases as upstream SDKs + evolve; pin **`flightdeck-ai`** if you depend on a specific mapper. +- **Core import rule:** **`import flightdeck`** does **not** install or import LangChain, Temporal, + OpenAI Agents, etc. Import only the submodule you need (for example + **`flightdeck.integrations.openai_chat`**) after installing the matching extra. + +## Extras (see **`pyproject.toml`**) + +| Extra | Purpose | +|-------|---------| +| **`openai`** | OpenAI Python client alongside FlightDeck (also used by examples) | +| **`anthropic`** | Anthropic Python client alongside FlightDeck | +| **`integrations-langchain`** | **`FlightDeckLangChainCallbackHandler`** in **`langchain_callback.py`** | +| **`integrations-temporal`** | Install **`temporalio`** next to FlightDeck when your worker shares a venv | +| **`integrations-openai-agents`** | **`openai-agents`** for result-shape experiments | +| **`integrations-ci`** | Meta-extra for CI: LangChain + Temporal + OpenAI Agents resolution | + +There is **no** **`crewai`** extra on the distribution. Use **`crewai_bridge.run_event_from_crew_token_totals`** +with totals you collect from CrewAI (or install **`crewai`** only in your application environment). + +## Trust boundaries + +Anyone who can reach **`POST /v1/events`** can append ledger rows. Keep **`flightdeck serve`** +on loopback or a private network unless you add your own controls. See **[SECURITY.md](../SECURITY.md)**. + +## Examples + +Copy-paste scripts: **[examples/integration/adoption/](../examples/integration/adoption/README.md)**. + +## Policy boundary (contributors) + +Contributor rules in **`AGENTS.md`** distinguish **in-product agent frameworks** (non-goals) from +these **narrow, opt-in adoption adapters**. Do not add a dynamic plugin registry. diff --git a/docs/sdk.md b/docs/sdk.md index ff301f1..aedb3c9 100644 --- a/docs/sdk.md +++ b/docs/sdk.md @@ -18,6 +18,10 @@ pip install 'flightdeck-ai' uv add flightdeck-ai ``` +Optional **LangChain / Temporal / OpenAI Agents** mappers ship under **`flightdeck.integrations`** +(experimental; separate extras). See **[sdk-integrations.md](sdk-integrations.md)** and +**[examples/integration/adoption/](../examples/integration/adoption/README.md)**. + ## Quick start ```python diff --git a/examples/README.md b/examples/README.md index 5635299..e990f1b 100644 --- a/examples/README.md +++ b/examples/README.md @@ -4,7 +4,7 @@ This folder holds **copy-pasteable** references for wiring FlightDeck into a rea ## End-to-end loop -1. **Emit run events** from your app or a test harness — see [integration/](integration/README.md) (`emit_sample_events.py` and `POST /v1/events` shape). +1. **Emit run events** from your app or a test harness — see [integration/](integration/README.md) (`emit_sample_events.py` and `POST /v1/events` shape). Optional framework-oriented emitters: [integration/adoption/](integration/adoption/README.md). 2. **Ingest** evidence: `flightdeck runs ingest ` (or JSON array file), or HTTP `POST /v1/events` while `flightdeck serve` is running. 3. **Register** a release bundle: `flightdeck release register ` then **`flightdeck release verify`** against the same tree before you trust the checksum. 4. **Diff and gate** in CI: `flightdeck release diff …` with **`--fail-on-policy`** when you want a non-zero exit without mutating promotion — see [ci/](ci/README.md) and `ledger_gate.py` / GitHub Actions templates. Optional **`pricing_catalog_path`** in `flightdeck.yaml` adds **`pricing.catalog`** / **`pricing.hints`** on diffs (see [docs/pricing-catalog.md](../docs/pricing-catalog.md)). **Same contract in the browser or HTTP:** with `flightdeck serve`, open **`/#/diff`** for structured policy / pricing / rollup sections, or call **`POST /v1/diff`** (matches **`flightdeck release diff --output json`**). Details: [docs/web-ui.md](../docs/web-ui.md), [docs/http-api.md](../docs/http-api.md). @@ -32,5 +32,6 @@ Use this as a **discoverability** pass for the **[ROADMAP.md](../ROADMAP.md)** s | [ci/](ci/README.md) | Policy gate script, sample policy YAML, GitHub Actions job snippets. | | [deploy/](deploy/README.md) | Dockerfile and compose for `flightdeck serve`. | | [integration/](integration/README.md) | Sample event emitter for HTTP ingest. | +| [integration/adoption/](integration/adoption/README.md) | OpenAI, Anthropic, LangChain, Agents SDK, CrewAI-style totals, Temporal labels → `RunEvent`. | | [fleet/](fleet/README.md) | Multi-workspace naming, optional catalog path, approval workflow notes. | | [pricing/catalog.sample.yaml](pricing/catalog.sample.yaml) | Sample `PricingCatalog` for cross-vendor comparable diff costs. | diff --git a/examples/integration/README.md b/examples/integration/README.md index 22bef73..a99bc4c 100644 --- a/examples/integration/README.md +++ b/examples/integration/README.md @@ -2,6 +2,8 @@ These examples show how application or agent runtimes can **push evidence** into FlightDeck over **`POST /v1/events`** (same contract as **`flightdeck runs ingest`**). +**Framework adoption:** [adoption/](adoption/README.md) — optional **`flightdeck.integrations`** helpers and per-vendor scripts (see also **[docs/sdk-integrations.md](../../docs/sdk-integrations.md)**). + ## Prerequisites - A running **`flightdeck serve`** (local CLI or **[examples/deploy](../deploy/README.md)**). diff --git a/examples/integration/adoption/README.md b/examples/integration/adoption/README.md new file mode 100644 index 0000000..4f22285 --- /dev/null +++ b/examples/integration/adoption/README.md @@ -0,0 +1,26 @@ +# Adoption hooks (framework examples) + +These examples show how to turn **OpenAI**, **Anthropic**, **LangChain**, **OpenAI Agents SDK**, +**CrewAI-shaped totals**, or **Temporal** correlation metadata into **`RunEvent`** records and +emit them with **`FlightdeckClient.ingest_run_events`** (same contract as +**[../README.md](../README.md)**). + +Installable helpers live under **`src/flightdeck/integrations/`** with optional **`pyproject.toml`** +extras; see **[docs/sdk-integrations.md](../../../docs/sdk-integrations.md)**. + +## Python 3.14 + +This repository targets **CPython 3.14** only. Third-party wheels may lag; if an extra does not +install, run your agent in a **supported Python sidecar** and POST JSON to **`POST /v1/events`** +using the same **`RunEvent`** shape (**`schemas/v1/run_event.schema.json`**). + +## Layout + +| Directory | Notes | +|-----------|--------| +| [openai_chat/](openai_chat/) | Chat Completions → `RunEvent` | +| [anthropic_messages/](anthropic_messages/) | Messages API → `RunEvent` | +| [openai_agents/](openai_agents/) | Agents SDK result (duck-typed) → `RunEvent` | +| [langchain/](langchain/) | `FlightDeckLangChainCallbackHandler` | +| [crewai/](crewai/) | Totals via **`crewai_bridge`** (no `crewai` extra in the wheel; install CrewAI yourself if needed) | +| [temporal/](temporal/) | Suggested **`labels`** / **`run_id`** pattern | diff --git a/examples/integration/adoption/anthropic_messages/README.md b/examples/integration/adoption/anthropic_messages/README.md new file mode 100644 index 0000000..773d688 --- /dev/null +++ b/examples/integration/adoption/anthropic_messages/README.md @@ -0,0 +1,9 @@ +# Anthropic Messages → FlightDeck + +```bash +uv sync --frozen --extra dev --extra anthropic +uv run python examples/integration/adoption/anthropic_messages/emit_run.py \ + --release-id rel_yourregisteredid --agent-id agent_support +``` + +Add **`--ingest`** to POST. Add **`--live`** for a real **`messages.create`** call (**`ANTHROPIC_API_KEY`**). diff --git a/examples/integration/adoption/anthropic_messages/emit_run.py b/examples/integration/adoption/anthropic_messages/emit_run.py new file mode 100644 index 0000000..d0b9230 --- /dev/null +++ b/examples/integration/adoption/anthropic_messages/emit_run.py @@ -0,0 +1,67 @@ +"""Emit one RunEvent from an Anthropic Messages response.""" + +from __future__ import annotations + +import argparse +import json +from types import SimpleNamespace +from uuid import uuid4 + +from flightdeck.integrations.anthropic_messages import run_event_from_anthropic_message +from flightdeck.sdk.client import FlightdeckClient + + +def _parse_args() -> argparse.Namespace: + p = argparse.ArgumentParser(description=__doc__) + p.add_argument("--base-url", default="http://127.0.0.1:8765") + p.add_argument("--release-id", required=True) + p.add_argument("--agent-id", required=True) + p.add_argument("--environment", default="local") + p.add_argument("--ingest", action="store_true") + p.add_argument("--live", action="store_true") + return p.parse_args() + + +def _synthetic_message() -> object: + usage = SimpleNamespace(input_tokens=80, output_tokens=30, cache_read_input_tokens=0) + return SimpleNamespace(model="claude-3-5-haiku-20241022", usage=usage) + + +def main() -> None: + args = _parse_args() + if args.live: + import anthropic + + client = anthropic.Anthropic() + msg = client.messages.create( + model="claude-3-5-haiku-20241022", + max_tokens=32, + messages=[{"role": "user", "content": "Say ok in one word."}], + ) + else: + msg = _synthetic_message() + + rid = uuid4().hex[:10] + ev = run_event_from_anthropic_message( + msg, + agent_id=args.agent_id, + release_id=args.release_id, + run_id=f"adopt-anthropic-{rid}", + tenant_id="tenant_example", + task_id="task_example", + environment=args.environment, + labels={"source": "examples/integration/adoption/anthropic_messages/emit_run.py"}, + ) + if not args.ingest: + print(json.dumps({"events": [ev.model_dump(mode="json")]}, indent=2)) + return + client = FlightdeckClient(args.base_url.rstrip("/")) + try: + n = client.ingest_run_events([ev]) + finally: + client.close() + print(f"Inserted {n} event(s).") + + +if __name__ == "__main__": + main() diff --git a/examples/integration/adoption/crewai/README.md b/examples/integration/adoption/crewai/README.md new file mode 100644 index 0000000..9ec7074 --- /dev/null +++ b/examples/integration/adoption/crewai/README.md @@ -0,0 +1,11 @@ +# CrewAI-style totals → FlightDeck + +There is **no** `crewai` wheel extra on **`flightdeck-ai`** (CrewAI pulls a very large resolver +graph). After **`Crew.kickoff()`**, take aggregated token totals from your telemetry and call +**`run_event_from_crew_token_totals`** from **`flightdeck.integrations.crewai_bridge`**. + +```bash +uv sync --frozen --extra dev +uv run python examples/integration/adoption/crewai/emit_totals.py \ + --release-id rel_yourregisteredid --agent-id agent_support +``` diff --git a/examples/integration/adoption/crewai/emit_totals.py b/examples/integration/adoption/crewai/emit_totals.py new file mode 100644 index 0000000..ef15812 --- /dev/null +++ b/examples/integration/adoption/crewai/emit_totals.py @@ -0,0 +1,52 @@ +"""Emit a RunEvent from explicit token totals (CrewAI-style manual aggregation).""" + +from __future__ import annotations + +import argparse +import json +from uuid import uuid4 + +from flightdeck.integrations.crewai_bridge import run_event_from_crew_token_totals +from flightdeck.sdk.client import FlightdeckClient + + +def _parse_args() -> argparse.Namespace: + p = argparse.ArgumentParser(description=__doc__) + p.add_argument("--base-url", default="http://127.0.0.1:8765") + p.add_argument("--release-id", required=True) + p.add_argument("--agent-id", required=True) + p.add_argument("--environment", default="local") + p.add_argument("--ingest", action="store_true") + return p.parse_args() + + +def main() -> None: + args = _parse_args() + rid = uuid4().hex[:10] + ev = run_event_from_crew_token_totals( + agent_id=args.agent_id, + release_id=args.release_id, + run_id=f"adopt-crewai-{rid}", + tenant_id="tenant_example", + task_id="task_example", + environment=args.environment, + provider="openai", + model="gpt-4.1-mini", + input_tokens=500, + output_tokens=120, + crew_name="example_crew", + labels={"source": "examples/integration/adoption/crewai/emit_totals.py"}, + ) + if not args.ingest: + print(json.dumps({"events": [ev.model_dump(mode="json")]}, indent=2)) + return + client = FlightdeckClient(args.base_url.rstrip("/")) + try: + n = client.ingest_run_events([ev]) + finally: + client.close() + print(f"Inserted {n} event(s).") + + +if __name__ == "__main__": + main() diff --git a/examples/integration/adoption/langchain/README.md b/examples/integration/adoption/langchain/README.md new file mode 100644 index 0000000..0a174b2 --- /dev/null +++ b/examples/integration/adoption/langchain/README.md @@ -0,0 +1,9 @@ +# LangChain Core → FlightDeck + +```bash +uv sync --frozen --extra dev --extra integrations-langchain +uv run python examples/integration/adoption/langchain/emit_run.py \ + --release-id rel_yourregisteredid --agent-id agent_support +``` + +Uses **`FlightDeckLangChainCallbackHandler`** with a synthetic **`on_llm_end`** payload (no LLM network call). diff --git a/examples/integration/adoption/langchain/emit_run.py b/examples/integration/adoption/langchain/emit_run.py new file mode 100644 index 0000000..a4f792a --- /dev/null +++ b/examples/integration/adoption/langchain/emit_run.py @@ -0,0 +1,54 @@ +"""Accumulate LangChain-style LLM usage and emit one RunEvent.""" + +from __future__ import annotations + +import argparse +import json +from types import SimpleNamespace +from uuid import uuid4 + +from flightdeck.integrations.langchain_callback import FlightDeckLangChainCallbackHandler +from flightdeck.sdk.client import FlightdeckClient + + +def _parse_args() -> argparse.Namespace: + p = argparse.ArgumentParser(description=__doc__) + p.add_argument("--base-url", default="http://127.0.0.1:8765") + p.add_argument("--release-id", required=True) + p.add_argument("--agent-id", required=True) + p.add_argument("--environment", default="local") + p.add_argument("--ingest", action="store_true") + return p.parse_args() + + +def main() -> None: + args = _parse_args() + rid = uuid4().hex[:10] + h = FlightDeckLangChainCallbackHandler( + agent_id=args.agent_id, + release_id=args.release_id, + run_id=f"adopt-langchain-{rid}", + tenant_id="tenant_example", + task_id="task_example", + environment=args.environment, + labels={"source": "examples/integration/adoption/langchain/emit_run.py"}, + ) + llm_output = { + "token_usage": {"prompt_tokens": 200, "completion_tokens": 60}, + "model_name": "gpt-4.1-mini", + } + h.on_llm_end(SimpleNamespace(generations=[], llm_output=llm_output)) + ev = h.to_run_event() + if not args.ingest: + print(json.dumps({"events": [ev.model_dump(mode="json")]}, indent=2)) + return + client = FlightdeckClient(args.base_url.rstrip("/")) + try: + n = client.ingest_run_events([ev]) + finally: + client.close() + print(f"Inserted {n} event(s).") + + +if __name__ == "__main__": + main() diff --git a/examples/integration/adoption/openai_agents/README.md b/examples/integration/adoption/openai_agents/README.md new file mode 100644 index 0000000..51f10dc --- /dev/null +++ b/examples/integration/adoption/openai_agents/README.md @@ -0,0 +1,10 @@ +# OpenAI Agents SDK → FlightDeck + +Install **`flightdeck-ai[integrations-openai-agents]`** (see root **`pyproject.toml`**). The +helper **duck-types** the run result object; verify fields against your installed SDK version. + +```bash +uv sync --frozen --extra dev --extra integrations-openai-agents +uv run python examples/integration/adoption/openai_agents/emit_run.py \ + --release-id rel_yourregisteredid --agent-id agent_support +``` diff --git a/examples/integration/adoption/openai_agents/emit_run.py b/examples/integration/adoption/openai_agents/emit_run.py new file mode 100644 index 0000000..98ae2b1 --- /dev/null +++ b/examples/integration/adoption/openai_agents/emit_run.py @@ -0,0 +1,52 @@ +"""Emit a RunEvent from a synthetic OpenAI Agents-style result (usage dict).""" + +from __future__ import annotations + +import argparse +import json +from types import SimpleNamespace +from uuid import uuid4 + +from flightdeck.integrations.openai_agents import run_event_from_openai_agents_result +from flightdeck.sdk.client import FlightdeckClient + + +def _parse_args() -> argparse.Namespace: + p = argparse.ArgumentParser(description=__doc__) + p.add_argument("--base-url", default="http://127.0.0.1:8765") + p.add_argument("--release-id", required=True) + p.add_argument("--agent-id", required=True) + p.add_argument("--environment", default="local") + p.add_argument("--ingest", action="store_true") + return p.parse_args() + + +def main() -> None: + args = _parse_args() + result = SimpleNamespace( + usage={"input_tokens": 55, "output_tokens": 12, "model": "gpt-4.1-mini"}, + ) + rid = uuid4().hex[:10] + ev = run_event_from_openai_agents_result( + result, + agent_id=args.agent_id, + release_id=args.release_id, + run_id=f"adopt-oai-agents-{rid}", + tenant_id="tenant_example", + task_id="task_example", + environment=args.environment, + labels={"source": "examples/integration/adoption/openai_agents/emit_run.py"}, + ) + if not args.ingest: + print(json.dumps({"events": [ev.model_dump(mode="json")]}, indent=2)) + return + client = FlightdeckClient(args.base_url.rstrip("/")) + try: + n = client.ingest_run_events([ev]) + finally: + client.close() + print(f"Inserted {n} event(s).") + + +if __name__ == "__main__": + main() diff --git a/examples/integration/adoption/openai_chat/README.md b/examples/integration/adoption/openai_chat/README.md new file mode 100644 index 0000000..ee2dd7d --- /dev/null +++ b/examples/integration/adoption/openai_chat/README.md @@ -0,0 +1,11 @@ +# OpenAI Chat Completions → FlightDeck + +```bash +uv sync --frozen --extra dev --extra openai +uv run python examples/integration/adoption/openai_chat/emit_run.py \ + --release-id rel_yourregisteredid --agent-id agent_support +``` + +Prints a synthetic completion payload as JSON. Add **`--ingest`** to **`POST /v1/events`** while +**`flightdeck serve`** is running. Add **`--live`** for a real completion (requires +**`OPENAI_API_KEY`**). diff --git a/examples/integration/adoption/openai_chat/emit_run.py b/examples/integration/adoption/openai_chat/emit_run.py new file mode 100644 index 0000000..044c860 --- /dev/null +++ b/examples/integration/adoption/openai_chat/emit_run.py @@ -0,0 +1,77 @@ +"""Emit one RunEvent from an OpenAI chat completion (synthetic, ingest, or live+ingest).""" + +from __future__ import annotations + +import argparse +import json +from types import SimpleNamespace +from uuid import uuid4 + +from flightdeck.integrations.openai_chat import run_event_from_openai_chat_completion +from flightdeck.sdk.client import FlightdeckClient + + +def _parse_args() -> argparse.Namespace: + p = argparse.ArgumentParser(description=__doc__) + p.add_argument("--base-url", default="http://127.0.0.1:8765") + p.add_argument("--release-id", required=True) + p.add_argument("--agent-id", required=True) + p.add_argument("--environment", default="local") + p.add_argument( + "--ingest", + action="store_true", + help="POST the built RunEvent to FlightDeck (otherwise print JSON only)", + ) + p.add_argument( + "--live", + action="store_true", + help="Call OpenAI (requires OPENAI_API_KEY and flightdeck-ai[openai])", + ) + return p.parse_args() + + +def _synthetic_response() -> object: + usage = SimpleNamespace( + prompt_tokens=120, + completion_tokens=40, + prompt_tokens_details=SimpleNamespace(cached_tokens=0), + ) + return SimpleNamespace(model="gpt-4.1-mini", usage=usage) + + +def main() -> None: + args = _parse_args() + if args.live: + from openai import OpenAI + + resp = OpenAI().chat.completions.create( + model="gpt-4.1-mini", + messages=[{"role": "user", "content": "Say ok in one word."}], + ) + else: + resp = _synthetic_response() + + rid = uuid4().hex[:10] + ev = run_event_from_openai_chat_completion( + resp, + agent_id=args.agent_id, + release_id=args.release_id, + run_id=f"adopt-openai-{rid}", + tenant_id="tenant_example", + task_id="task_example", + environment=args.environment, + labels={"source": "examples/integration/adoption/openai_chat/emit_run.py"}, + ) + if not args.ingest: + print(json.dumps({"events": [ev.model_dump(mode="json")]}, indent=2)) + return + client = FlightdeckClient(args.base_url.rstrip("/")) + try: + n = client.ingest_run_events([ev]) + finally: + client.close() + print(f"Inserted {n} event(s).") + + +if __name__ == "__main__": + main() diff --git a/examples/integration/adoption/temporal/README.md b/examples/integration/adoption/temporal/README.md new file mode 100644 index 0000000..750969e --- /dev/null +++ b/examples/integration/adoption/temporal/README.md @@ -0,0 +1,14 @@ +# Temporal → FlightDeck labels + +FlightDeck does not import **`temporalio`** in core. Use **`temporal_labels`** from +**`flightdeck.integrations.common`** (or copy the dict keys) so **`RunEvent.labels`** correlate +ledger rows with **`workflow_id`** / **`run_id`**. + +```bash +uv sync --frozen --extra dev +uv run python examples/integration/adoption/temporal/emit_run.py \ + --release-id rel_yourregisteredid --agent-id agent_support +``` + +Optional: install **`flightdeck-ai[integrations-temporal]`** if your worker code shares that venv +for other reasons; this example only needs **`make_run_end_event`**. diff --git a/examples/integration/adoption/temporal/emit_run.py b/examples/integration/adoption/temporal/emit_run.py new file mode 100644 index 0000000..13b7160 --- /dev/null +++ b/examples/integration/adoption/temporal/emit_run.py @@ -0,0 +1,58 @@ +"""Emit a RunEvent with Temporal-oriented labels (synthetic usage).""" + +from __future__ import annotations + +import argparse +import json +from uuid import uuid4 + +from flightdeck.integrations.common import make_run_end_event, temporal_labels +from flightdeck.sdk.client import FlightdeckClient + + +def _parse_args() -> argparse.Namespace: + p = argparse.ArgumentParser(description=__doc__) + p.add_argument("--base-url", default="http://127.0.0.1:8765") + p.add_argument("--release-id", required=True) + p.add_argument("--agent-id", required=True) + p.add_argument("--environment", default="local") + p.add_argument("--workflow-id", default="wf_example_001") + p.add_argument("--workflow-run-id", default="") + p.add_argument("--ingest", action="store_true") + return p.parse_args() + + +def main() -> None: + args = _parse_args() + rid = uuid4().hex[:10] + wf_run = args.workflow_run_id or None + labels = { + **temporal_labels(workflow_id=args.workflow_id, workflow_run_id=wf_run), + "source": "examples/integration/adoption/temporal/emit_run.py", + } + ev = make_run_end_event( + agent_id=args.agent_id, + release_id=args.release_id, + run_id=f"adopt-temporal-{rid}", + tenant_id="tenant_example", + task_id="task_example", + environment=args.environment, + provider="openai", + model="gpt-4.1-mini", + input_tokens=10, + output_tokens=5, + labels=labels, + ) + if not args.ingest: + print(json.dumps({"events": [ev.model_dump(mode="json")]}, indent=2)) + return + client = FlightdeckClient(args.base_url.rstrip("/")) + try: + n = client.ingest_run_events([ev]) + finally: + client.close() + print(f"Inserted {n} event(s).") + + +if __name__ == "__main__": + main() diff --git a/pyproject.toml b/pyproject.toml index ddd9578..f8735fd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -52,6 +52,15 @@ dev = [ postgres = [ "psycopg[binary]>=3.2", ] +# Optional adoption hooks (see docs/sdk-integrations.md). Not imported by core runtime. +integrations-langchain = ["langchain-core>=0.3.0"] +integrations-temporal = ["temporalio>=1.8.0"] +integrations-openai-agents = ["openai-agents>=0.14.0"] +integrations-ci = [ + "langchain-core>=0.3.0", + "temporalio>=1.8.0", + "openai-agents>=0.14.0", +] [project.urls] Homepage = "https://github.com/flightdeckdev/flightdeck" diff --git a/src/flightdeck/integrations/__init__.py b/src/flightdeck/integrations/__init__.py new file mode 100644 index 0000000..07ae11b --- /dev/null +++ b/src/flightdeck/integrations/__init__.py @@ -0,0 +1,13 @@ +"""Experimental adoption helpers: map runtime telemetry to v1 ``RunEvent`` models. + +These modules are **optional** and may require ``pip install 'flightdeck-ai[…]'`` extras; see +**``docs/sdk-integrations.md``**. They do not change core CLI behavior. The normative wire +contract remains **``schemas/v1/run_event.schema.json``** and **``POST /v1/events``**. + +Import specific helpers from submodules (for example ``flightdeck.integrations.common``) so +optional third-party packages are only loaded when you use that integration. +""" + +from flightdeck.integrations.common import make_run_end_event, temporal_labels + +__all__ = ["make_run_end_event", "temporal_labels"] diff --git a/src/flightdeck/integrations/anthropic_messages.py b/src/flightdeck/integrations/anthropic_messages.py new file mode 100644 index 0000000..b9d036d --- /dev/null +++ b/src/flightdeck/integrations/anthropic_messages.py @@ -0,0 +1,60 @@ +"""Map Anthropic Messages API responses to RunEvent (optional ``anthropic`` extra for callers).""" + +from __future__ import annotations + +from typing import Any + +from flightdeck.integrations.common import make_run_end_event +from flightdeck.models import RunEvent + + +def token_totals_from_anthropic_message(message: object) -> tuple[int, int, int, str]: + """Return ``(input_tokens, output_tokens, cached_input_tokens, model_id)`` using duck typing.""" + model = getattr(message, "model", None) or "unknown" + usage = getattr(message, "usage", None) + if usage is None: + return 0, 0, 0, str(model) + inp = int(getattr(usage, "input_tokens", 0) or 0) + out = int(getattr(usage, "output_tokens", 0) or 0) + cached = int(getattr(usage, "cache_read_input_tokens", 0) or 0) + return inp, out, cached, str(model) + + +def run_event_from_anthropic_message( + message: object, + *, + agent_id: str, + release_id: str, + run_id: str, + tenant_id: str, + task_id: str, + environment: str, + labels: dict[str, Any] | None = None, + trace_id: str | None = None, + session_id: str | None = None, + latency_ms: int | None = None, + success: bool = True, + error_type: str | None = None, +) -> RunEvent: + """Build a ``RunEvent`` from an Anthropic ``Message`` object.""" + inp, out, cached, model = token_totals_from_anthropic_message(message) + merged = {"integration": "anthropic_messages", **(labels or {})} + return make_run_end_event( + agent_id=agent_id, + release_id=release_id, + run_id=run_id, + tenant_id=tenant_id, + task_id=task_id, + environment=environment, + provider="anthropic", + model=model, + input_tokens=inp, + output_tokens=out, + cached_input_tokens=cached, + latency_ms=latency_ms, + success=success, + error_type=error_type, + trace_id=trace_id, + session_id=session_id, + labels=merged, + ) diff --git a/src/flightdeck/integrations/common.py b/src/flightdeck/integrations/common.py new file mode 100644 index 0000000..b66c506 --- /dev/null +++ b/src/flightdeck/integrations/common.py @@ -0,0 +1,71 @@ +"""Shared helpers for building v1 RunEvent payloads (no third-party imports).""" + +from __future__ import annotations + +from datetime import datetime, timezone +from typing import Any + +from flightdeck.models import RunEvent, RunEventMetrics, RunEventModelUsage, RunEventRequest, RunEventUsage + + +def make_run_end_event( + *, + agent_id: str, + release_id: str, + run_id: str, + tenant_id: str, + task_id: str, + environment: str, + provider: str, + model: str, + input_tokens: int, + output_tokens: int, + cached_input_tokens: int = 0, + latency_ms: int | None = None, + success: bool = True, + error_type: str | None = None, + trace_id: str | None = None, + session_id: str | None = None, + span_id: str | None = None, + labels: dict[str, Any] | None = None, + timestamp: datetime | None = None, + workspace_id: str = "ws_local", +) -> RunEvent: + """Construct a type=run_end RunEvent with model usage (stable v1 wire shape).""" + req: RunEventRequest | None = None + if trace_id is not None or session_id is not None or span_id is not None: + req = RunEventRequest(session_id=session_id, trace_id=trace_id, span_id=span_id) + return RunEvent( + timestamp=timestamp or datetime.now(timezone.utc), + workspace_id=workspace_id, + agent_id=agent_id, + release_id=release_id, + run_id=run_id, + tenant_id=tenant_id, + task_id=task_id, + environment=environment, + request=req, + metrics=RunEventMetrics( + latency_ms=latency_ms, + success=success, + error_type=error_type, + ), + usage=RunEventUsage( + model=RunEventModelUsage( + provider=provider, + model=model, + input_tokens=max(0, int(input_tokens)), + output_tokens=max(0, int(output_tokens)), + cached_input_tokens=max(0, int(cached_input_tokens)), + ), + ), + labels=dict(labels or {}), + ) + + +def temporal_labels(*, workflow_id: str, workflow_run_id: str | None = None) -> dict[str, str]: + """Suggested labels when emitting from Temporal (no temporalio import).""" + out = {"temporal.workflow_id": workflow_id} + if workflow_run_id: + out["temporal.run_id"] = workflow_run_id + return out diff --git a/src/flightdeck/integrations/crewai_bridge.py b/src/flightdeck/integrations/crewai_bridge.py new file mode 100644 index 0000000..e778149 --- /dev/null +++ b/src/flightdeck/integrations/crewai_bridge.py @@ -0,0 +1,57 @@ +"""CrewAI-friendly RunEvent construction without importing CrewAI at module import time. + +Callers typically invoke :func:`run_event_from_crew_token_totals` after ``Crew.kickoff()`` with +token counts taken from CrewAI logs or a custom callback, until a stable usage API is available +across versions. +""" + +from __future__ import annotations + +from typing import Any + +from flightdeck.integrations.common import make_run_end_event +from flightdeck.models import RunEvent + + +def run_event_from_crew_token_totals( + *, + agent_id: str, + release_id: str, + run_id: str, + tenant_id: str, + task_id: str, + environment: str, + provider: str, + model: str, + input_tokens: int, + output_tokens: int, + cached_input_tokens: int = 0, + crew_name: str | None = None, + labels: dict[str, Any] | None = None, + trace_id: str | None = None, + latency_ms: int | None = None, + success: bool = True, + error_type: str | None = None, +) -> RunEvent: + """Build a ``RunEvent`` from aggregated LLM totals after a CrewAI run.""" + merged: dict[str, Any] = {"integration": "crewai", **(labels or {})} + if crew_name: + merged["crew.name"] = crew_name + return make_run_end_event( + agent_id=agent_id, + release_id=release_id, + run_id=run_id, + tenant_id=tenant_id, + task_id=task_id, + environment=environment, + provider=provider, + model=model, + input_tokens=input_tokens, + output_tokens=output_tokens, + cached_input_tokens=cached_input_tokens, + latency_ms=latency_ms, + success=success, + error_type=error_type, + trace_id=trace_id, + labels=merged, + ) diff --git a/src/flightdeck/integrations/langchain_callback.py b/src/flightdeck/integrations/langchain_callback.py new file mode 100644 index 0000000..443319a --- /dev/null +++ b/src/flightdeck/integrations/langchain_callback.py @@ -0,0 +1,109 @@ +"""LangChain Core callback handler that aggregates token usage into a RunEvent. + +Requires the ``integrations-langchain`` extra (``langchain-core``). + +Import this submodule only when that dependency is installed: + +``from flightdeck.integrations.langchain_callback import FlightDeckLangChainCallbackHandler`` +""" + +from __future__ import annotations + +from typing import Any + +from langchain_core.callbacks import BaseCallbackHandler + +from flightdeck.integrations.common import make_run_end_event +from flightdeck.models import RunEvent + + +def _usage_from_llm_result(response: Any) -> tuple[int, int, int, str]: + """Best-effort token totals and model name from an LLMResult-like object.""" + inp = out = cached = 0 + model = "unknown" + llm_output = getattr(response, "llm_output", None) or {} + if isinstance(llm_output, dict): + tu = llm_output.get("token_usage") or {} + inp = int(tu.get("prompt_tokens") or tu.get("input_tokens") or 0) + out = int(tu.get("completion_tokens") or tu.get("output_tokens") or 0) + model = str(llm_output.get("model_name") or model) + gens = getattr(response, "generations", None) or [] + for gen_list in gens: + for gen in gen_list: + msg = getattr(gen, "message", None) + meta = getattr(msg, "usage_metadata", None) if msg is not None else None + if isinstance(meta, dict): + inp += int(meta.get("input_tokens") or 0) + out += int(meta.get("output_tokens") or 0) + itd = meta.get("input_token_details") + if isinstance(itd, dict): + cached += int(itd.get("cache_read") or 0) + model = str(meta.get("model_name") or meta.get("model") or model) + return inp, out, cached, model + + +class FlightDeckLangChainCallbackHandler(BaseCallbackHandler): + """Accumulate LLM token usage across steps; materialize with :meth:`to_run_event`.""" + + def __init__( + self, + *, + agent_id: str, + release_id: str, + run_id: str, + tenant_id: str, + task_id: str, + environment: str, + provider: str = "openai", + labels: dict[str, Any] | None = None, + ) -> None: + super().__init__() + self._agent_id = agent_id + self._release_id = release_id + self._run_id = run_id + self._tenant_id = tenant_id + self._task_id = task_id + self._environment = environment + self._provider = provider + self._labels = {"integration": "langchain", **(labels or {})} + self._inp = 0 + self._out = 0 + self._cached = 0 + self._model = "unknown" + + def on_llm_end(self, response: Any, **kwargs: Any) -> None: # noqa: ANN401 + di, do, dc, model = _usage_from_llm_result(response) + self._inp += di + self._out += do + self._cached += dc + if model != "unknown": + self._model = model + + def to_run_event( + self, + *, + latency_ms: int | None = None, + success: bool = True, + error_type: str | None = None, + trace_id: str | None = None, + session_id: str | None = None, + ) -> RunEvent: + return make_run_end_event( + agent_id=self._agent_id, + release_id=self._release_id, + run_id=self._run_id, + tenant_id=self._tenant_id, + task_id=self._task_id, + environment=self._environment, + provider=self._provider, + model=self._model, + input_tokens=self._inp, + output_tokens=self._out, + cached_input_tokens=self._cached, + latency_ms=latency_ms, + success=success, + error_type=error_type, + trace_id=trace_id, + session_id=session_id, + labels=dict(self._labels), + ) diff --git a/src/flightdeck/integrations/openai_agents.py b/src/flightdeck/integrations/openai_agents.py new file mode 100644 index 0000000..66ed0e6 --- /dev/null +++ b/src/flightdeck/integrations/openai_agents.py @@ -0,0 +1,78 @@ +"""Helpers for OpenAI Agents SDK runs (optional ``integrations-openai-agents`` extra). + +The Agents SDK surface evolves; these helpers use duck typing on the **result** object returned +from a run helper (for example ``Runner.run``) and optional context attributes. +""" + +from __future__ import annotations + +from typing import Any + +from flightdeck.integrations.common import make_run_end_event +from flightdeck.models import RunEvent + + +def _gather_usage_dict(obj: object) -> dict[str, Any]: + """Best-effort extraction of usage-like mappings from SDK result/context objects.""" + for attr in ("usage", "token_usage", "metrics"): + got = getattr(obj, attr, None) + if isinstance(got, dict): + return got + # Some versions nest usage on context + ctx = getattr(obj, "context", None) + if ctx is not None: + for attr in ("usage", "token_usage"): + got = getattr(ctx, attr, None) + if isinstance(got, dict): + return got + return {} + + +def token_totals_from_openai_agents_result(result: object) -> tuple[int, int, int, str]: + """Return ``(input_tokens, output_tokens, cached_input_tokens, model_guess)``.""" + usage = _gather_usage_dict(result) + inp = int(usage.get("input_tokens") or usage.get("prompt_tokens") or 0) + out = int(usage.get("output_tokens") or usage.get("completion_tokens") or 0) + cached = int(usage.get("cached_input_tokens") or usage.get("cached_tokens") or 0) + model = str(usage.get("model") or getattr(result, "model", None) or "unknown") + return inp, out, cached, model + + +def run_event_from_openai_agents_result( + result: object, + *, + agent_id: str, + release_id: str, + run_id: str, + tenant_id: str, + task_id: str, + environment: str, + labels: dict[str, Any] | None = None, + trace_id: str | None = None, + session_id: str | None = None, + latency_ms: int | None = None, + success: bool = True, + error_type: str | None = None, +) -> RunEvent: + """Build a ``RunEvent`` after an OpenAI Agents SDK run completes.""" + inp, out, cached, model = token_totals_from_openai_agents_result(result) + merged = {"integration": "openai_agents", **(labels or {})} + return make_run_end_event( + agent_id=agent_id, + release_id=release_id, + run_id=run_id, + tenant_id=tenant_id, + task_id=task_id, + environment=environment, + provider="openai", + model=model, + input_tokens=inp, + output_tokens=out, + cached_input_tokens=cached, + latency_ms=latency_ms, + success=success, + error_type=error_type, + trace_id=trace_id, + session_id=session_id, + labels=merged, + ) diff --git a/src/flightdeck/integrations/openai_chat.py b/src/flightdeck/integrations/openai_chat.py new file mode 100644 index 0000000..7d792b8 --- /dev/null +++ b/src/flightdeck/integrations/openai_chat.py @@ -0,0 +1,67 @@ +"""Map OpenAI Chat Completions API responses to RunEvent (optional ``openai`` extra for callers).""" + +from __future__ import annotations + +from typing import Any + +from flightdeck.integrations.common import make_run_end_event +from flightdeck.models import RunEvent + + +def token_totals_from_openai_chat_completion(response: object) -> tuple[int, int, int, str]: + """Return ``(input_tokens, output_tokens, cached_input_tokens, model_id)`` from a completion response. + + Uses duck typing so tests do not require the ``openai`` package. Missing usage yields zeros + and ``"unknown"`` for the model. + """ + model = getattr(response, "model", None) or "unknown" + usage = getattr(response, "usage", None) + if usage is None: + return 0, 0, 0, str(model) + inp = int(getattr(usage, "prompt_tokens", 0) or 0) + out = int(getattr(usage, "completion_tokens", 0) or 0) + cached = 0 + details = getattr(usage, "prompt_tokens_details", None) + if details is not None: + cached = int(getattr(details, "cached_tokens", 0) or 0) + return inp, out, cached, str(model) + + +def run_event_from_openai_chat_completion( + response: object, + *, + agent_id: str, + release_id: str, + run_id: str, + tenant_id: str, + task_id: str, + environment: str, + labels: dict[str, Any] | None = None, + trace_id: str | None = None, + session_id: str | None = None, + latency_ms: int | None = None, + success: bool = True, + error_type: str | None = None, +) -> RunEvent: + """Build a ``RunEvent`` from an OpenAI chat completion ``response`` object.""" + inp, out, cached, model = token_totals_from_openai_chat_completion(response) + merged = {"integration": "openai_chat", **(labels or {})} + return make_run_end_event( + agent_id=agent_id, + release_id=release_id, + run_id=run_id, + tenant_id=tenant_id, + task_id=task_id, + environment=environment, + provider="openai", + model=model, + input_tokens=inp, + output_tokens=out, + cached_input_tokens=cached, + latency_ms=latency_ms, + success=success, + error_type=error_type, + trace_id=trace_id, + session_id=session_id, + labels=merged, + ) diff --git a/tests/test_integrations.py b/tests/test_integrations.py new file mode 100644 index 0000000..2ff30e4 --- /dev/null +++ b/tests/test_integrations.py @@ -0,0 +1,115 @@ +"""Unit tests for flightdeck.integrations (no optional third-party deps for core cases).""" + +from __future__ import annotations + +from datetime import datetime, timezone +from types import SimpleNamespace + +from flightdeck.integrations import common +from flightdeck.integrations import anthropic_messages +from flightdeck.integrations import crewai_bridge +from flightdeck.integrations import openai_agents +from flightdeck.integrations import openai_chat + + +def test_make_run_end_event_minimal() -> None: + ts = datetime(2026, 1, 2, tzinfo=timezone.utc) + ev = common.make_run_end_event( + agent_id="a", + release_id="r", + run_id="run1", + tenant_id="t", + task_id="k", + environment="local", + provider="openai", + model="gpt-4o-mini", + input_tokens=10, + output_tokens=3, + timestamp=ts, + ) + assert ev.api_version == "v1" + assert ev.type == "run_end" + assert ev.timestamp == ts + assert ev.usage.model.input_tokens == 10 + assert ev.usage.model.output_tokens == 3 + assert ev.usage.model.cached_input_tokens == 0 + + +def test_temporal_labels() -> None: + assert common.temporal_labels(workflow_id="wf1") == {"temporal.workflow_id": "wf1"} + assert common.temporal_labels(workflow_id="wf1", workflow_run_id="wr1")[ + "temporal.run_id" + ] == "wr1" + + +def test_openai_chat_completion_usage() -> None: + usage = SimpleNamespace( + prompt_tokens=100, + completion_tokens=40, + prompt_tokens_details=SimpleNamespace(cached_tokens=20), + ) + resp = SimpleNamespace(model="gpt-4.1-mini", usage=usage) + inp, out, cached, model = openai_chat.token_totals_from_openai_chat_completion(resp) + assert (inp, out, cached, model) == (100, 40, 20, "gpt-4.1-mini") + ev = openai_chat.run_event_from_openai_chat_completion( + resp, + agent_id="agent_support", + release_id="rel_x", + run_id="run_openai", + tenant_id="tenant_a", + task_id="t1", + environment="local", + ) + assert ev.usage.model.provider == "openai" + assert ev.labels.get("integration") == "openai_chat" + + +def test_anthropic_message_usage() -> None: + usage = SimpleNamespace(input_tokens=50, output_tokens=25, cache_read_input_tokens=10) + msg = SimpleNamespace(model="claude-3-5-haiku-20241022", usage=usage) + inp, out, cached, model = anthropic_messages.token_totals_from_anthropic_message(msg) + assert (inp, out, cached) == (50, 25, 10) + ev = anthropic_messages.run_event_from_anthropic_message( + msg, + agent_id="agent_support", + release_id="rel_x", + run_id="run_ant", + tenant_id="tenant_a", + task_id="t1", + environment="local", + ) + assert ev.usage.model.provider == "anthropic" + assert ev.labels.get("integration") == "anthropic_messages" + + +def test_openai_agents_result_dict_usage() -> None: + result = SimpleNamespace(usage={"input_tokens": 9, "output_tokens": 2, "model": "gpt-4o"}) + ev = openai_agents.run_event_from_openai_agents_result( + result, + agent_id="a", + release_id="r", + run_id="run_ag", + tenant_id="t", + task_id="k", + environment="local", + ) + assert ev.usage.model.input_tokens == 9 + assert ev.usage.model.output_tokens == 2 + + +def test_crewai_bridge() -> None: + ev = crewai_bridge.run_event_from_crew_token_totals( + agent_id="a", + release_id="r", + run_id="run_c", + tenant_id="t", + task_id="k", + environment="local", + provider="openai", + model="gpt-4o-mini", + input_tokens=200, + output_tokens=50, + crew_name="research", + ) + assert ev.labels.get("integration") == "crewai" + assert ev.labels.get("crew.name") == "research" diff --git a/tests/test_integrations_langchain.py b/tests/test_integrations_langchain.py new file mode 100644 index 0000000..bdaeae6 --- /dev/null +++ b/tests/test_integrations_langchain.py @@ -0,0 +1,31 @@ +"""LangChain callback tests (requires ``integrations-ci`` / ``integrations-langchain``).""" + +from __future__ import annotations + +from types import SimpleNamespace + +import pytest + +pytest.importorskip("langchain_core") + +from flightdeck.integrations.langchain_callback import ( # noqa: E402 + FlightDeckLangChainCallbackHandler, +) + + +def test_langchain_callback_accumulates() -> None: + h = FlightDeckLangChainCallbackHandler( + agent_id="a", + release_id="r", + run_id="run_lc", + tenant_id="t", + task_id="k", + environment="local", + ) + llm_output = {"token_usage": {"prompt_tokens": 5, "completion_tokens": 2}, "model_name": "m1"} + resp = SimpleNamespace(generations=[], llm_output=llm_output) + h.on_llm_end(resp) + ev = h.to_run_event() + assert ev.usage.model.input_tokens == 5 + assert ev.usage.model.output_tokens == 2 + assert ev.usage.model.model == "m1" diff --git a/uv.lock b/uv.lock index 77d172c..1ce0ac9 100644 --- a/uv.lock +++ b/uv.lock @@ -60,6 +60,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", size = 114353, upload-time = "2026-03-24T12:59:08.246Z" }, ] +[[package]] +name = "attrs" +version = "26.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/8e/82a0fe20a541c03148528be8cac2408564a6c9a0cc7e9171802bc1d26985/attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32", size = 952055, upload-time = "2026-03-19T14:22:25.026Z" } +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 = "certifi" version = "2026.4.22" @@ -69,6 +78,39 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/22/30/7cd8fdcdfbc5b869528b079bfb76dcdf6056b1a2097a662e5e8c04f42965/certifi-2026.4.22-py3-none-any.whl", hash = "sha256:3cb2210c8f88ba2318d29b0388d1023c8492ff72ecdde4ebdaddbb13a31b1c4a", size = 135707, upload-time = "2026-04-22T11:26:09.372Z" }, ] +[[package]] +name = "cffi" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pycparser", marker = "implementation_name != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" }, + { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" }, + { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" }, + { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" }, + { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" }, + { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" }, + { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" }, + { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" }, + { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" }, + { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" }, + { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" }, + { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" }, + { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" }, + { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" }, + { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" }, + { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" }, + { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" }, + { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" }, + { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" }, + { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" }, + { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" }, + { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, +] + [[package]] name = "charset-normalizer" version = "3.4.7" @@ -131,6 +173,59 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, ] +[[package]] +name = "cryptography" +version = "47.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ef/b2/7ffa7fe8207a8c42147ffe70c3e360b228160c1d85dc3faff16aaa3244c0/cryptography-47.0.0.tar.gz", hash = "sha256:9f8e55fe4e63613a5e1cc5819030f27b97742d720203a087802ce4ce9ceb52bb", size = 830863, upload-time = "2026-04-24T19:54:57.056Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/98/40dfe932134bdcae4f6ab5927c87488754bf9eb79297d7e0070b78dd58e9/cryptography-47.0.0-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:160ad728f128972d362e714054f6ba0067cab7fb350c5202a9ae8ae4ce3ef1a0", size = 7912214, upload-time = "2026-04-24T19:53:03.864Z" }, + { url = "https://files.pythonhosted.org/packages/34/c6/2733531243fba725f58611b918056b277692f1033373dcc8bd01af1c05d4/cryptography-47.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b9a8943e359b7615db1a3ba587994618e094ff3d6fa5a390c73d079ce18b3973", size = 4644617, upload-time = "2026-04-24T19:53:06.909Z" }, + { url = "https://files.pythonhosted.org/packages/00/e3/b27be1a670a9b87f855d211cf0e1174a5d721216b7616bd52d8581d912ed/cryptography-47.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f5c15764f261394b22aef6b00252f5195f46f2ca300bec57149474e2538b31f8", size = 4668186, upload-time = "2026-04-24T19:53:09.053Z" }, + { url = "https://files.pythonhosted.org/packages/81/b9/8443cfe5d17d482d348cee7048acf502bb89a51b6382f06240fd290d4ca3/cryptography-47.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:9c59ab0e0fa3a180a5a9c59f3a5abe3ef90d474bc56d7fadfbe80359491b615b", size = 4651244, upload-time = "2026-04-24T19:53:11.217Z" }, + { url = "https://files.pythonhosted.org/packages/5d/5e/13ed0cdd0eb88ba159d6dd5ebfece8cb901dbcf1ae5ac4072e28b55d3153/cryptography-47.0.0-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:34b4358b925a5ea3e14384ca781a2c0ef7ac219b57bb9eacc4457078e2b19f92", size = 5252906, upload-time = "2026-04-24T19:53:13.532Z" }, + { url = "https://files.pythonhosted.org/packages/64/16/ed058e1df0f33d440217cd120d41d5dda9dd215a80b8187f68483185af82/cryptography-47.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:0024b87d47ae2399165a6bfb20d24888881eeab83ae2566d62467c5ff0030ce7", size = 4701842, upload-time = "2026-04-24T19:53:15.618Z" }, + { url = "https://files.pythonhosted.org/packages/02/e0/3d30986b30fdbd9e969abbdf8ba00ed0618615144341faeb57f395a084fe/cryptography-47.0.0-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:1e47422b5557bb82d3fff997e8d92cff4e28b9789576984f08c248d2b3535d93", size = 4289313, upload-time = "2026-04-24T19:53:17.755Z" }, + { url = "https://files.pythonhosted.org/packages/df/fd/32db38e3ad0cb331f0691cb4c7a8a6f176f679124dee746b3af6633db4d9/cryptography-47.0.0-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:6f29f36582e6151d9686235e586dd35bb67491f024767d10b842e520dc6a07ac", size = 4650964, upload-time = "2026-04-24T19:53:20.062Z" }, + { url = "https://files.pythonhosted.org/packages/86/53/5395d944dfd48cb1f67917f533c609c34347185ef15eb4308024c876f274/cryptography-47.0.0-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:a9b761f012a943b7de0e828843c5688d0de94a0578d44d6c85a1bae32f87791f", size = 5207817, upload-time = "2026-04-24T19:53:22.498Z" }, + { url = "https://files.pythonhosted.org/packages/34/4f/e5711b28e1901f7d480a2b1b688b645aa4c77c73f10731ed17e7f7db3f0d/cryptography-47.0.0-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:4e1de79e047e25d6e9f8cea71c86b4a53aced64134f0f003bbcbf3655fd172c8", size = 4701544, upload-time = "2026-04-24T19:53:24.356Z" }, + { url = "https://files.pythonhosted.org/packages/22/22/c8ddc25de3010fc8da447648f5a092c40e7a8fadf01dd6d255d9c0b9373d/cryptography-47.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ef6b3634087f18d2155b1e8ce264e5345a753da2c5fa9815e7d41315c90f8318", size = 4783536, upload-time = "2026-04-24T19:53:26.665Z" }, + { url = "https://files.pythonhosted.org/packages/66/b6/d4a68f4ea999c6d89e8498579cba1c5fcba4276284de7773b17e4fa69293/cryptography-47.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:11dbb9f50a0f1bb9757b3d8c27c1101780efb8f0bdecfb12439c22a74d64c001", size = 4926106, upload-time = "2026-04-24T19:53:28.686Z" }, + { url = "https://files.pythonhosted.org/packages/54/ed/5f524db1fade9c013aa618e1c99c6ed05e8ffc9ceee6cda22fed22dda3f4/cryptography-47.0.0-cp311-abi3-win32.whl", hash = "sha256:7fda2f02c9015db3f42bb8a22324a454516ed10a8c29ca6ece6cdbb5efe2a203", size = 3258581, upload-time = "2026-04-24T19:53:31.058Z" }, + { url = "https://files.pythonhosted.org/packages/b2/dc/1b901990b174786569029f67542b3edf72ac068b6c3c8683c17e6a2f5363/cryptography-47.0.0-cp311-abi3-win_amd64.whl", hash = "sha256:f5c3296dab66202f1b18a91fa266be93d6aa0c2806ea3d67762c69f60adc71aa", size = 3775309, upload-time = "2026-04-24T19:53:33.054Z" }, + { url = "https://files.pythonhosted.org/packages/14/88/7aa18ad9c11bc87689affa5ce4368d884b517502d75739d475fc6f4a03c7/cryptography-47.0.0-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:be12cb6a204f77ed968bcefe68086eb061695b540a3dd05edac507a3111b25f0", size = 7904299, upload-time = "2026-04-24T19:53:35.003Z" }, + { url = "https://files.pythonhosted.org/packages/07/55/c18f75724544872f234678fdedc871391722cb34a2aee19faa9f63100bb2/cryptography-47.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2ebd84adf0728c039a3be2700289378e1c164afc6748df1a5ed456767bef9ba7", size = 4631180, upload-time = "2026-04-24T19:53:37.517Z" }, + { url = "https://files.pythonhosted.org/packages/ee/65/31a5cc0eaca99cec5bafffe155d407115d96136bb161e8b49e0ef73f09a7/cryptography-47.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7f68d6fbc7fbbcfb0939fea72c3b96a9f9a6edfc0e1b1d29778a2066030418b1", size = 4653529, upload-time = "2026-04-24T19:53:39.775Z" }, + { url = "https://files.pythonhosted.org/packages/e5/bc/641c0519a495f3bfd0421b48d7cd325c4336578523ccd76ea322b6c29c7a/cryptography-47.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:6651d32eff255423503aa276739da98c30f26c40cbeffcc6048e0d54ef704c0c", size = 4638570, upload-time = "2026-04-24T19:53:42.129Z" }, + { url = "https://files.pythonhosted.org/packages/2b/f2/300327b0a47f6dc94dd8b71b57052aefe178bb51745073d73d80604f11ab/cryptography-47.0.0-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:3fb8fa48075fad7193f2e5496135c6a76ac4b2aa5a38433df0a539296b377829", size = 5238019, upload-time = "2026-04-24T19:53:44.577Z" }, + { url = "https://files.pythonhosted.org/packages/e9/5a/5b5cf994391d4bf9d9c7efd4c66aabe4d95227256627f8fea6cff7dfadbd/cryptography-47.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:11438c7518132d95f354fa01a4aa2f806d172a061a7bed18cf18cbdacdb204d7", size = 4686832, upload-time = "2026-04-24T19:53:47.015Z" }, + { url = "https://files.pythonhosted.org/packages/dc/2c/ae950e28fd6475c852fc21a44db3e6b5bcc1261d1e370f2b6e42fa800fef/cryptography-47.0.0-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:8c1a736bbb3288005796c3f7ccb9453360d7fed483b13b9f468aea5171432923", size = 4269301, upload-time = "2026-04-24T19:53:48.97Z" }, + { url = "https://files.pythonhosted.org/packages/67/fb/6a39782e150ffe5cc1b0018cb6ddc48bf7ca62b498d7539ffc8a758e977d/cryptography-47.0.0-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:f1557695e5c2b86e204f6ce9470497848634100787935ab7adc5397c54abd7ab", size = 4638110, upload-time = "2026-04-24T19:53:51.011Z" }, + { url = "https://files.pythonhosted.org/packages/8e/d7/0b3c71090a76e5c203164a47688b697635ece006dcd2499ab3a4dbd3f0bd/cryptography-47.0.0-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:f9a034b642b960767fb343766ae5ba6ad653f2e890ddd82955aef288ffea8736", size = 5194988, upload-time = "2026-04-24T19:53:52.962Z" }, + { url = "https://files.pythonhosted.org/packages/63/33/63a961498a9df51721ab578c5a2622661411fc520e00bd83b0cc64eb20c4/cryptography-47.0.0-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:b1c76fca783aa7698eb21eb14f9c4aa09452248ee54a627d125025a43f83e7a7", size = 4686563, upload-time = "2026-04-24T19:53:55.274Z" }, + { url = "https://files.pythonhosted.org/packages/b7/bf/5ee5b145248f92250de86145d1c1d6edebbd57a7fe7caa4dedb5d4cf06a1/cryptography-47.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:4f7722c97826770bab8ae92959a2e7b20a5e9e9bf4deae68fd86c3ca457bab52", size = 4770094, upload-time = "2026-04-24T19:53:57.753Z" }, + { url = "https://files.pythonhosted.org/packages/92/43/21d220b2da5d517773894dacdcdb5c682c28d3fffce65548cb06e87d5501/cryptography-47.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:09f6d7bf6724f8db8b32f11eccf23efc8e759924bc5603800335cf8859a3ddbd", size = 4913811, upload-time = "2026-04-24T19:54:00.236Z" }, + { url = "https://files.pythonhosted.org/packages/31/98/dc4ad376ac5f1a1a7d4a83f7b0c6f2bcad36b5d2d8f30aeb482d3a7d9582/cryptography-47.0.0-cp314-cp314t-win32.whl", hash = "sha256:6eebcaf0df1d21ce1f90605c9b432dd2c4f4ab665ac29a40d5e3fc68f51b5e63", size = 3237158, upload-time = "2026-04-24T19:54:02.606Z" }, + { url = "https://files.pythonhosted.org/packages/bc/da/97f62d18306b5133468bc3f8cc73a3111e8cdc8cf8d3e69474d6e5fd2d1b/cryptography-47.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:51c9313e90bd1690ec5a75ed047c27c0b8e6c570029712943d6116ef9a90620b", size = 3758706, upload-time = "2026-04-24T19:54:04.433Z" }, + { url = "https://files.pythonhosted.org/packages/e0/34/a4fae8ae7c3bc227460c9ae43f56abf1b911da0ec29e0ebac53bb0a4b6b7/cryptography-47.0.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:14432c8a9bcb37009784f9594a62fae211a2ae9543e96c92b2a8e4c3cd5cd0c4", size = 7904072, upload-time = "2026-04-24T19:54:06.411Z" }, + { url = "https://files.pythonhosted.org/packages/01/64/d7b1e54fdb69f22d24a64bb3e88dc718b31c7fb10ef0b9691a3cf7eeea6e/cryptography-47.0.0-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:07efe86201817e7d3c18781ca9770bc0db04e1e48c994be384e4602bc38f8f27", size = 4635767, upload-time = "2026-04-24T19:54:08.519Z" }, + { url = "https://files.pythonhosted.org/packages/8b/7b/cca826391fb2a94efdcdfe4631eb69306ee1cff0b22f664a412c90713877/cryptography-47.0.0-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2b45761c6ec22b7c726d6a829558777e32d0f1c8be7c3f3480f9c912d5ee8a10", size = 4654350, upload-time = "2026-04-24T19:54:10.795Z" }, + { url = "https://files.pythonhosted.org/packages/4c/65/4b57bcc823f42a991627c51c2f68c9fd6eb1393c1756aac876cba2accae2/cryptography-47.0.0-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:edd4da498015da5b9f26d38d3bfc2e90257bfa9cbed1f6767c282a0025ae649b", size = 4643394, upload-time = "2026-04-24T19:54:13.275Z" }, + { url = "https://files.pythonhosted.org/packages/f4/c4/2c5fbeea70adbbca2bbae865e1d605d6a4a7f8dbd9d33eaf69645087f06c/cryptography-47.0.0-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:9af828c0d5a65c70ec729cd7495a4bf1a67ecb66417b8f02ff125ab8a6326a74", size = 5225777, upload-time = "2026-04-24T19:54:15.18Z" }, + { url = "https://files.pythonhosted.org/packages/7e/b8/ac57107ef32749d2b244e36069bb688792a363aaaa3acc9e3cf84c130315/cryptography-47.0.0-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:256d07c78a04d6b276f5df935a9923275f53bd1522f214447fdf365494e2d515", size = 4688771, upload-time = "2026-04-24T19:54:17.835Z" }, + { url = "https://files.pythonhosted.org/packages/56/fc/9f1de22ff8be99d991f240a46863c52d475404c408886c5a38d2b5c3bb26/cryptography-47.0.0-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:5d0e362ff51041b0c0d219cc7d6924d7b8996f57ce5712bdcef71eb3c65a59cc", size = 4270753, upload-time = "2026-04-24T19:54:19.963Z" }, + { url = "https://files.pythonhosted.org/packages/00/68/d70c852797aa68e8e48d12e5a87170c43f67bb4a59403627259dd57d15de/cryptography-47.0.0-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:1581aef4219f7ca2849d0250edaa3866212fb74bf5667284f46aa92f9e65c1ca", size = 4642911, upload-time = "2026-04-24T19:54:21.818Z" }, + { url = "https://files.pythonhosted.org/packages/a5/51/661cbee74f594c5d97ff82d34f10d5551c085ca4668645f4606ebd22bd5d/cryptography-47.0.0-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:a49a3eb5341b9503fa3000a9a0db033161db90d47285291f53c2a9d2cd1b7f76", size = 5181411, upload-time = "2026-04-24T19:54:24.376Z" }, + { url = "https://files.pythonhosted.org/packages/94/87/f2b6c374a82cf076cfa1416992ac8e8ec94d79facc37aec87c1a5cb72352/cryptography-47.0.0-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:2207a498b03275d0051589e326b79d4cf59985c99031b05bb292ac52631c37fe", size = 4688262, upload-time = "2026-04-24T19:54:26.946Z" }, + { url = "https://files.pythonhosted.org/packages/14/e2/8b7462f4acf21ec509616f0245018bb197194ab0b65c2ea21a0bdd53c0eb/cryptography-47.0.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:7a02675e2fabd0c0fc04c868b8781863cbf1967691543c22f5470500ff840b31", size = 4775506, upload-time = "2026-04-24T19:54:28.926Z" }, + { url = "https://files.pythonhosted.org/packages/70/75/158e494e4c08dc05e039da5bb48553826bd26c23930cf8d3cd5f21fa8921/cryptography-47.0.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:80887c5cbd1774683cb126f0ab4184567f080071d5acf62205acb354b4b753b7", size = 4912060, upload-time = "2026-04-24T19:54:30.869Z" }, + { url = "https://files.pythonhosted.org/packages/06/bd/0a9d3edbf5eadbac926d7b9b3cd0c4be584eeeae4a003d24d9eda4affbbd/cryptography-47.0.0-cp38-abi3-win32.whl", hash = "sha256:ed67ea4e0cfb5faa5bc7ecb6e2b8838f3807a03758eec239d6c21c8769355310", size = 3248487, upload-time = "2026-04-24T19:54:33.494Z" }, + { url = "https://files.pythonhosted.org/packages/60/80/5681af756d0da3a599b7bdb586fac5a1540f1bcefd2717a20e611ddade45/cryptography-47.0.0-cp38-abi3-win_amd64.whl", hash = "sha256:835d2d7f47cdc53b3224e90810fb1d36ca94ea29cc1801fb4c1bc43876735769", size = 3755737, upload-time = "2026-04-24T19:54:35.408Z" }, +] + [[package]] name = "distro" version = "1.9.0" @@ -196,6 +291,20 @@ dev = [ { name = "pytest" }, { name = "ruff" }, ] +integrations-ci = [ + { name = "langchain-core" }, + { name = "openai-agents" }, + { name = "temporalio" }, +] +integrations-langchain = [ + { name = "langchain-core" }, +] +integrations-openai-agents = [ + { name = "openai-agents" }, +] +integrations-temporal = [ + { name = "temporalio" }, +] openai = [ { name = "openai" }, ] @@ -216,8 +325,12 @@ requires-dist = [ { name = "click", specifier = ">=8.0" }, { name = "fastapi", specifier = ">=0.100.0" }, { name = "httpx", specifier = ">=0.24.0" }, + { name = "langchain-core", marker = "extra == 'integrations-ci'", specifier = ">=0.3.0" }, + { name = "langchain-core", marker = "extra == 'integrations-langchain'", specifier = ">=0.3.0" }, { name = "openai", marker = "extra == 'all'", specifier = ">=1.0" }, { name = "openai", marker = "extra == 'openai'", specifier = ">=1.0" }, + { name = "openai-agents", marker = "extra == 'integrations-ci'", specifier = ">=0.14.0" }, + { name = "openai-agents", marker = "extra == 'integrations-openai-agents'", specifier = ">=0.14.0" }, { name = "opentelemetry-api", marker = "extra == 'all'", specifier = ">=1.20.0" }, { name = "opentelemetry-api", marker = "extra == 'telemetry'", specifier = ">=1.20.0" }, { name = "opentelemetry-exporter-otlp", marker = "extra == 'all'", specifier = ">=1.20.0" }, @@ -231,9 +344,11 @@ requires-dist = [ { name = "rich", specifier = ">=13.0" }, { name = "ruff", marker = "extra == 'dev'", specifier = "==0.15.12" }, { name = "sqlalchemy", specifier = ">=2.0" }, + { name = "temporalio", marker = "extra == 'integrations-ci'", specifier = ">=1.8.0" }, + { name = "temporalio", marker = "extra == 'integrations-temporal'", specifier = ">=1.8.0" }, { name = "uvicorn", extras = ["standard"], specifier = ">=0.20.0" }, ] -provides-extras = ["openai", "anthropic", "telemetry", "all", "dev", "postgres"] +provides-extras = ["openai", "anthropic", "telemetry", "all", "dev", "postgres", "integrations-langchain", "integrations-temporal", "integrations-openai-agents", "integrations-ci"] [[package]] name = "googleapis-common-protos" @@ -270,6 +385,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/15/32/77ee8a6c1564fc345a491a4e85b3bf360e4cf26eac98c4532d2fdb96e01f/greenlet-3.5.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d60097128cb0a1cab9ea541186ea13cd7b847b8449a7787c2e2350da0cb82d86", size = 245324, upload-time = "2026-04-27T12:24:40.295Z" }, ] +[[package]] +name = "griffelib" +version = "2.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9d/82/74f4a3310cdabfbb10da554c3a672847f1ed33c6f61dd472681ce7f1fe67/griffelib-2.0.2.tar.gz", hash = "sha256:3cf20b3bc470e83763ffbf236e0076b1211bac1bc67de13daf494640f2de707e", size = 166461, upload-time = "2026-03-27T11:34:51.091Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/8c/c9138d881c79aa0ea9ed83cbd58d5ca75624378b38cee225dcf5c42cc91f/griffelib-2.0.2-py3-none-any.whl", hash = "sha256:925c857658fb1ba40c0772c37acbc2ab650bd794d9c1b9726922e36ea4117ea1", size = 142357, upload-time = "2026-03-27T11:34:46.275Z" }, +] + [[package]] name = "grpcio" version = "1.80.0" @@ -343,6 +467,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, ] +[[package]] +name = "httpx-sse" +version = "0.4.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0f/4c/751061ffa58615a32c31b2d82e8482be8dd4a89154f003147acee90f2be9/httpx_sse-0.4.3.tar.gz", hash = "sha256:9b1ed0127459a66014aec3c56bebd93da3c1bc8bb6618c8082039a44889a755d", size = 15943, upload-time = "2025-10-10T21:48:22.271Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/fd/6668e5aec43ab844de6fc74927e155a3b37bf40d7c3790e49fc0406b6578/httpx_sse-0.4.3-py3-none-any.whl", hash = "sha256:0ac1c9fe3c0afad2e0ebb25a934a59f4c7823b60792691f779fad2c5568830fc", size = 8960, upload-time = "2025-10-10T21:48:21.158Z" }, +] + [[package]] name = "idna" version = "3.13" @@ -408,6 +541,106 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/dc/2e/a44c20c58aeed0355f2d326969a181696aeb551a25195f47563908a815be/jiter-0.14.0-cp314-cp314t-win_arm64.whl", hash = "sha256:5419d4aa2024961da9fe12a9cfe7484996735dca99e8e090b5c88595ef1951ff", size = 191338, upload-time = "2026-04-10T14:28:02.853Z" }, ] +[[package]] +name = "jsonpatch" +version = "1.33" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jsonpointer" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/42/78/18813351fe5d63acad16aec57f94ec2b70a09e53ca98145589e185423873/jsonpatch-1.33.tar.gz", hash = "sha256:9fcd4009c41e6d12348b4a0ff2563ba56a2923a7dfee731d004e212e1ee5030c", size = 21699, upload-time = "2023-06-26T12:07:29.144Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/73/07/02e16ed01e04a374e644b575638ec7987ae846d25ad97bcc9945a3ee4b0e/jsonpatch-1.33-py2.py3-none-any.whl", hash = "sha256:0ae28c0cd062bbd8b8ecc26d7d164fbbea9652a1a3693f3b956c1eae5145dade", size = 12898, upload-time = "2023-06-16T21:01:28.466Z" }, +] + +[[package]] +name = "jsonpointer" +version = "3.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/18/c7/af399a2e7a67fd18d63c40c5e62d3af4e67b836a2107468b6a5ea24c4304/jsonpointer-3.1.1.tar.gz", hash = "sha256:0b801c7db33a904024f6004d526dcc53bbb8a4a0f4e32bfd10beadf60adf1900", size = 9068, upload-time = "2026-03-23T22:32:32.458Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/6a/a83720e953b1682d2d109d3c2dbb0bc9bf28cc1cbc205be4ef4be5da709d/jsonpointer-3.1.1-py3-none-any.whl", hash = "sha256:8ff8b95779d071ba472cf5bc913028df06031797532f08a7d5b602d8b2a488ca", size = 7659, upload-time = "2026-03-23T22:32:31.568Z" }, +] + +[[package]] +name = "jsonschema" +version = "4.26.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "jsonschema-specifications" }, + { name = "referencing" }, + { name = "rpds-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583, upload-time = "2026-01-07T13:41:07.246Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630, upload-time = "2026-01-07T13:41:05.306Z" }, +] + +[[package]] +name = "jsonschema-specifications" +version = "2025.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "referencing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" }, +] + +[[package]] +name = "langchain-core" +version = "1.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jsonpatch" }, + { name = "langchain-protocol" }, + { name = "langsmith" }, + { name = "packaging" }, + { name = "pydantic" }, + { name = "pyyaml" }, + { name = "tenacity" }, + { name = "typing-extensions" }, + { name = "uuid-utils" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a8/03/7219502e8ca728d65eb44d7a3eb60239230742a70dbfc9241b9bfd61c4ab/langchain_core-1.3.2.tar.gz", hash = "sha256:fd7a50b2f28ba561fd9d7f5d2760bc9e06cf00cdf820a3ccafe88a94ffa8d5b7", size = 911813, upload-time = "2026-04-24T15:49:23.699Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7d/d5/8fa4431007cbb7cfed7590f4d6a5dea3ad724f4174d248f6642ef5ce7d05/langchain_core-1.3.2-py3-none-any.whl", hash = "sha256:d44a66127f9f8db735bdfd0ab9661bccb47a97113cfd3f2d89c74864422b7274", size = 542390, upload-time = "2026-04-24T15:49:21.991Z" }, +] + +[[package]] +name = "langchain-protocol" +version = "0.0.15" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4f/24/9777489d6fbbee64af0c8f96d4f840239c408cf694f3394672807dafc490/langchain_protocol-0.0.15.tar.gz", hash = "sha256:9ab2d11ee73944754f10e037e717098d3a6796f0e58afa9cadda6154e7655ade", size = 5862, upload-time = "2026-05-01T22:30:04.748Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1d/7a/9c97a7b9cbe4c5dc6a44cdb1545450c28f0c8ce89b9c1f0ee7fbad896263/langchain_protocol-0.0.15-py3-none-any.whl", hash = "sha256:461eb794358f83d5e42635a5797799ffec7b4702314e34edf73ac21e75d3ef79", size = 6982, upload-time = "2026-05-01T22:30:03.877Z" }, +] + +[[package]] +name = "langsmith" +version = "0.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "httpx" }, + { name = "orjson", marker = "platform_python_implementation != 'PyPy'" }, + { name = "packaging" }, + { name = "pydantic" }, + { name = "requests" }, + { name = "requests-toolbelt" }, + { name = "uuid-utils" }, + { name = "xxhash" }, + { name = "zstandard" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a8/64/95f1f013531395f4e8ed73caeee780f65c7c58fe028cb543f8937b45611b/langsmith-0.8.0.tar.gz", hash = "sha256:59fe5b2a56bbbe14a08aa76691f84b49e8675dd21e11b57d80c6db8c08bac2e3", size = 4432996, upload-time = "2026-04-30T22:13:07.341Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f3/e1/a4be2e696c9473bb53298df398237da5674704d781d4b748ed35aeef592a/langsmith-0.8.0-py3-none-any.whl", hash = "sha256:12cc4bc5622b835a6d841964d6034df3617bdb912dae0c1381fd0a68a9b3a3ef", size = 393268, upload-time = "2026-04-30T22:13:05.56Z" }, +] + [[package]] name = "markdown-it-py" version = "4.0.0" @@ -420,6 +653,31 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" }, ] +[[package]] +name = "mcp" +version = "1.27.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "httpx" }, + { name = "httpx-sse" }, + { name = "jsonschema" }, + { name = "pydantic" }, + { name = "pydantic-settings" }, + { name = "pyjwt", extra = ["crypto"] }, + { name = "python-multipart" }, + { name = "pywin32", marker = "sys_platform == 'win32'" }, + { name = "sse-starlette" }, + { name = "starlette" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, + { name = "uvicorn", marker = "sys_platform != 'emscripten'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8b/eb/c0cfc62075dc6e1ec1c64d352ae09ac051d9334311ed226f1f425312848a/mcp-1.27.0.tar.gz", hash = "sha256:d3dc35a7eec0d458c1da4976a48f982097ddaab87e278c5511d5a4a56e852b83", size = 607509, upload-time = "2026-04-02T14:48:08.88Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9c/46/f6b4ad632c67ef35209a66127e4bddc95759649dd595f71f13fba11bdf9a/mcp-1.27.0-py3-none-any.whl", hash = "sha256:5ce1fa81614958e267b21fb2aa34e0aea8e2c6ede60d52aba45fd47246b4d741", size = 215967, upload-time = "2026-04-02T14:48:07.24Z" }, +] + [[package]] name = "mdurl" version = "0.1.2" @@ -429,6 +687,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, ] +[[package]] +name = "nexus-rpc" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/35/d5/cd1ffb202b76ebc1b33c1332a3416e55a39929006982adc2b1eb069aaa9b/nexus_rpc-1.4.0.tar.gz", hash = "sha256:3b8b373d4865671789cc43623e3dc0bcbf192562e40e13727e17f1c149050fba", size = 82367, upload-time = "2026-02-25T22:01:34.053Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/52/6327a5f4fda01207205038a106a99848a41c83e933cd23ea2cab3d2ebc6c/nexus_rpc-1.4.0-py3-none-any.whl", hash = "sha256:14c953d3519113f8ccec533a9efdb6b10c28afef75d11cdd6d422640c40b3a49", size = 29645, upload-time = "2026-02-25T22:01:33.122Z" }, +] + [[package]] name = "openai" version = "2.33.0" @@ -448,6 +718,25 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7d/32/37734d769bc8b42e4938785313cc05aade6cb0fa72479d3220a0d61a4e78/openai-2.33.0-py3-none-any.whl", hash = "sha256:03ac37d70e8c9e3a8124214e3afa785e2cbc12e627fbd98177a086ef2fd87ad5", size = 1162695, upload-time = "2026-04-28T14:04:40.482Z" }, ] +[[package]] +name = "openai-agents" +version = "0.15.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "griffelib" }, + { name = "mcp" }, + { name = "openai" }, + { name = "pydantic" }, + { name = "requests" }, + { name = "types-requests" }, + { name = "typing-extensions" }, + { name = "websockets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1d/52/125891e56b67ec78bef08d91dc0a8d39457088cd0f59bf8e74a37e5e591c/openai_agents-0.15.1.tar.gz", hash = "sha256:78c3f1226e1d6d34dd7566e211c8345e996629d1704335153d1728995a3a7775", size = 5319915, upload-time = "2026-05-02T02:20:53.631Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/7b/69a33975b3610300219e1d25185c46280814cdb87e69a779acf0c9b9166a/openai_agents-0.15.1-py3-none-any.whl", hash = "sha256:2d304a5dcb919bc4fa1de5c7c9c93a4353a8a79d87d582d6170929390b7b3cef", size = 818627, upload-time = "2026-05-02T02:20:50.932Z" }, +] + [[package]] name = "opentelemetry-api" version = "1.41.1" @@ -561,6 +850,29 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/eb/a6/83dc2ab6fa397ee66fba04fe2e74bdf7be3b3870005359ceb7689103c058/opentelemetry_semantic_conventions-0.62b1-py3-none-any.whl", hash = "sha256:cf506938103d331fbb78eded0d9788095f7fd59016f2bda813c3324e5a74a93c", size = 231620, upload-time = "2026-04-24T13:15:35.454Z" }, ] +[[package]] +name = "orjson" +version = "3.11.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9d/1b/2024d06792d0779f9dbc51531b61c24f76c75b9f4ce05e6f3377a1814cea/orjson-3.11.8.tar.gz", hash = "sha256:96163d9cdc5a202703e9ad1b9ae757d5f0ca62f4fa0cc93d1f27b0e180cc404e", size = 5603832, upload-time = "2026-03-31T16:16:27.878Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6d/35/b01910c3d6b85dc882442afe5060cbf719c7d1fc85749294beda23d17873/orjson-3.11.8-cp314-cp314-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:ec795530a73c269a55130498842aaa762e4a939f6ce481a7e986eeaa790e9da4", size = 229171, upload-time = "2026-03-31T16:16:00.651Z" }, + { url = "https://files.pythonhosted.org/packages/c2/56/c9ec97bd11240abef39b9e5d99a15462809c45f677420fd148a6c5e6295e/orjson-3.11.8-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:c492a0e011c0f9066e9ceaa896fbc5b068c54d365fea5f3444b697ee01bc8625", size = 128746, upload-time = "2026-03-31T16:16:02.673Z" }, + { url = "https://files.pythonhosted.org/packages/3b/e4/66d4f30a90de45e2f0cbd9623588e8ae71eef7679dbe2ae954ed6d66a41f/orjson-3.11.8-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:883206d55b1bd5f5679ad5e6ddd3d1a5e3cac5190482927fdb8c78fb699193b5", size = 131867, upload-time = "2026-03-31T16:16:04.342Z" }, + { url = "https://files.pythonhosted.org/packages/19/30/2a645fc9286b928675e43fa2a3a16fb7b6764aa78cc719dc82141e00f30b/orjson-3.11.8-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5774c1fdcc98b2259800b683b19599c133baeb11d60033e2095fd9d4667b82db", size = 124664, upload-time = "2026-03-31T16:16:05.837Z" }, + { url = "https://files.pythonhosted.org/packages/db/44/77b9a86d84a28d52ba3316d77737f6514e17118119ade3f91b639e859029/orjson-3.11.8-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ac7381c83dd3d4a6347e6635950aa448f54e7b8406a27c7ecb4a37e9f1ae08b", size = 129701, upload-time = "2026-03-31T16:16:07.407Z" }, + { url = "https://files.pythonhosted.org/packages/b3/ea/eff3d9bfe47e9bc6969c9181c58d9f71237f923f9c86a2d2f490cd898c82/orjson-3.11.8-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:14439063aebcb92401c11afc68ee4e407258d2752e62d748b6942dad20d2a70d", size = 141202, upload-time = "2026-03-31T16:16:09.48Z" }, + { url = "https://files.pythonhosted.org/packages/52/c8/90d4b4c60c84d62068d0cf9e4d8f0a4e05e76971d133ac0c60d818d4db20/orjson-3.11.8-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fa72e71977bff96567b0f500fc5bfd2fdf915f34052c782a4c6ebbdaa97aa858", size = 127194, upload-time = "2026-03-31T16:16:11.02Z" }, + { url = "https://files.pythonhosted.org/packages/8d/c7/ea9e08d1f0ba981adffb629811148b44774d935171e7b3d780ae43c4c254/orjson-3.11.8-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7679bc2f01bb0d219758f1a5f87bb7c8a81c0a186824a393b366876b4948e14f", size = 133639, upload-time = "2026-03-31T16:16:13.434Z" }, + { url = "https://files.pythonhosted.org/packages/6c/8c/ddbbfd6ba59453c8fc7fe1d0e5983895864e264c37481b2a791db635f046/orjson-3.11.8-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:14f7b8fcb35ef403b42fa5ecfa4ed032332a91f3dc7368fbce4184d59e1eae0d", size = 141914, upload-time = "2026-03-31T16:16:14.955Z" }, + { url = "https://files.pythonhosted.org/packages/4e/31/dbfbefec9df060d34ef4962cd0afcb6fa7a9ec65884cb78f04a7859526c3/orjson-3.11.8-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:c2bdf7b2facc80b5e34f48a2d557727d5c5c57a8a450de122ae81fa26a81c1bc", size = 423800, upload-time = "2026-03-31T16:16:16.594Z" }, + { url = "https://files.pythonhosted.org/packages/87/cf/f74e9ae9803d4ab46b163494adba636c6d7ea955af5cc23b8aaa94cfd528/orjson-3.11.8-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ccd7ba1b0605813a0715171d39ec4c314cb97a9c85893c2c5c0c3a3729df38bf", size = 147837, upload-time = "2026-03-31T16:16:18.585Z" }, + { url = "https://files.pythonhosted.org/packages/64/e6/9214f017b5db85e84e68602792f742e5dc5249e963503d1b356bee611e01/orjson-3.11.8-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:cdbc8c9c02463fef4d3c53a9ba3336d05496ec8e1f1c53326a1e4acc11f5c600", size = 136441, upload-time = "2026-03-31T16:16:20.151Z" }, + { url = "https://files.pythonhosted.org/packages/24/dd/3590348818f58f837a75fb969b04cdf187ae197e14d60b5e5a794a38b79d/orjson-3.11.8-cp314-cp314-win32.whl", hash = "sha256:0b57f67710a8cd459e4e54eb96d5f77f3624eba0c661ba19a525807e42eccade", size = 131983, upload-time = "2026-03-31T16:16:21.823Z" }, + { url = "https://files.pythonhosted.org/packages/3f/0f/b6cb692116e05d058f31ceee819c70f097fa9167c82f67fabe7516289abc/orjson-3.11.8-cp314-cp314-win_amd64.whl", hash = "sha256:735e2262363dcbe05c35e3a8869898022af78f89dde9e256924dc02e99fe69ca", size = 127396, upload-time = "2026-03-31T16:16:23.685Z" }, + { url = "https://files.pythonhosted.org/packages/c0/d1/facb5b5051fabb0ef9d26c6544d87ef19a939a9a001198655d0d891062dd/orjson-3.11.8-cp314-cp314-win_arm64.whl", hash = "sha256:6ccdea2c213cf9f3d9490cbd5d427693c870753df41e6cb375bd79bcbafc8817", size = 127330, upload-time = "2026-03-31T16:16:25.496Z" }, +] + [[package]] name = "packaging" version = "26.2" @@ -629,6 +941,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/eb/e6/5fff07a70d1f945ed90ae131c3bd76cab32beff7c58c6db15ad5820b6d1f/psycopg_binary-3.3.4-cp314-cp314-win_amd64.whl", hash = "sha256:c37e024c07308cd06cf3ec51bfd0e7f6157585a4d84d1bce4a7f5f7913719bf8", size = 3666849, upload-time = "2026-05-01T23:31:51.165Z" }, ] +[[package]] +name = "pycparser" +version = "3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, +] + [[package]] name = "pydantic" version = "2.13.3" @@ -685,6 +1006,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0b/db/d8182a7f1d9343a032265aae186eb063fe26ca4c40f256b21e8da4498e89/pydantic_core-2.46.3-cp314-cp314t-win_arm64.whl", hash = "sha256:77706aeb41df6a76568434701e0917da10692da28cb69d5fb6919ce5fdb07374", size = 2026310, upload-time = "2026-04-20T14:41:01.778Z" }, ] +[[package]] +name = "pydantic-settings" +version = "2.14.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, + { name = "python-dotenv" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/42/98/c8345dccdc31de4228c039a98f6467a941e39558da41c1744fbe29fa5666/pydantic_settings-2.14.0.tar.gz", hash = "sha256:24285fd4b0e0c06507dd9fdfd331ee23794305352aaec8fc4eb92d4047aeb67d", size = 235709, upload-time = "2026-04-20T13:37:40.293Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/01/dd/bebff3040138f00ae8a102d426b27349b9a49acc310fcae7f92112d867e3/pydantic_settings-2.14.0-py3-none-any.whl", hash = "sha256:fc8d5d692eb7092e43c8647c1c35a3ecd00e040fcf02ed86f4cb5458ca62182e", size = 60940, upload-time = "2026-04-20T13:37:38.586Z" }, +] + [[package]] name = "pygments" version = "2.20.0" @@ -694,6 +1029,20 @@ 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 = "pyjwt" +version = "2.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c2/27/a3b6e5bf6ff856d2509292e95c8f57f0df7017cf5394921fc4e4ef40308a/pyjwt-2.12.1.tar.gz", hash = "sha256:c74a7a2adf861c04d002db713dd85f84beb242228e671280bf709d765b03672b", size = 102564, upload-time = "2026-03-13T19:27:37.25Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/7a/8dd906bd22e79e47397a61742927f6747fe93242ef86645ee9092e610244/pyjwt-2.12.1-py3-none-any.whl", hash = "sha256:28ca37c070cad8ba8cd9790cd940535d40274d22f80ab87f3ac6a713e6e8454c", size = 29726, upload-time = "2026-03-13T19:27:35.677Z" }, +] + +[package.optional-dependencies] +crypto = [ + { name = "cryptography" }, +] + [[package]] name = "pytest" version = "9.0.3" @@ -719,6 +1068,25 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0b/d7/1959b9648791274998a9c3526f6d0ec8fd2233e4d4acce81bbae76b44b2a/python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", size = 22101, upload-time = "2026-03-01T16:00:25.09Z" }, ] +[[package]] +name = "python-multipart" +version = "0.0.27" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/69/9b/f23807317a113dc36e74e75eb265a02dd1a4d9082abc3c1064acd22997c4/python_multipart-0.0.27.tar.gz", hash = "sha256:9870a6a8c5a20a5bf4f07c017bd1489006ff8836cff097b6933355ee2b49b602", size = 44043, upload-time = "2026-04-27T10:51:26.649Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/99/78/4126abcbdbd3c559d43e0db7f7b9173fc6befe45d39a2856cc0b8ec2a5a6/python_multipart-0.0.27-py3-none-any.whl", hash = "sha256:6fccfad17a27334bd0193681b369f476eda3409f17381a2d65aa7df3f7275645", size = 29254, upload-time = "2026-04-27T10:51:24.997Z" }, +] + +[[package]] +name = "pywin32" +version = "311" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c9/31/097f2e132c4f16d99a22bfb777e0fd88bd8e1c634304e102f313af69ace5/pywin32-311-cp314-cp314-win32.whl", hash = "sha256:b7a2c10b93f8986666d0c803ee19b5990885872a7de910fc460f9b0c2fbf92ee", size = 8840714, upload-time = "2025-07-14T20:13:32.449Z" }, + { url = "https://files.pythonhosted.org/packages/90/4b/07c77d8ba0e01349358082713400435347df8426208171ce297da32c313d/pywin32-311-cp314-cp314-win_amd64.whl", hash = "sha256:3aca44c046bd2ed8c90de9cb8427f581c479e594e99b5c0bb19b29c10fd6cb87", size = 9656800, upload-time = "2025-07-14T20:13:34.312Z" }, + { url = "https://files.pythonhosted.org/packages/c0/d2/21af5c535501a7233e734b8af901574572da66fcc254cb35d0609c9080dd/pywin32-311-cp314-cp314-win_arm64.whl", hash = "sha256:a508e2d9025764a8270f93111a970e1d0fbfc33f4153b388bb649b7eec4f9b42", size = 8932540, upload-time = "2025-07-14T20:13:36.379Z" }, +] + [[package]] name = "pyyaml" version = "6.0.3" @@ -745,6 +1113,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, ] +[[package]] +name = "referencing" +version = "0.37.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "rpds-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036, upload-time = "2025-10-13T15:30:48.871Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766, upload-time = "2025-10-13T15:30:47.625Z" }, +] + [[package]] name = "requests" version = "2.33.1" @@ -760,6 +1141,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d7/8e/7540e8a2036f79a125c1d2ebadf69ed7901608859186c856fa0388ef4197/requests-2.33.1-py3-none-any.whl", hash = "sha256:4e6d1ef462f3626a1f0a0a9c42dd93c63bad33f9f1c1937509b8c5c8718ab56a", size = 64947, upload-time = "2026-03-30T16:09:13.83Z" }, ] +[[package]] +name = "requests-toolbelt" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f3/61/d7545dafb7ac2230c70d38d31cbfe4cc64f7144dc41f6e4e4b78ecd9f5bb/requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6", size = 206888, upload-time = "2023-05-01T04:11:33.229Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/51/d4db610ef29373b879047326cbf6fa98b6c1969d6f6dc423279de2b1be2c/requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06", size = 54481, upload-time = "2023-05-01T04:11:28.427Z" }, +] + [[package]] name = "rich" version = "15.0.0" @@ -773,6 +1166,43 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/82/3b/64d4899d73f91ba49a8c18a8ff3f0ea8f1c1d75481760df8c68ef5235bf5/rich-15.0.0-py3-none-any.whl", hash = "sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb", size = 310654, upload-time = "2026-04-12T08:24:02.83Z" }, ] +[[package]] +name = "rpds-py" +version = "0.30.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469, upload-time = "2025-11-30T20:24:38.837Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/86/81/dad16382ebbd3d0e0328776d8fd7ca94220e4fa0798d1dc5e7da48cb3201/rpds_py-0.30.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0", size = 362099, upload-time = "2025-11-30T20:23:27.316Z" }, + { url = "https://files.pythonhosted.org/packages/2b/60/19f7884db5d5603edf3c6bce35408f45ad3e97e10007df0e17dd57af18f8/rpds_py-0.30.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be", size = 353192, upload-time = "2025-11-30T20:23:29.151Z" }, + { url = "https://files.pythonhosted.org/packages/bf/c4/76eb0e1e72d1a9c4703c69607cec123c29028bff28ce41588792417098ac/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f", size = 384080, upload-time = "2025-11-30T20:23:30.785Z" }, + { url = "https://files.pythonhosted.org/packages/72/87/87ea665e92f3298d1b26d78814721dc39ed8d2c74b86e83348d6b48a6f31/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f", size = 394841, upload-time = "2025-11-30T20:23:32.209Z" }, + { url = "https://files.pythonhosted.org/packages/77/ad/7783a89ca0587c15dcbf139b4a8364a872a25f861bdb88ed99f9b0dec985/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87", size = 516670, upload-time = "2025-11-30T20:23:33.742Z" }, + { url = "https://files.pythonhosted.org/packages/5b/3c/2882bdac942bd2172f3da574eab16f309ae10a3925644e969536553cb4ee/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18", size = 408005, upload-time = "2025-11-30T20:23:35.253Z" }, + { url = "https://files.pythonhosted.org/packages/ce/81/9a91c0111ce1758c92516a3e44776920b579d9a7c09b2b06b642d4de3f0f/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad", size = 382112, upload-time = "2025-11-30T20:23:36.842Z" }, + { url = "https://files.pythonhosted.org/packages/cf/8e/1da49d4a107027e5fbc64daeab96a0706361a2918da10cb41769244b805d/rpds_py-0.30.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07", size = 399049, upload-time = "2025-11-30T20:23:38.343Z" }, + { url = "https://files.pythonhosted.org/packages/df/5a/7ee239b1aa48a127570ec03becbb29c9d5a9eb092febbd1699d567cae859/rpds_py-0.30.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f", size = 415661, upload-time = "2025-11-30T20:23:40.263Z" }, + { url = "https://files.pythonhosted.org/packages/70/ea/caa143cf6b772f823bc7929a45da1fa83569ee49b11d18d0ada7f5ee6fd6/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65", size = 565606, upload-time = "2025-11-30T20:23:42.186Z" }, + { url = "https://files.pythonhosted.org/packages/64/91/ac20ba2d69303f961ad8cf55bf7dbdb4763f627291ba3d0d7d67333cced9/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f", size = 591126, upload-time = "2025-11-30T20:23:44.086Z" }, + { url = "https://files.pythonhosted.org/packages/21/20/7ff5f3c8b00c8a95f75985128c26ba44503fb35b8e0259d812766ea966c7/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53", size = 553371, upload-time = "2025-11-30T20:23:46.004Z" }, + { url = "https://files.pythonhosted.org/packages/72/c7/81dadd7b27c8ee391c132a6b192111ca58d866577ce2d9b0ca157552cce0/rpds_py-0.30.0-cp314-cp314-win32.whl", hash = "sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed", size = 215298, upload-time = "2025-11-30T20:23:47.696Z" }, + { url = "https://files.pythonhosted.org/packages/3e/d2/1aaac33287e8cfb07aab2e6b8ac1deca62f6f65411344f1433c55e6f3eb8/rpds_py-0.30.0-cp314-cp314-win_amd64.whl", hash = "sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950", size = 228604, upload-time = "2025-11-30T20:23:49.501Z" }, + { url = "https://files.pythonhosted.org/packages/e8/95/ab005315818cc519ad074cb7784dae60d939163108bd2b394e60dc7b5461/rpds_py-0.30.0-cp314-cp314-win_arm64.whl", hash = "sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6", size = 222391, upload-time = "2025-11-30T20:23:50.96Z" }, + { url = "https://files.pythonhosted.org/packages/9e/68/154fe0194d83b973cdedcdcc88947a2752411165930182ae41d983dcefa6/rpds_py-0.30.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb", size = 364868, upload-time = "2025-11-30T20:23:52.494Z" }, + { url = "https://files.pythonhosted.org/packages/83/69/8bbc8b07ec854d92a8b75668c24d2abcb1719ebf890f5604c61c9369a16f/rpds_py-0.30.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8", size = 353747, upload-time = "2025-11-30T20:23:54.036Z" }, + { url = "https://files.pythonhosted.org/packages/ab/00/ba2e50183dbd9abcce9497fa5149c62b4ff3e22d338a30d690f9af970561/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7", size = 383795, upload-time = "2025-11-30T20:23:55.556Z" }, + { url = "https://files.pythonhosted.org/packages/05/6f/86f0272b84926bcb0e4c972262f54223e8ecc556b3224d281e6598fc9268/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898", size = 393330, upload-time = "2025-11-30T20:23:57.033Z" }, + { url = "https://files.pythonhosted.org/packages/cb/e9/0e02bb2e6dc63d212641da45df2b0bf29699d01715913e0d0f017ee29438/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e", size = 518194, upload-time = "2025-11-30T20:23:58.637Z" }, + { url = "https://files.pythonhosted.org/packages/ee/ca/be7bca14cf21513bdf9c0606aba17d1f389ea2b6987035eb4f62bd923f25/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419", size = 408340, upload-time = "2025-11-30T20:24:00.2Z" }, + { url = "https://files.pythonhosted.org/packages/c2/c7/736e00ebf39ed81d75544c0da6ef7b0998f8201b369acf842f9a90dc8fce/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551", size = 383765, upload-time = "2025-11-30T20:24:01.759Z" }, + { url = "https://files.pythonhosted.org/packages/4a/3f/da50dfde9956aaf365c4adc9533b100008ed31aea635f2b8d7b627e25b49/rpds_py-0.30.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8", size = 396834, upload-time = "2025-11-30T20:24:03.687Z" }, + { url = "https://files.pythonhosted.org/packages/4e/00/34bcc2565b6020eab2623349efbdec810676ad571995911f1abdae62a3a0/rpds_py-0.30.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5", size = 415470, upload-time = "2025-11-30T20:24:05.232Z" }, + { url = "https://files.pythonhosted.org/packages/8c/28/882e72b5b3e6f718d5453bd4d0d9cf8df36fddeb4ddbbab17869d5868616/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404", size = 565630, upload-time = "2025-11-30T20:24:06.878Z" }, + { url = "https://files.pythonhosted.org/packages/3b/97/04a65539c17692de5b85c6e293520fd01317fd878ea1995f0367d4532fb1/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856", size = 591148, upload-time = "2025-11-30T20:24:08.445Z" }, + { url = "https://files.pythonhosted.org/packages/85/70/92482ccffb96f5441aab93e26c4d66489eb599efdcf96fad90c14bbfb976/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40", size = 556030, upload-time = "2025-11-30T20:24:10.956Z" }, + { url = "https://files.pythonhosted.org/packages/20/53/7c7e784abfa500a2b6b583b147ee4bb5a2b3747a9166bab52fec4b5b5e7d/rpds_py-0.30.0-cp314-cp314t-win32.whl", hash = "sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0", size = 211570, upload-time = "2025-11-30T20:24:12.735Z" }, + { url = "https://files.pythonhosted.org/packages/d0/02/fa464cdfbe6b26e0600b62c528b72d8608f5cc49f96b8d6e38c95d60c676/rpds_py-0.30.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3", size = 226532, upload-time = "2025-11-30T20:24:14.634Z" }, +] + [[package]] name = "ruff" version = "0.15.12" @@ -833,6 +1263,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e5/30/8519fdde58a7bdf155b714359791ad1dc018b47d60269d5d160d311fdc36/sqlalchemy-2.0.49-py3-none-any.whl", hash = "sha256:ec44cfa7ef1a728e88ad41674de50f6db8cfdb3e2af84af86e0041aaf02d43d0", size = 1942158, upload-time = "2026-04-03T16:53:44.135Z" }, ] +[[package]] +name = "sse-starlette" +version = "3.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "starlette" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e1/9a/f35932a8c0eb6b2287b66fa65a0321df8c84e4e355a659c1841a37c39fdb/sse_starlette-3.4.1.tar.gz", hash = "sha256:f780bebcf6c8997fe514e3bd8e8c648d8284976b391c8bed0bcb1f611632b555", size = 35127, upload-time = "2026-04-26T13:32:32.292Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ff/07/45c21ed03d708c477367305726b89919b020a3a2a01f72aaf5ad941caf35/sse_starlette-3.4.1-py3-none-any.whl", hash = "sha256:6b43cf21f1d574d582a6e1b0cfbde1c94dc86a32a701a7168c99c4475c6bd1d0", size = 16487, upload-time = "2026-04-26T13:32:30.819Z" }, +] + [[package]] name = "starlette" version = "1.0.0" @@ -845,6 +1288,34 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0b/c9/584bc9651441b4ba60cc4d557d8a547b5aff901af35bda3a4ee30c819b82/starlette-1.0.0-py3-none-any.whl", hash = "sha256:d3ec55e0bb321692d275455ddfd3df75fff145d009685eb40dc91fc66b03d38b", size = 72651, upload-time = "2026-03-22T18:29:45.111Z" }, ] +[[package]] +name = "temporalio" +version = "1.27.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nexus-rpc" }, + { name = "protobuf" }, + { name = "types-protobuf" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/98/a1/6d59768d97ebb03676a33d10fec22a12ba6e7062801adc5946aa99138431/temporalio-1.27.0.tar.gz", hash = "sha256:fb92ae1077967ec626375a034af7e7108fe65f7b4ab1d98798ac2eecab247a65", size = 2497835, upload-time = "2026-04-30T22:39:56.305Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/60/07/6f2a59c250b1383f7da1a607cb03a9e7bc9bd9811ce8a4fdd4e951a334b4/temporalio-1.27.0-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:7daa4345b377b74602626326357c49ea1864bf1277fb7cb0b9f659f505c3dfb5", size = 14594920, upload-time = "2026-04-30T22:40:25.778Z" }, + { url = "https://files.pythonhosted.org/packages/a5/e2/16d881961ff4a8f64ab4205e5519ac6330e1cccc8c50808884e649bfc487/temporalio-1.27.0-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:17bed16eaf340dd48ad59178c3928ecf848674bce31f80529ea057848a29399b", size = 13940741, upload-time = "2026-04-30T22:40:35.578Z" }, + { url = "https://files.pythonhosted.org/packages/0f/bf/33c66ef5dcd5a63e5d4d171660e81302e4909545e6400e531d7d609d0a62/temporalio-1.27.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:449c1e4c0d4f4d6545442060b74760f491b67c075ebde6765be16468454e2874", size = 14238127, upload-time = "2026-04-30T22:40:46.198Z" }, + { url = "https://files.pythonhosted.org/packages/db/5f/a8b7f9e26e6b7a1d0fa8e5e9e280fac3439114318fc0e65ae4c97905ebee/temporalio-1.27.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5302035f8c2fdadd69a1362309ea5514bb4129bd4e494e6b69f9a5bb85e8cb85", size = 14782764, upload-time = "2026-04-30T22:40:05.627Z" }, + { url = "https://files.pythonhosted.org/packages/e4/ba/9428864f1d79c92b1a2f987aab5ab8f57911c0deb8dcf738cb0d89556ed6/temporalio-1.27.0-cp310-abi3-win_amd64.whl", hash = "sha256:0f125e82ff616dd46fb45ac4d253319e989bf3734a4eb25af703d5bc27147af9", size = 14974832, upload-time = "2026-04-30T22:40:14.921Z" }, +] + +[[package]] +name = "tenacity" +version = "9.1.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/47/c6/ee486fd809e357697ee8a44d3d69222b344920433d3b6666ccd9b374630c/tenacity-9.1.4.tar.gz", hash = "sha256:adb31d4c263f2bd041081ab33b498309a57c77f9acf2db65aadf0898179cf93a", size = 49413, upload-time = "2026-02-07T10:45:33.841Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d7/c1/eb8f9debc45d3b7918a32ab756658a0904732f75e555402972246b0b8e71/tenacity-9.1.4-py3-none-any.whl", hash = "sha256:6095a360c919085f28c6527de529e76a06ad89b23659fa881ae0649b867a9d55", size = 28926, upload-time = "2026-02-07T10:45:32.24Z" }, +] + [[package]] name = "tqdm" version = "4.67.3" @@ -857,6 +1328,27 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/16/e1/3079a9ff9b8e11b846c6ac5c8b5bfb7ff225eee721825310c91b3b50304f/tqdm-4.67.3-py3-none-any.whl", hash = "sha256:ee1e4c0e59148062281c49d80b25b67771a127c85fc9676d3be5f243206826bf", size = 78374, upload-time = "2026-02-03T17:35:50.982Z" }, ] +[[package]] +name = "types-protobuf" +version = "6.32.1.20260221" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5f/e2/9aa4a3b2469508bd7b4e2ae11cbedaf419222a09a1b94daffcd5efca4023/types_protobuf-6.32.1.20260221.tar.gz", hash = "sha256:6d5fb060a616bfb076cbb61b4b3c3969f5fc8bec5810f9a2f7e648ee5cbcbf6e", size = 64408, upload-time = "2026-02-21T03:55:13.916Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2e/e8/1fd38926f9cf031188fbc5a96694203ea6f24b0e34bd64a225ec6f6291ba/types_protobuf-6.32.1.20260221-py3-none-any.whl", hash = "sha256:da7cdd947975964a93c30bfbcc2c6841ee646b318d3816b033adc2c4eb6448e4", size = 77956, upload-time = "2026-02-21T03:55:12.894Z" }, +] + +[[package]] +name = "types-requests" +version = "2.33.0.20260408" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/69/6a/749dc53a54a3f35842c1f8197b3ca6b54af6d7458a1bfc75f6629b6da666/types_requests-2.33.0.20260408.tar.gz", hash = "sha256:95b9a86376807a216b2fb412b47617b202091c3ea7c078f47cc358d5528ccb7b", size = 23882, upload-time = "2026-04-08T04:34:49.33Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/90/b8/78fd6c037de4788c040fdd323b3369804400351b7827473920f6c1d03c10/types_requests-2.33.0.20260408-py3-none-any.whl", hash = "sha256:81f31d5ea4acb39f03be7bc8bed569ba6d5a9c5d97e89f45ac43d819b68ca50f", size = 20739, upload-time = "2026-04-08T04:34:48.325Z" }, +] + [[package]] name = "typing-extensions" version = "4.15.0" @@ -896,6 +1388,28 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, ] +[[package]] +name = "uuid-utils" +version = "0.14.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7b/d1/38a573f0c631c062cf42fa1f5d021d4dd3c31fb23e4376e4b56b0c9fbbed/uuid_utils-0.14.1.tar.gz", hash = "sha256:9bfc95f64af80ccf129c604fb6b8ca66c6f256451e32bc4570f760e4309c9b69", size = 22195, upload-time = "2026-02-20T22:50:38.833Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/b7/add4363039a34506a58457d96d4aa2126061df3a143eb4d042aedd6a2e76/uuid_utils-0.14.1-cp39-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:93a3b5dc798a54a1feb693f2d1cb4cf08258c32ff05ae4929b5f0a2ca624a4f0", size = 604679, upload-time = "2026-02-20T22:50:27.469Z" }, + { url = "https://files.pythonhosted.org/packages/dd/84/d1d0bef50d9e66d31b2019997c741b42274d53dde2e001b7a83e9511c339/uuid_utils-0.14.1-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:ccd65a4b8e83af23eae5e56d88034b2fe7264f465d3e830845f10d1591b81741", size = 309346, upload-time = "2026-02-20T22:50:31.857Z" }, + { url = "https://files.pythonhosted.org/packages/ef/ed/b6d6fd52a6636d7c3eddf97d68da50910bf17cd5ac221992506fb56cf12e/uuid_utils-0.14.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b56b0cacd81583834820588378e432b0696186683b813058b707aedc1e16c4b1", size = 344714, upload-time = "2026-02-20T22:50:42.642Z" }, + { url = "https://files.pythonhosted.org/packages/a8/a7/a19a1719fb626fe0b31882db36056d44fe904dc0cf15b06fdf56b2679cf7/uuid_utils-0.14.1-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb3cf14de789097320a3c56bfdfdd51b1225d11d67298afbedee7e84e3837c96", size = 350914, upload-time = "2026-02-20T22:50:36.487Z" }, + { url = "https://files.pythonhosted.org/packages/1d/fc/f6690e667fdc3bb1a73f57951f97497771c56fe23e3d302d7404be394d4f/uuid_utils-0.14.1-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:60e0854a90d67f4b0cc6e54773deb8be618f4c9bad98d3326f081423b5d14fae", size = 482609, upload-time = "2026-02-20T22:50:37.511Z" }, + { url = "https://files.pythonhosted.org/packages/54/6e/dcd3fa031320921a12ec7b4672dea3bd1dd90ddffa363a91831ba834d559/uuid_utils-0.14.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce6743ba194de3910b5feb1a62590cd2587e33a73ab6af8a01b642ceb5055862", size = 345699, upload-time = "2026-02-20T22:50:46.87Z" }, + { url = "https://files.pythonhosted.org/packages/04/28/e5220204b58b44ac0047226a9d016a113fde039280cc8732d9e6da43b39f/uuid_utils-0.14.1-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:043fb58fde6cf1620a6c066382f04f87a8e74feb0f95a585e4ed46f5d44af57b", size = 372205, upload-time = "2026-02-20T22:50:28.438Z" }, + { url = "https://files.pythonhosted.org/packages/c7/d9/3d2eb98af94b8dfffc82b6a33b4dfc87b0a5de2c68a28f6dde0db1f8681b/uuid_utils-0.14.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c915d53f22945e55fe0d3d3b0b87fd965a57f5fd15666fd92d6593a73b1dd297", size = 521836, upload-time = "2026-02-20T22:50:23.057Z" }, + { url = "https://files.pythonhosted.org/packages/a8/15/0eb106cc6fe182f7577bc0ab6e2f0a40be247f35c5e297dbf7bbc460bd02/uuid_utils-0.14.1-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:0972488e3f9b449e83f006ead5a0e0a33ad4a13e4462e865b7c286ab7d7566a3", size = 625260, upload-time = "2026-02-20T22:50:25.949Z" }, + { url = "https://files.pythonhosted.org/packages/3c/17/f539507091334b109e7496830af2f093d9fc8082411eafd3ece58af1f8ba/uuid_utils-0.14.1-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:1c238812ae0c8ffe77d8d447a32c6dfd058ea4631246b08b5a71df586ff08531", size = 587824, upload-time = "2026-02-20T22:50:35.225Z" }, + { url = "https://files.pythonhosted.org/packages/2e/c2/d37a7b2e41f153519367d4db01f0526e0d4b06f1a4a87f1c5dfca5d70a8b/uuid_utils-0.14.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:bec8f8ef627af86abf8298e7ec50926627e29b34fa907fcfbedb45aaa72bca43", size = 551407, upload-time = "2026-02-20T22:50:44.915Z" }, + { url = "https://files.pythonhosted.org/packages/65/36/2d24b2cbe78547c6532da33fb8613debd3126eccc33a6374ab788f5e46e9/uuid_utils-0.14.1-cp39-abi3-win32.whl", hash = "sha256:b54d6aa6252d96bac1fdbc80d26ba71bad9f220b2724d692ad2f2310c22ef523", size = 183476, upload-time = "2026-02-20T22:50:32.745Z" }, + { url = "https://files.pythonhosted.org/packages/83/92/2d7e90df8b1a69ec4cff33243ce02b7a62f926ef9e2f0eca5a026889cd73/uuid_utils-0.14.1-cp39-abi3-win_amd64.whl", hash = "sha256:fc27638c2ce267a0ce3e06828aff786f91367f093c80625ee21dad0208e0f5ba", size = 187147, upload-time = "2026-02-20T22:50:45.807Z" }, + { url = "https://files.pythonhosted.org/packages/d9/26/529f4beee17e5248e37e0bc17a2761d34c0fa3b1e5729c88adb2065bae6e/uuid_utils-0.14.1-cp39-abi3-win_arm64.whl", hash = "sha256:b04cb49b42afbc4ff8dbc60cf054930afc479d6f4dd7f1ec3bbe5dbfdde06b7a", size = 188132, upload-time = "2026-02-20T22:50:41.718Z" }, +] + [[package]] name = "uvicorn" version = "0.46.0" @@ -1001,6 +1515,57 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6f/28/258ebab549c2bf3e64d2b0217b973467394a9cea8c42f70418ca2c5d0d2e/websockets-16.0-py3-none-any.whl", hash = "sha256:1637db62fad1dc833276dded54215f2c7fa46912301a24bd94d45d46a011ceec", size = 171598, upload-time = "2026-01-10T09:23:45.395Z" }, ] +[[package]] +name = "xxhash" +version = "3.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/24/2f/e183a1b407002f5af81822bee18b61cdb94b8670208ef34734d8d2b8ebe9/xxhash-3.7.0.tar.gz", hash = "sha256:6cc4eefbb542a5d6ffd6d70ea9c502957c925e800f998c5630ecc809d6702bae", size = 82022, upload-time = "2026-04-25T11:10:32.553Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0e/cc/431db584f6fbb9312e40a173af027644e5580d39df1f73603cbb9dca4d6b/xxhash-3.7.0-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:8c5fcfd806c335bfa2adf1cd0b3110a44fc7b6995c3a648c27489bae85801465", size = 36644, upload-time = "2026-04-25T11:08:00.658Z" }, + { url = "https://files.pythonhosted.org/packages/bc/01/255ec513e0a705d1f9a61413e78dfce4e3235203f0ed525a24c2b4b56345/xxhash-3.7.0-cp314-cp314-android_24_x86_64.whl", hash = "sha256:506a0b488f190f0a06769575e30caf71615c898ed93ab18b0dbcb6dec5c3713c", size = 35003, upload-time = "2026-04-25T11:08:02.338Z" }, + { url = "https://files.pythonhosted.org/packages/68/70/c55fc33c93445b44d8fc5a17b41ed99e3cebe92bcf8396809e63fc9a1165/xxhash-3.7.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:ec68dbba21532c0173a9872298e65c89749f7c9d21538c3a78b5bb6105871568", size = 29655, upload-time = "2026-04-25T11:08:03.701Z" }, + { url = "https://files.pythonhosted.org/packages/c2/72/ff8de73df000d74467d12a59ce6d6e2b2a368b978d41ab7b1fba5ed442be/xxhash-3.7.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:fa77e7ec1450d415d20129961814787c9abd9a07f98872f070b1fe96c5084611", size = 30664, upload-time = "2026-04-25T11:08:05.011Z" }, + { url = "https://files.pythonhosted.org/packages/b6/91/08416d9bd9bc3bf39d831abe8a5631ac2db5141dfd6fe81c3fe59a1f9264/xxhash-3.7.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:fe32736295ea38e43e7d9424053c8c47c9f64fecfc7c895fb3da9b30b131c9ee", size = 33317, upload-time = "2026-04-25T11:08:06.413Z" }, + { url = "https://files.pythonhosted.org/packages/0e/3b/86b1caa4dee10a99f4bf9521e623359341c5e50d05158fa10c275b2bd079/xxhash-3.7.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:ab9dd2c83c4bbd63e422181a76f13502d049d3ddcac9a1bdc29196263d692bb8", size = 33457, upload-time = "2026-04-25T11:08:08.099Z" }, + { url = "https://files.pythonhosted.org/packages/ed/38/98ea14ad1517e1461292a65906951458d520689782bfbae111050145bdba/xxhash-3.7.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:3afec3a336a2286601a437cb07562ab0227685e6fbb9ec17e8c18457ff348ecf", size = 30894, upload-time = "2026-04-25T11:08:09.429Z" }, + { url = "https://files.pythonhosted.org/packages/61/a2/074654d0b893606541199993c7db70067d9fc63b748e0d60020a52a1bd36/xxhash-3.7.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:565df64437a9390f84465dcca33e7377114c7ede8d05cd2cf20081f831ea788e", size = 194409, upload-time = "2026-04-25T11:08:10.91Z" }, + { url = "https://files.pythonhosted.org/packages/e2/26/6d2a1afc468189f77ca28c32e1c83e1b9da1178231e05641dbc1b350e332/xxhash-3.7.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:12eca820a5d558633d423bf8bb78ce72a55394823f64089247f788a7e0ae691e", size = 213135, upload-time = "2026-04-25T11:08:12.575Z" }, + { url = "https://files.pythonhosted.org/packages/8e/0e/d8aecf95e09c42547453137be74d2f7b8b14e08f5177fa2fab6144a19061/xxhash-3.7.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f262b8f7599516567e070abf607b9af649052b2c4bd6f9be02b0cb41b7024805", size = 236379, upload-time = "2026-04-25T11:08:14.206Z" }, + { url = "https://files.pythonhosted.org/packages/f2/74/8140e8210536b3dd0cc816c4faaeb5ba6e63e8125ab25af4bcddd6a037b3/xxhash-3.7.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1598916cb197681e03e601901e4ab96a9a963de398c59d0964f8a6f44a2b361", size = 212447, upload-time = "2026-04-25T11:08:15.79Z" }, + { url = "https://files.pythonhosted.org/packages/a0/d2/462001d2903b4bee5a5689598a0a55e5e7cd1ac7f4247a5545cff10d3ebb/xxhash-3.7.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:322b2f0622230f526aeb1738149948a7ae357a9e2ceb1383c6fd1fdaecdafa16", size = 445660, upload-time = "2026-04-25T11:08:17.441Z" }, + { url = "https://files.pythonhosted.org/packages/23/09/2bd1ed7f8689b20e51727952cac8329d50c694dc32b2eba06ba5bc742b37/xxhash-3.7.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24cc22070880cc57b830a65cde4e65fa884c6d9b28ae4803b5ee05911e7bafba", size = 194076, upload-time = "2026-04-25T11:08:19.134Z" }, + { url = "https://files.pythonhosted.org/packages/c9/6e/692302cd0a5f4ac4e6289f37fa888dc2e1e07750b68fe3e4bfe939b8cea3/xxhash-3.7.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb5a888a968b2434abf9ecda357b5d43f10d7b5a6da6fdbbe036208473aff0e2", size = 284990, upload-time = "2026-04-25T11:08:20.618Z" }, + { url = "https://files.pythonhosted.org/packages/05/d9/e54b159b3d9df7999d2a7c676ce7b323d1b5588a64f8f51ed8172567bd87/xxhash-3.7.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a999771ff97bec27d18341be4f3a36b163bb1ac41ec17bef6d2dabd84acd33c7", size = 210590, upload-time = "2026-04-25T11:08:22.24Z" }, + { url = "https://files.pythonhosted.org/packages/50/93/0e0df1a3a196ced4ca71de76d65ead25d8e87bbfb87b64306ea47a40c00d/xxhash-3.7.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:ed4a6efe2dee1655adb73e7ad40c6aa955a6892422b1e3b95de6a34de56e3cbb", size = 241442, upload-time = "2026-04-25T11:08:23.844Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a9/d917a7a814e90b218f8a0d37967105eea91bf752c3303683c99a1f7bfc1f/xxhash-3.7.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:9fd17f14ac0faa12126c2f9ca774a8cf342957265ec3c8669c144e5e6cdb478c", size = 198356, upload-time = "2026-04-25T11:08:25.99Z" }, + { url = "https://files.pythonhosted.org/packages/89/5e/f2ba1877c39469abbefc72991d6ebdcbd4c0880db01ae8cb1f553b0c537d/xxhash-3.7.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:05fd1254268c59b5cb2a029dfc204275e9fc52de2913f1e53aa8d01442c96b4d", size = 210898, upload-time = "2026-04-25T11:08:27.608Z" }, + { url = "https://files.pythonhosted.org/packages/90/c6/be56b58e73de531f39a10de1355bb77ceb663900dc4bf2d6d3002a9c3f9e/xxhash-3.7.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:a2eae53197c6276d5b317f75a1be226bbf440c20b58bf525f36b5d0e1f657ca6", size = 275519, upload-time = "2026-04-25T11:08:29.301Z" }, + { url = "https://files.pythonhosted.org/packages/92/e2/17ddc85d5765b9c709f192009ed8f5a1fc876f4eb35bba7c307b5b1169f9/xxhash-3.7.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:bfe6f92e3522dcbe8c4281efd74fa7542a336cb00b0e3272c4ec0edabeaeaf67", size = 414191, upload-time = "2026-04-25T11:08:31.16Z" }, + { url = "https://files.pythonhosted.org/packages/9c/42/85f5b79f4bf1ec7ba052491164adfd4f4e9519f5dc7246de4fbd64a1bd56/xxhash-3.7.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7ab9a49c410d8c6c786ab99e79c529938d894c01433130353dd0fe999111077a", size = 191604, upload-time = "2026-04-25T11:08:32.862Z" }, + { url = "https://files.pythonhosted.org/packages/b8/d0/6127b623aa4cca18d8b7743592b048d689fd6c6e37ff26a22cddf6cd9d7f/xxhash-3.7.0-cp314-cp314-win32.whl", hash = "sha256:040ea63668f9185b92bc74942df09c7e65703deed71431333678fc6e739a9955", size = 31271, upload-time = "2026-04-25T11:08:34.651Z" }, + { url = "https://files.pythonhosted.org/packages/64/4f/44fc4788568004c43921701cbc127f48218a1eede2c9aea231115323564d/xxhash-3.7.0-cp314-cp314-win_amd64.whl", hash = "sha256:2a61e2a3fb23c892496d587b470dee7fa1b58b248a187719c65ea8e94ec13257", size = 32284, upload-time = "2026-04-25T11:08:35.987Z" }, + { url = "https://files.pythonhosted.org/packages/6d/77/18bb895eb60a49453d16e17d67990e5caff557c78eafc90ad4e2eabf4570/xxhash-3.7.0-cp314-cp314-win_arm64.whl", hash = "sha256:c7741c7524961d8c0cb4d4c21b28957ff731a3fd5b5cd8b856dc80a40e9e5acc", size = 28701, upload-time = "2026-04-25T11:08:37.767Z" }, + { url = "https://files.pythonhosted.org/packages/45/a0/46f72244570c550fbbb7db1ef554183dd5ebe9136385f30e032b781ae8f6/xxhash-3.7.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:fc84bf7aa7592f31ec63a3e7b11d624f468a3f19f5238cec7282a42e838ab1d7", size = 33646, upload-time = "2026-04-25T11:08:39.109Z" }, + { url = "https://files.pythonhosted.org/packages/4a/3a/453846a7eceea11e75def361eed01ec6a0205b9822c19927ed364ccae7cc/xxhash-3.7.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9f1563fdc8abfc389748e6932c7e4e99c89a53e4ec37d4563c24fc06f5e5644b", size = 31125, upload-time = "2026-04-25T11:08:40.467Z" }, + { url = "https://files.pythonhosted.org/packages/bd/3e/49434aba738885d512f9e486db1bdd19db28dfa40372b56da26ef7a4e738/xxhash-3.7.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:2d415f18becf6f153046ab6adc97da77e3643a0ee205dae61c4012604113a020", size = 196633, upload-time = "2026-04-25T11:08:41.943Z" }, + { url = "https://files.pythonhosted.org/packages/a4/e9/006cb6127baeb9f8abe6d15e62faa01349f09b34e2bfd65175b2422d026b/xxhash-3.7.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bb16aa13ed175bc9be5c2491ba031b85a9b51c4ed90e0b3d4ebe63cf3fb54f8e", size = 215899, upload-time = "2026-04-25T11:08:43.645Z" }, + { url = "https://files.pythonhosted.org/packages/27/e4/cc57d72e66df0ae29b914335f1c6dcf61e8f3746ddf0ae3c471aa4f15e00/xxhash-3.7.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f9fd595f1e5941b3d7863e4774e4b30caa6731fc34b9277da032295aa5656ee5", size = 238116, upload-time = "2026-04-25T11:08:45.698Z" }, + { url = "https://files.pythonhosted.org/packages/af/78/3531d4a3fd8a0038cc6be1f265a69c1b3587f557a10b677dd736de2202c1/xxhash-3.7.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1295325c5a98d552333fa53dc2b026b0ef0ec9c8e73ca3a952990b4c7d65d459", size = 215012, upload-time = "2026-04-25T11:08:47.355Z" }, + { url = "https://files.pythonhosted.org/packages/b4/f6/259fb1eaaec921f59b17203b0daee69829761226d3b980d5191d7723dd83/xxhash-3.7.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3573a651d146912da9daa9e29e5fbc45994420daaa9ef1e2fa5823e1dc485513", size = 448534, upload-time = "2026-04-25T11:08:49.149Z" }, + { url = "https://files.pythonhosted.org/packages/7b/16/a66d0eaf6a7e68532c07714361ddc904c663ec940f3b028c1ae4a21a7b9d/xxhash-3.7.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5ec1e080a3d02d94ea9335bfab0e3374b877e25411422c18f51a943fa4b46381", size = 196217, upload-time = "2026-04-25T11:08:50.805Z" }, + { url = "https://files.pythonhosted.org/packages/8d/ef/d2efc7fc51756dc52509109d1a25cefc859d74bc4b19a167b12dbd8c2786/xxhash-3.7.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:84415265192072d8638a3afc3c1bc5995e310570cd9acb54dc46d3939e364fe0", size = 286906, upload-time = "2026-04-25T11:08:52.418Z" }, + { url = "https://files.pythonhosted.org/packages/fc/67/25decd1d4a4018582ec4db2a868a2b7e40640f4adb20dfeb19ac923aa825/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d4dea659b57443989ef32f4295104fd6912c73d0bf26d1d148bb88a9f159b02", size = 213057, upload-time = "2026-04-25T11:08:54.105Z" }, + { url = "https://files.pythonhosted.org/packages/0d/5d/17651eb29d06786cdc40c60ae3d27d645aa5d61d2eca6237a7ba0b94789b/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:05ece0fe4d9c9c2728912d1981ae1566cfc83a011571b24732cbf76e1fb70dca", size = 243886, upload-time = "2026-04-25T11:08:56.109Z" }, + { url = "https://files.pythonhosted.org/packages/8a/d4/174d9cf7502243d586e6a9ae842b1ae23026620995114f85f1380e588bc9/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:fd880353cf1ffaf321bc18dd663e111976dbd0d3bbd8a66d58d2b470dfa7f396", size = 201015, upload-time = "2026-04-25T11:08:57.777Z" }, + { url = "https://files.pythonhosted.org/packages/91/8c/2254e2d06c3ac5e6fe22eaf3da791b87ea823ae9f2c17b4af66755c5752d/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:4e15cc9e2817f6481160f930c62842b3ff419e20e13072bcbab12230943092bc", size = 213457, upload-time = "2026-04-25T11:08:59.826Z" }, + { url = "https://files.pythonhosted.org/packages/79/a2/e3daa762545921173e3360f3b4ff7fc63c2d27359f7230ec1a7a74e117f6/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:90b9d1a8bd37d768ffc92a1f651ec69afc532a96fa1ac2ea7abbed5d630b3237", size = 277738, upload-time = "2026-04-25T11:09:01.423Z" }, + { url = "https://files.pythonhosted.org/packages/e1/4c/e186da2c46b87f5204640e008d42730bf3c1ee9f0efb71ae1ebcdfeac681/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:157c49475b34ecea8809e51123d9769a534e139d1247942f7a4bc67710bb2533", size = 417127, upload-time = "2026-04-25T11:09:03.592Z" }, + { url = "https://files.pythonhosted.org/packages/17/28/3798e15007a3712d0da3d3fe70f8e11916569858b5cc371053bc26270832/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5a6ddec83325685e729ca119d1f5c518ec39294212ecd770e60693cdc5f7eb79", size = 193962, upload-time = "2026-04-25T11:09:06.228Z" }, + { url = "https://files.pythonhosted.org/packages/ad/95/a26baa93b5241fd7630998816a4ec47a5a0bad193b3f8fc8f3593e1a4a67/xxhash-3.7.0-cp314-cp314t-win32.whl", hash = "sha256:a04a6cab47e2166435aaf5b9e5ee41d1532cc8300efdef87f2a4d0acb7db19ed", size = 31643, upload-time = "2026-04-25T11:09:08.153Z" }, + { url = "https://files.pythonhosted.org/packages/44/36/5454f13c447e395f9b06a3e91274c59f503d31fad84e1836efe3bdb71f6a/xxhash-3.7.0-cp314-cp314t-win_amd64.whl", hash = "sha256:8653dd7c2eda020545bb2c71c7f7039b53fe7434d0fc1a0a9deb79ab3f1a4fc1", size = 32522, upload-time = "2026-04-25T11:09:09.534Z" }, + { url = "https://files.pythonhosted.org/packages/74/35/698e7e3ff38e22992ea24870a511d8762474fb6783627a2910ff22a185c2/xxhash-3.7.0-cp314-cp314t-win_arm64.whl", hash = "sha256:468f0fc114faaa4b36699f8e328bbc3bb11dc418ba94ac52c26dd736d4b6c637", size = 28807, upload-time = "2026-04-25T11:09:11.234Z" }, +] + [[package]] name = "zipp" version = "3.23.1" @@ -1009,3 +1574,26 @@ sdist = { url = "https://files.pythonhosted.org/packages/30/21/093488dfc7cc8964d wheels = [ { url = "https://files.pythonhosted.org/packages/08/8a/0861bec20485572fbddf3dfba2910e38fe249796cb73ecdeb74e07eeb8d3/zipp-3.23.1-py3-none-any.whl", hash = "sha256:0b3596c50a5c700c9cb40ba8d86d9f2cc4807e9bedb06bcdf7fac85633e444dc", size = 10378, upload-time = "2026-04-13T23:21:45.386Z" }, ] + +[[package]] +name = "zstandard" +version = "0.25.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fd/aa/3e0508d5a5dd96529cdc5a97011299056e14c6505b678fd58938792794b1/zstandard-0.25.0.tar.gz", hash = "sha256:7713e1179d162cf5c7906da876ec2ccb9c3a9dcbdffef0cc7f70c3667a205f0b", size = 711513, upload-time = "2025-09-14T22:15:54.002Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3d/5c/f8923b595b55fe49e30612987ad8bf053aef555c14f05bb659dd5dbe3e8a/zstandard-0.25.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e29f0cf06974c899b2c188ef7f783607dbef36da4c242eb6c82dcd8b512855e3", size = 795887, upload-time = "2025-09-14T22:17:54.198Z" }, + { url = "https://files.pythonhosted.org/packages/8d/09/d0a2a14fc3439c5f874042dca72a79c70a532090b7ba0003be73fee37ae2/zstandard-0.25.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:05df5136bc5a011f33cd25bc9f506e7426c0c9b3f9954f056831ce68f3b6689f", size = 640658, upload-time = "2025-09-14T22:17:55.423Z" }, + { url = "https://files.pythonhosted.org/packages/5d/7c/8b6b71b1ddd517f68ffb55e10834388d4f793c49c6b83effaaa05785b0b4/zstandard-0.25.0-cp314-cp314-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:f604efd28f239cc21b3adb53eb061e2a205dc164be408e553b41ba2ffe0ca15c", size = 5379849, upload-time = "2025-09-14T22:17:57.372Z" }, + { url = "https://files.pythonhosted.org/packages/a4/86/a48e56320d0a17189ab7a42645387334fba2200e904ee47fc5a26c1fd8ca/zstandard-0.25.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:223415140608d0f0da010499eaa8ccdb9af210a543fac54bce15babbcfc78439", size = 5058095, upload-time = "2025-09-14T22:17:59.498Z" }, + { url = "https://files.pythonhosted.org/packages/f8/ad/eb659984ee2c0a779f9d06dbfe45e2dc39d99ff40a319895df2d3d9a48e5/zstandard-0.25.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2e54296a283f3ab5a26fc9b8b5d4978ea0532f37b231644f367aa588930aa043", size = 5551751, upload-time = "2025-09-14T22:18:01.618Z" }, + { url = "https://files.pythonhosted.org/packages/61/b3/b637faea43677eb7bd42ab204dfb7053bd5c4582bfe6b1baefa80ac0c47b/zstandard-0.25.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ca54090275939dc8ec5dea2d2afb400e0f83444b2fc24e07df7fdef677110859", size = 6364818, upload-time = "2025-09-14T22:18:03.769Z" }, + { url = "https://files.pythonhosted.org/packages/31/dc/cc50210e11e465c975462439a492516a73300ab8caa8f5e0902544fd748b/zstandard-0.25.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e09bb6252b6476d8d56100e8147b803befa9a12cea144bbe629dd508800d1ad0", size = 5560402, upload-time = "2025-09-14T22:18:05.954Z" }, + { url = "https://files.pythonhosted.org/packages/c9/ae/56523ae9c142f0c08efd5e868a6da613ae76614eca1305259c3bf6a0ed43/zstandard-0.25.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a9ec8c642d1ec73287ae3e726792dd86c96f5681eb8df274a757bf62b750eae7", size = 4955108, upload-time = "2025-09-14T22:18:07.68Z" }, + { url = "https://files.pythonhosted.org/packages/98/cf/c899f2d6df0840d5e384cf4c4121458c72802e8bda19691f3b16619f51e9/zstandard-0.25.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:a4089a10e598eae6393756b036e0f419e8c1d60f44a831520f9af41c14216cf2", size = 5269248, upload-time = "2025-09-14T22:18:09.753Z" }, + { url = "https://files.pythonhosted.org/packages/1b/c0/59e912a531d91e1c192d3085fc0f6fb2852753c301a812d856d857ea03c6/zstandard-0.25.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:f67e8f1a324a900e75b5e28ffb152bcac9fbed1cc7b43f99cd90f395c4375344", size = 5430330, upload-time = "2025-09-14T22:18:11.966Z" }, + { url = "https://files.pythonhosted.org/packages/a0/1d/7e31db1240de2df22a58e2ea9a93fc6e38cc29353e660c0272b6735d6669/zstandard-0.25.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:9654dbc012d8b06fc3d19cc825af3f7bf8ae242226df5f83936cb39f5fdc846c", size = 5811123, upload-time = "2025-09-14T22:18:13.907Z" }, + { url = "https://files.pythonhosted.org/packages/f6/49/fac46df5ad353d50535e118d6983069df68ca5908d4d65b8c466150a4ff1/zstandard-0.25.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4203ce3b31aec23012d3a4cf4a2ed64d12fea5269c49aed5e4c3611b938e4088", size = 5359591, upload-time = "2025-09-14T22:18:16.465Z" }, + { url = "https://files.pythonhosted.org/packages/c2/38/f249a2050ad1eea0bb364046153942e34abba95dd5520af199aed86fbb49/zstandard-0.25.0-cp314-cp314-win32.whl", hash = "sha256:da469dc041701583e34de852d8634703550348d5822e66a0c827d39b05365b12", size = 444513, upload-time = "2025-09-14T22:18:20.61Z" }, + { url = "https://files.pythonhosted.org/packages/3a/43/241f9615bcf8ba8903b3f0432da069e857fc4fd1783bd26183db53c4804b/zstandard-0.25.0-cp314-cp314-win_amd64.whl", hash = "sha256:c19bcdd826e95671065f8692b5a4aa95c52dc7a02a4c5a0cac46deb879a017a2", size = 516118, upload-time = "2025-09-14T22:18:17.849Z" }, + { url = "https://files.pythonhosted.org/packages/f0/ef/da163ce2450ed4febf6467d77ccb4cd52c4c30ab45624bad26ca0a27260c/zstandard-0.25.0-cp314-cp314-win_arm64.whl", hash = "sha256:d7541afd73985c630bafcd6338d2518ae96060075f9463d7dc14cfb33514383d", size = 476940, upload-time = "2025-09-14T22:18:19.088Z" }, +]