Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| dd83d55aa7 | |||
| e59f5ae9d1 | |||
| dc772253af | |||
| f07c911e1a | |||
| 62179042e7 | |||
| 0222bdbcba | |||
| 883fd4d60f |
151
.woodpecker.yml
Normal file
151
.woodpecker.yml
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
when:
|
||||||
|
- event: [push, pull_request, manual]
|
||||||
|
|
||||||
|
variables:
|
||||||
|
- &uv_image "ghcr.io/astral-sh/uv:python3.12-bookworm-slim"
|
||||||
|
|
||||||
|
steps:
|
||||||
|
install:
|
||||||
|
image: *uv_image
|
||||||
|
commands:
|
||||||
|
- uv sync --all-extras --frozen
|
||||||
|
|
||||||
|
lint:
|
||||||
|
image: *uv_image
|
||||||
|
commands:
|
||||||
|
- |
|
||||||
|
uv sync --all-extras --frozen
|
||||||
|
uv run ruff check src/ tests/
|
||||||
|
uv run ruff format --check src/ tests/
|
||||||
|
depends_on:
|
||||||
|
- install
|
||||||
|
|
||||||
|
typecheck:
|
||||||
|
image: *uv_image
|
||||||
|
commands:
|
||||||
|
- |
|
||||||
|
uv sync --all-extras --frozen
|
||||||
|
uv run mypy src/
|
||||||
|
depends_on:
|
||||||
|
- install
|
||||||
|
|
||||||
|
security-bandit:
|
||||||
|
image: *uv_image
|
||||||
|
commands:
|
||||||
|
- |
|
||||||
|
uv sync --all-extras --frozen
|
||||||
|
uv run bandit -r src/ -f screen --skip B311
|
||||||
|
depends_on:
|
||||||
|
- install
|
||||||
|
|
||||||
|
security-audit:
|
||||||
|
image: *uv_image
|
||||||
|
commands:
|
||||||
|
- |
|
||||||
|
uv sync --all-extras --frozen
|
||||||
|
uv run pip-audit
|
||||||
|
depends_on:
|
||||||
|
- install
|
||||||
|
|
||||||
|
test:
|
||||||
|
image: *uv_image
|
||||||
|
commands:
|
||||||
|
- |
|
||||||
|
uv sync --all-extras --frozen
|
||||||
|
uv run pytest --cov=src/mosaicstack_telemetry --cov-report=term-missing --cov-fail-under=85
|
||||||
|
depends_on:
|
||||||
|
- install
|
||||||
|
|
||||||
|
publish:
|
||||||
|
image: *uv_image
|
||||||
|
environment:
|
||||||
|
GITEA_USER:
|
||||||
|
from_secret: gitea_username
|
||||||
|
GITEA_TOKEN:
|
||||||
|
from_secret: gitea_token
|
||||||
|
CI_COMMIT_BRANCH: ${CI_COMMIT_BRANCH}
|
||||||
|
CI_COMMIT_TAG: ${CI_COMMIT_TAG}
|
||||||
|
commands:
|
||||||
|
- |
|
||||||
|
uv sync --all-extras --frozen
|
||||||
|
uv pip install twine
|
||||||
|
BASE_VERSION=$$(uv run python3 -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
|
||||||
|
|
||||||
|
if [ -n "$$CI_COMMIT_TAG" ] || [ "$$CI_COMMIT_BRANCH" = "main" ]; then
|
||||||
|
VERSION="$$BASE_VERSION"
|
||||||
|
echo "Release build: $$VERSION"
|
||||||
|
elif [ "$$CI_COMMIT_BRANCH" = "develop" ]; then
|
||||||
|
TIMESTAMP=$$(date -u +%Y%m%d%H%M%S)
|
||||||
|
VERSION="$${BASE_VERSION}.dev$${TIMESTAMP}"
|
||||||
|
echo "Dev build: $$VERSION"
|
||||||
|
sed -i "s/version = \"$$BASE_VERSION\"/version = \"$$VERSION\"/" pyproject.toml
|
||||||
|
fi
|
||||||
|
|
||||||
|
uv build
|
||||||
|
|
||||||
|
if [ "$$CI_COMMIT_BRANCH" = "develop" ]; then
|
||||||
|
echo "Publishing dev version $$VERSION..."
|
||||||
|
uv run twine upload \
|
||||||
|
--repository-url "https://git.mosaicstack.dev/api/packages/mosaic/pypi" \
|
||||||
|
--username "$$GITEA_USER" \
|
||||||
|
--password "$$GITEA_TOKEN" \
|
||||||
|
dist/*
|
||||||
|
echo "Published mosaicstack-telemetry $$VERSION"
|
||||||
|
else
|
||||||
|
echo "Checking if release $$VERSION is already published..."
|
||||||
|
INDEX_PAGE=$$(curl -sf "https://git.mosaicstack.dev/api/packages/mosaic/pypi/simple/mosaicstack-telemetry/" 2>/dev/null || echo "")
|
||||||
|
if echo "$$INDEX_PAGE" | grep -q "mosaicstack_telemetry-$$VERSION"; then
|
||||||
|
echo "Version $$VERSION already published, skipping upload"
|
||||||
|
else
|
||||||
|
echo "Publishing release $$VERSION..."
|
||||||
|
uv run twine upload \
|
||||||
|
--repository-url "https://git.mosaicstack.dev/api/packages/mosaic/pypi" \
|
||||||
|
--username "$$GITEA_USER" \
|
||||||
|
--password "$$GITEA_TOKEN" \
|
||||||
|
dist/*
|
||||||
|
echo "Published mosaicstack-telemetry $$VERSION"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
when:
|
||||||
|
- branch: [main, develop]
|
||||||
|
event: [push, manual, tag]
|
||||||
|
depends_on:
|
||||||
|
- lint
|
||||||
|
- typecheck
|
||||||
|
- security-bandit
|
||||||
|
- security-audit
|
||||||
|
- test
|
||||||
|
|
||||||
|
link-package:
|
||||||
|
image: alpine:3
|
||||||
|
environment:
|
||||||
|
GITEA_TOKEN:
|
||||||
|
from_secret: gitea_token
|
||||||
|
commands:
|
||||||
|
- apk add --no-cache curl
|
||||||
|
- sleep 5
|
||||||
|
- |
|
||||||
|
set -e
|
||||||
|
for attempt in 1 2 3; do
|
||||||
|
STATUS=$$(curl -s -o /dev/null -w "%{http_code}" -X POST \
|
||||||
|
-H "Authorization: token $$GITEA_TOKEN" \
|
||||||
|
"https://git.mosaicstack.dev/api/v1/packages/mosaic/pypi/mosaicstack-telemetry/-/link/telemetry-client-py")
|
||||||
|
if [ "$$STATUS" = "201" ] || [ "$$STATUS" = "204" ]; then
|
||||||
|
echo "Package linked to repository"
|
||||||
|
exit 0
|
||||||
|
elif [ "$$STATUS" = "400" ]; then
|
||||||
|
echo "Package already linked (OK)"
|
||||||
|
exit 0
|
||||||
|
elif [ $$attempt -lt 3 ]; then
|
||||||
|
echo "Package not found yet, retrying in 5s (attempt $$attempt/3)..."
|
||||||
|
sleep 5
|
||||||
|
else
|
||||||
|
echo "Failed to link package (status $$STATUS)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
when:
|
||||||
|
- branch: [main, develop]
|
||||||
|
event: [push, manual, tag]
|
||||||
|
depends_on:
|
||||||
|
- publish
|
||||||
52
CLAUDE.md
52
CLAUDE.md
@@ -39,3 +39,55 @@ uv run mypy src/
|
|||||||
- All logging uses `logging.getLogger("mosaicstack_telemetry")`
|
- All logging uses `logging.getLogger("mosaicstack_telemetry")`
|
||||||
- Runtime deps: httpx + pydantic only
|
- Runtime deps: httpx + pydantic only
|
||||||
- Python 3.10+ compatible (uses `str, Enum` mixin instead of StrEnum)
|
- Python 3.10+ compatible (uses `str, Enum` mixin instead of StrEnum)
|
||||||
|
|
||||||
|
## Conditional Documentation Loading
|
||||||
|
|
||||||
|
**Read the relevant guide before starting work:**
|
||||||
|
|
||||||
|
| Task Type | Guide |
|
||||||
|
|-----------|-------|
|
||||||
|
| Bootstrapping a new project | `~/.claude/agent-guides/bootstrap.md` |
|
||||||
|
| Orchestrating autonomous tasks | `~/.claude/agent-guides/orchestrator.md` |
|
||||||
|
| Ralph autonomous development | `~/.claude/agent-guides/ralph-autonomous.md` |
|
||||||
|
| Frontend development | `~/.claude/agent-guides/frontend.md` |
|
||||||
|
| Backend/API development | `~/.claude/agent-guides/backend.md` |
|
||||||
|
| TypeScript strict typing | `~/.claude/agent-guides/typescript.md` |
|
||||||
|
| Code review | `~/.claude/agent-guides/code-review.md` |
|
||||||
|
| Authentication/Authorization | `~/.claude/agent-guides/authentication.md` |
|
||||||
|
| Infrastructure/DevOps | `~/.claude/agent-guides/infrastructure.md` |
|
||||||
|
| QA/Testing | `~/.claude/agent-guides/qa-testing.md` |
|
||||||
|
| Secrets management (Vault) | `~/.claude/agent-guides/vault-secrets.md` |
|
||||||
|
|
||||||
|
|
||||||
|
## Commits
|
||||||
|
|
||||||
|
```
|
||||||
|
<type>(#issue): Brief description
|
||||||
|
|
||||||
|
Detailed explanation if needed.
|
||||||
|
|
||||||
|
Fixes #123
|
||||||
|
```
|
||||||
|
|
||||||
|
Types: `feat`, `fix`, `docs`, `test`, `refactor`, `chore`
|
||||||
|
|
||||||
|
|
||||||
|
## Secrets Management
|
||||||
|
|
||||||
|
**NEVER hardcode secrets.** Use `.env` files (gitignored) or a secrets manager.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# .env.example is committed (with placeholders)
|
||||||
|
# .env is NOT committed (contains real values)
|
||||||
|
```
|
||||||
|
|
||||||
|
Ensure `.gitignore` includes `.env*` (except `.env.example`).
|
||||||
|
|
||||||
|
|
||||||
|
## Multi-Agent Coordination
|
||||||
|
|
||||||
|
When multiple agents work on this project:
|
||||||
|
1. `git pull --rebase` before editing
|
||||||
|
2. `git pull --rebase` before pushing
|
||||||
|
3. If conflicts, **alert the user** — don't auto-resolve data conflicts
|
||||||
|
|
||||||
|
|||||||
231
README.md
231
README.md
@@ -2,17 +2,52 @@
|
|||||||
|
|
||||||
Python client SDK for [Mosaic Stack Telemetry](https://github.com/mosaicstack/telemetry). Report AI coding task-completion telemetry and query crowd-sourced predictions for token usage, cost, and quality outcomes.
|
Python client SDK for [Mosaic Stack Telemetry](https://github.com/mosaicstack/telemetry). Report AI coding task-completion telemetry and query crowd-sourced predictions for token usage, cost, and quality outcomes.
|
||||||
|
|
||||||
|
**Targets:** Telemetry API **v1** | Event schema **1.0** | Python **3.10+**
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
|
Install from the Mosaic Stack package registry:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pip install mosaicstack-telemetry
|
pip install mosaicstack-telemetry --index-url https://git.mosaicstack.dev/api/packages/mosaic/pypi/simple/
|
||||||
# or
|
|
||||||
uv add mosaicstack-telemetry
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Quick Start (Sync)
|
Or with [uv](https://docs.astral.sh/uv/):
|
||||||
|
|
||||||
Best for scripts, aider integrations, and non-async contexts:
|
```bash
|
||||||
|
uv add mosaicstack-telemetry --index-url https://git.mosaicstack.dev/api/packages/mosaic/pypi/simple/
|
||||||
|
```
|
||||||
|
|
||||||
|
To avoid passing `--index-url` every time, add the registry to your project's `pyproject.toml`:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[[tool.uv.index]]
|
||||||
|
name = "mosaic"
|
||||||
|
url = "https://git.mosaicstack.dev/api/packages/mosaic/pypi/simple/"
|
||||||
|
```
|
||||||
|
|
||||||
|
Or to `pip.conf` / `~/.config/pip/pip.conf`:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[global]
|
||||||
|
extra-index-url = https://git.mosaicstack.dev/api/packages/mosaic/pypi/simple/
|
||||||
|
```
|
||||||
|
|
||||||
|
Runtime dependencies: `httpx` and `pydantic`.
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### 1. Configure
|
||||||
|
|
||||||
|
Set environment variables (or pass values to the constructor):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export MOSAIC_TELEMETRY_SERVER_URL=https://tel-api.mosaicstack.dev
|
||||||
|
export MOSAIC_TELEMETRY_API_KEY=your-64-char-hex-api-key
|
||||||
|
export MOSAIC_TELEMETRY_INSTANCE_ID=a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Track Events
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from mosaicstack_telemetry import (
|
from mosaicstack_telemetry import (
|
||||||
@@ -27,97 +62,75 @@ from mosaicstack_telemetry import (
|
|||||||
QualityGate,
|
QualityGate,
|
||||||
)
|
)
|
||||||
|
|
||||||
config = TelemetryConfig(
|
config = TelemetryConfig() # Reads from MOSAIC_TELEMETRY_* env vars
|
||||||
server_url="https://telemetry.mosaicstack.dev",
|
|
||||||
api_key="your-64-char-hex-api-key-here...",
|
|
||||||
instance_id="your-uuid-instance-id",
|
|
||||||
)
|
|
||||||
|
|
||||||
client = TelemetryClient(config)
|
with TelemetryClient(config) as client:
|
||||||
client.start() # Starts background submission thread
|
event = (
|
||||||
|
|
||||||
# Build and track an event
|
|
||||||
event = (
|
|
||||||
EventBuilder(instance_id=config.instance_id)
|
EventBuilder(instance_id=config.instance_id)
|
||||||
.task_type(TaskType.IMPLEMENTATION)
|
.task_type(TaskType.IMPLEMENTATION)
|
||||||
.model("claude-sonnet-4-20250514")
|
.model("claude-sonnet-4-5-20250929")
|
||||||
.provider(Provider.ANTHROPIC)
|
.provider(Provider.ANTHROPIC)
|
||||||
.harness_type(Harness.AIDER)
|
.harness_type(Harness.CLAUDE_CODE)
|
||||||
.complexity_level(Complexity.MEDIUM)
|
.complexity_level(Complexity.MEDIUM)
|
||||||
.outcome_value(Outcome.SUCCESS)
|
.outcome_value(Outcome.SUCCESS)
|
||||||
.duration_ms(45000)
|
.duration_ms(45000)
|
||||||
.tokens(estimated_in=5000, estimated_out=2000, actual_in=5200, actual_out=1800)
|
.tokens(estimated_in=105000, estimated_out=45000, actual_in=112340, actual_out=38760)
|
||||||
.cost(estimated=50000, actual=48000)
|
.cost(estimated=630000, actual=919200) # Microdollars (1 USD = 1,000,000)
|
||||||
.quality(passed=True, gates_run=[QualityGate.LINT, QualityGate.TEST])
|
.quality(passed=True, gates_run=[QualityGate.BUILD, QualityGate.LINT, QualityGate.TEST])
|
||||||
.context(compactions=0, rotations=0, utilization=0.4)
|
.context(compactions=2, rotations=0, utilization=0.72)
|
||||||
.language("python")
|
.language("typescript")
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
|
|
||||||
client.track(event) # Non-blocking, thread-safe
|
client.track(event) # Non-blocking, thread-safe, never throws
|
||||||
|
|
||||||
# When done
|
|
||||||
client.stop() # Flushes remaining events
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
`track()` queues the event in memory. A background thread flushes batches to the server every 5 minutes (configurable). The context manager ensures all events are flushed on exit.
|
||||||
|
|
||||||
## Async Usage
|
## Async Usage
|
||||||
|
|
||||||
For asyncio-based applications:
|
For asyncio applications (FastAPI, aiohttp, etc.):
|
||||||
|
|
||||||
```python
|
```python
|
||||||
import asyncio
|
async with TelemetryClient(config) as client:
|
||||||
|
client.track(event) # track() is always synchronous
|
||||||
|
```
|
||||||
|
|
||||||
|
Or manually:
|
||||||
|
|
||||||
|
```python
|
||||||
|
client = TelemetryClient(config)
|
||||||
|
await client.start_async()
|
||||||
|
client.track(event)
|
||||||
|
await client.stop_async()
|
||||||
|
```
|
||||||
|
|
||||||
|
## FastAPI Integration
|
||||||
|
|
||||||
|
```python
|
||||||
|
from contextlib import asynccontextmanager
|
||||||
|
from fastapi import FastAPI
|
||||||
from mosaicstack_telemetry import TelemetryClient, TelemetryConfig
|
from mosaicstack_telemetry import TelemetryClient, TelemetryConfig
|
||||||
|
|
||||||
async def main():
|
config = TelemetryConfig() # From env vars
|
||||||
config = TelemetryConfig(
|
telemetry = TelemetryClient(config)
|
||||||
server_url="https://telemetry.mosaicstack.dev",
|
|
||||||
api_key="your-64-char-hex-api-key-here...",
|
|
||||||
instance_id="your-uuid-instance-id",
|
|
||||||
)
|
|
||||||
|
|
||||||
client = TelemetryClient(config)
|
@asynccontextmanager
|
||||||
await client.start_async() # Starts asyncio background task
|
async def lifespan(app: FastAPI):
|
||||||
|
await telemetry.start_async()
|
||||||
|
yield
|
||||||
|
await telemetry.stop_async()
|
||||||
|
|
||||||
# track() is always synchronous
|
app = FastAPI(lifespan=lifespan)
|
||||||
client.track(event)
|
|
||||||
|
|
||||||
await client.stop_async() # Flushes remaining events
|
@app.post("/tasks/complete")
|
||||||
|
async def complete_task():
|
||||||
asyncio.run(main())
|
# ... build event from request data ...
|
||||||
|
telemetry.track(event)
|
||||||
|
return {"status": "tracked"}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Context Manager
|
See the [Integration Guide](docs/integration-guide.md) for the full FastAPI example.
|
||||||
|
|
||||||
Both sync and async context managers are supported:
|
|
||||||
|
|
||||||
```python
|
|
||||||
# Sync
|
|
||||||
with TelemetryClient(config) as client:
|
|
||||||
client.track(event)
|
|
||||||
|
|
||||||
# Async
|
|
||||||
async with TelemetryClient(config) as client:
|
|
||||||
client.track(event)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration via Environment Variables
|
|
||||||
|
|
||||||
All core settings can be set via environment variables:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
export MOSAIC_TELEMETRY_ENABLED=true
|
|
||||||
export MOSAIC_TELEMETRY_SERVER_URL=https://telemetry.mosaicstack.dev
|
|
||||||
export MOSAIC_TELEMETRY_API_KEY=your-64-char-hex-api-key
|
|
||||||
export MOSAIC_TELEMETRY_INSTANCE_ID=your-uuid-instance-id
|
|
||||||
```
|
|
||||||
|
|
||||||
Then create a config with defaults:
|
|
||||||
|
|
||||||
```python
|
|
||||||
config = TelemetryConfig() # Picks up env vars automatically
|
|
||||||
```
|
|
||||||
|
|
||||||
Explicit constructor values take priority over environment variables.
|
|
||||||
|
|
||||||
## Querying Predictions
|
## Querying Predictions
|
||||||
|
|
||||||
@@ -128,53 +141,79 @@ from mosaicstack_telemetry import PredictionQuery, TaskType, Provider, Complexit
|
|||||||
|
|
||||||
query = PredictionQuery(
|
query = PredictionQuery(
|
||||||
task_type=TaskType.IMPLEMENTATION,
|
task_type=TaskType.IMPLEMENTATION,
|
||||||
model="claude-sonnet-4-20250514",
|
model="claude-sonnet-4-5-20250929",
|
||||||
provider=Provider.ANTHROPIC,
|
provider=Provider.ANTHROPIC,
|
||||||
complexity=Complexity.MEDIUM,
|
complexity=Complexity.MEDIUM,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Async
|
|
||||||
await client.refresh_predictions([query])
|
|
||||||
|
|
||||||
# Sync
|
# Sync
|
||||||
client.refresh_predictions_sync([query])
|
client.refresh_predictions_sync([query])
|
||||||
|
# Async
|
||||||
|
await client.refresh_predictions([query])
|
||||||
|
|
||||||
# Read from cache
|
# Read from cache
|
||||||
prediction = client.get_prediction(query)
|
prediction = client.get_prediction(query)
|
||||||
if prediction and prediction.prediction:
|
if prediction and prediction.prediction:
|
||||||
print(f"Expected input tokens (median): {prediction.prediction.input_tokens.median}")
|
print(f"Median input tokens: {prediction.prediction.input_tokens.median}")
|
||||||
print(f"Expected cost (median): ${prediction.prediction.cost_usd_micros['median'] / 1_000_000:.4f}")
|
print(f"Median cost: ${prediction.prediction.cost_usd_micros['median'] / 1_000_000:.4f}")
|
||||||
print(f"Quality gate pass rate: {prediction.prediction.quality.gate_pass_rate:.0%}")
|
print(f"Quality gate pass rate: {prediction.prediction.quality.gate_pass_rate:.0%}")
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Configuration Reference
|
||||||
|
|
||||||
|
| Parameter | Default | Env Var | Description |
|
||||||
|
|-----------|---------|---------|-------------|
|
||||||
|
| `server_url` | (required) | `MOSAIC_TELEMETRY_SERVER_URL` | Telemetry API base URL |
|
||||||
|
| `api_key` | (required) | `MOSAIC_TELEMETRY_API_KEY` | 64-character hex API key |
|
||||||
|
| `instance_id` | (required) | `MOSAIC_TELEMETRY_INSTANCE_ID` | UUID identifying this instance |
|
||||||
|
| `enabled` | `True` | `MOSAIC_TELEMETRY_ENABLED` | Enable/disable telemetry |
|
||||||
|
| `submit_interval_seconds` | `300.0` | -- | Background flush interval (seconds) |
|
||||||
|
| `max_queue_size` | `1000` | -- | Max events in memory queue |
|
||||||
|
| `batch_size` | `100` | -- | Events per batch (server max: 100) |
|
||||||
|
| `request_timeout_seconds` | `10.0` | -- | HTTP request timeout |
|
||||||
|
| `prediction_cache_ttl_seconds` | `21600.0` | -- | Prediction cache TTL (6 hours) |
|
||||||
|
| `dry_run` | `False` | -- | Log batches but don't send to server |
|
||||||
|
| `max_retries` | `3` | -- | Retries on transient failures |
|
||||||
|
|
||||||
|
Constructor values take priority over environment variables.
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
- `track()` **never throws** and **never blocks**. Errors are logged, not raised.
|
||||||
|
- When the queue is full, the oldest events are evicted.
|
||||||
|
- Failed submissions are retried with exponential backoff (honors `Retry-After` on 429).
|
||||||
|
- All logging uses the `mosaicstack_telemetry` logger.
|
||||||
|
|
||||||
## Dry-Run Mode
|
## Dry-Run Mode
|
||||||
|
|
||||||
Test your integration without sending data to the server:
|
Test your integration without sending data:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
config = TelemetryConfig(
|
config = TelemetryConfig(
|
||||||
server_url="https://telemetry.mosaicstack.dev",
|
server_url="https://tel-api.mosaicstack.dev",
|
||||||
api_key="a" * 64,
|
api_key="a" * 64,
|
||||||
instance_id="12345678-1234-1234-1234-123456789abc",
|
instance_id="12345678-1234-1234-1234-123456789abc",
|
||||||
dry_run=True, # Logs batches but doesn't send
|
dry_run=True, # Logs batches but doesn't send
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Configuration Reference
|
## Versioning
|
||||||
|
|
||||||
| Parameter | Default | Description |
|
This SDK publishes two types of versions to the Mosaic Stack PyPI registry:
|
||||||
|-----------|---------|-------------|
|
|
||||||
| `server_url` | (required) | Telemetry server base URL |
|
| Channel | Version Format | Example | Branch | Install |
|
||||||
| `api_key` | (required) | 64-character hex API key |
|
|---------|---------------|---------|--------|---------|
|
||||||
| `instance_id` | (required) | UUID identifying this instance |
|
| **Stable** | `X.Y.Z` | `0.1.0` | `main` / tags | `pip install mosaicstack-telemetry` |
|
||||||
| `enabled` | `True` | Enable/disable telemetry |
|
| **Dev** | `X.Y.Z.devYYYYMMDDHHMMSS` | `0.1.0.dev20260215045901` | `develop` | `pip install mosaicstack-telemetry --pre` |
|
||||||
| `submit_interval_seconds` | `300.0` | Background flush interval |
|
|
||||||
| `max_queue_size` | `1000` | Max events in memory queue |
|
Dev versions use a UTC timestamp suffix (PEP 440 format). This aligns with the JS client SDK which uses the equivalent semver format `X.Y.Z-dev.YYYYMMDDHHmmss`.
|
||||||
| `batch_size` | `100` | Events per batch (server max) |
|
|
||||||
| `request_timeout_seconds` | `10.0` | HTTP request timeout |
|
By default, `pip install` resolves only stable releases. Pass `--pre` to include dev versions.
|
||||||
| `prediction_cache_ttl_seconds` | `21600.0` | Prediction cache TTL (6h) |
|
|
||||||
| `dry_run` | `False` | Log but don't send |
|
## Documentation
|
||||||
| `max_retries` | `3` | Retries on failure |
|
|
||||||
|
- **[Integration Guide](docs/integration-guide.md)** -- Installation, configuration, FastAPI and generic Python examples, async vs sync patterns, prediction queries, error handling
|
||||||
|
- **[API Reference](docs/api-reference.md)** -- All exported classes, methods, types, and enums
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|||||||
496
docs/api-reference.md
Normal file
496
docs/api-reference.md
Normal file
@@ -0,0 +1,496 @@
|
|||||||
|
# SDK API Reference
|
||||||
|
|
||||||
|
Complete reference for all public classes, methods, types, and enums exported by `mosaicstack-telemetry`.
|
||||||
|
|
||||||
|
**SDK version:** 0.1.0 (stable: `0.1.0`, dev: `0.1.0.devYYYYMMDDHHMMSS`)
|
||||||
|
**Telemetry API version:** v1
|
||||||
|
**Event schema version:** 1.0
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## TelemetryClient
|
||||||
|
|
||||||
|
`mosaicstack_telemetry.TelemetryClient`
|
||||||
|
|
||||||
|
The main entry point for the SDK. Manages event queuing, background submission, and prediction caching.
|
||||||
|
|
||||||
|
### Constructor
|
||||||
|
|
||||||
|
```python
|
||||||
|
TelemetryClient(config: TelemetryConfig)
|
||||||
|
```
|
||||||
|
|
||||||
|
Validates the config on construction. If validation errors are found and telemetry is enabled, warnings are logged (but the client is still created).
|
||||||
|
|
||||||
|
### Methods
|
||||||
|
|
||||||
|
#### `start() -> None`
|
||||||
|
|
||||||
|
Start background event submission using a threading-based loop. Spawns a daemon thread that flushes the queue every `config.submit_interval_seconds`.
|
||||||
|
|
||||||
|
No-op if `config.enabled` is `False`.
|
||||||
|
|
||||||
|
#### `await start_async() -> None`
|
||||||
|
|
||||||
|
Start background event submission using an asyncio task. Creates an `asyncio.Task` that flushes the queue periodically.
|
||||||
|
|
||||||
|
No-op if `config.enabled` is `False`.
|
||||||
|
|
||||||
|
#### `stop() -> None`
|
||||||
|
|
||||||
|
Stop the sync background submitter. Performs a final flush of all remaining queued events before returning. Safe to call if not started.
|
||||||
|
|
||||||
|
#### `await stop_async() -> None`
|
||||||
|
|
||||||
|
Stop the async background submitter. Performs a final flush of all remaining queued events before returning. Safe to call if not started.
|
||||||
|
|
||||||
|
#### `track(event: TaskCompletionEvent) -> None`
|
||||||
|
|
||||||
|
Queue an event for submission. **Always synchronous.** **Never blocks.** **Never throws.**
|
||||||
|
|
||||||
|
If telemetry is disabled, the event is silently dropped. If the queue is full, the oldest event is evicted. Any unexpected error is caught and logged.
|
||||||
|
|
||||||
|
This method is thread-safe and can be called from any thread or coroutine.
|
||||||
|
|
||||||
|
#### `get_prediction(query: PredictionQuery) -> PredictionResponse | None`
|
||||||
|
|
||||||
|
Return a cached prediction for the given query, or `None` if not cached or expired.
|
||||||
|
|
||||||
|
#### `refresh_predictions_sync(queries: list[PredictionQuery]) -> None`
|
||||||
|
|
||||||
|
Fetch predictions from the server synchronously using `POST /v1/predictions/batch`. Results are stored in the internal prediction cache.
|
||||||
|
|
||||||
|
No-op if `queries` is empty.
|
||||||
|
|
||||||
|
#### `await refresh_predictions(queries: list[PredictionQuery]) -> None`
|
||||||
|
|
||||||
|
Fetch predictions from the server asynchronously using `POST /v1/predictions/batch`. Results are stored in the internal prediction cache.
|
||||||
|
|
||||||
|
No-op if `queries` is empty.
|
||||||
|
|
||||||
|
### Properties
|
||||||
|
|
||||||
|
#### `queue_size: int`
|
||||||
|
|
||||||
|
Number of events currently in the in-memory queue.
|
||||||
|
|
||||||
|
#### `is_running: bool`
|
||||||
|
|
||||||
|
Whether the background submitter (sync or async) is currently active.
|
||||||
|
|
||||||
|
### Context Managers
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Sync: calls start() on entry, stop() on exit
|
||||||
|
with TelemetryClient(config) as client:
|
||||||
|
client.track(event)
|
||||||
|
|
||||||
|
# Async: calls start_async() on entry, stop_async() on exit
|
||||||
|
async with TelemetryClient(config) as client:
|
||||||
|
client.track(event)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## TelemetryConfig
|
||||||
|
|
||||||
|
`mosaicstack_telemetry.TelemetryConfig`
|
||||||
|
|
||||||
|
A dataclass holding all configuration for the telemetry client. Supports environment variable overrides.
|
||||||
|
|
||||||
|
### Fields
|
||||||
|
|
||||||
|
| Field | Type | Default | Description |
|
||||||
|
|-------|------|---------|-------------|
|
||||||
|
| `server_url` | `str` | `""` | Telemetry API base URL. Trailing slashes are stripped. |
|
||||||
|
| `api_key` | `str` | `""` | 64-character hex API key for Bearer token auth. |
|
||||||
|
| `instance_id` | `str` | `""` | UUID string identifying this Mosaic Stack instance. |
|
||||||
|
| `enabled` | `bool` | `True` | Master switch for telemetry. When `False`, `track()` is a no-op and no background threads/tasks are created. |
|
||||||
|
| `submit_interval_seconds` | `float` | `300.0` | Interval between background queue flushes (seconds). |
|
||||||
|
| `max_queue_size` | `int` | `1000` | Maximum events in the in-memory queue. Older events are evicted when full. |
|
||||||
|
| `batch_size` | `int` | `100` | Events per HTTP batch request. Server maximum is 100. |
|
||||||
|
| `request_timeout_seconds` | `float` | `10.0` | HTTP request timeout (seconds). |
|
||||||
|
| `prediction_cache_ttl_seconds` | `float` | `21600.0` | Prediction cache time-to-live (seconds). Default: 6 hours. |
|
||||||
|
| `dry_run` | `bool` | `False` | When `True`, batches are logged but not sent to the server. |
|
||||||
|
| `max_retries` | `int` | `3` | Maximum retry attempts for transient failures. |
|
||||||
|
| `user_agent` | `str` | `"mosaicstack-telemetry-python/0.1.0"` | User-Agent header sent with all requests. |
|
||||||
|
|
||||||
|
### Environment Variables
|
||||||
|
|
||||||
|
These are read during `__post_init__` and only apply when the corresponding constructor field is empty/default:
|
||||||
|
|
||||||
|
| Env Var | Field | Notes |
|
||||||
|
|---------|-------|-------|
|
||||||
|
| `MOSAIC_TELEMETRY_ENABLED` | `enabled` | Accepts `1`, `true`, `yes` (case-insensitive) as truthy. Always overrides the constructor value. |
|
||||||
|
| `MOSAIC_TELEMETRY_SERVER_URL` | `server_url` | Only used if `server_url` is not set in the constructor. |
|
||||||
|
| `MOSAIC_TELEMETRY_API_KEY` | `api_key` | Only used if `api_key` is not set in the constructor. |
|
||||||
|
| `MOSAIC_TELEMETRY_INSTANCE_ID` | `instance_id` | Only used if `instance_id` is not set in the constructor. |
|
||||||
|
|
||||||
|
### Methods
|
||||||
|
|
||||||
|
#### `validate() -> list[str]`
|
||||||
|
|
||||||
|
Validate the configuration and return a list of error messages. Returns an empty list if valid.
|
||||||
|
|
||||||
|
Checks performed:
|
||||||
|
- `server_url` is non-empty and starts with `http://` or `https://`
|
||||||
|
- `api_key` is a 64-character hex string
|
||||||
|
- `instance_id` is a valid UUID string
|
||||||
|
- `submit_interval_seconds` is positive
|
||||||
|
- `max_queue_size` is positive
|
||||||
|
- `batch_size` is between 1 and 100
|
||||||
|
- `request_timeout_seconds` is positive
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## EventBuilder
|
||||||
|
|
||||||
|
`mosaicstack_telemetry.EventBuilder`
|
||||||
|
|
||||||
|
Fluent builder for constructing `TaskCompletionEvent` instances with a chainable API and sensible defaults.
|
||||||
|
|
||||||
|
### Constructor
|
||||||
|
|
||||||
|
```python
|
||||||
|
EventBuilder(instance_id: str | UUID)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Setter Methods
|
||||||
|
|
||||||
|
All setter methods return `self` for chaining.
|
||||||
|
|
||||||
|
| Method | Parameter Type | Sets Field | Default |
|
||||||
|
|--------|---------------|-----------|---------|
|
||||||
|
| `.event_id(value)` | `str \| UUID` | `event_id` | Auto-generated UUIDv4 |
|
||||||
|
| `.timestamp(value)` | `datetime` | `timestamp` | `datetime.now(UTC)` |
|
||||||
|
| `.task_type(value)` | `TaskType` | `task_type` | `TaskType.UNKNOWN` |
|
||||||
|
| `.complexity_level(value)` | `Complexity` | `complexity` | `Complexity.MEDIUM` |
|
||||||
|
| `.harness_type(value)` | `Harness` | `harness` | `Harness.UNKNOWN` |
|
||||||
|
| `.model(value)` | `str` | `model` | `"unknown"` |
|
||||||
|
| `.provider(value)` | `Provider` | `provider` | `Provider.UNKNOWN` |
|
||||||
|
| `.duration_ms(value)` | `int` | `task_duration_ms` | `0` |
|
||||||
|
| `.outcome_value(value)` | `Outcome` | `outcome` | `Outcome.FAILURE` |
|
||||||
|
| `.retry_count(value)` | `int` | `retry_count` | `0` |
|
||||||
|
| `.language(value)` | `str \| None` | `language` | `None` |
|
||||||
|
| `.repo_size(value)` | `RepoSizeCategory \| None` | `repo_size_category` | `None` |
|
||||||
|
|
||||||
|
#### `.tokens(*, estimated_in, estimated_out, actual_in, actual_out) -> EventBuilder`
|
||||||
|
|
||||||
|
Set all four token count fields. All parameters are keyword-only integers (default `0`).
|
||||||
|
|
||||||
|
#### `.cost(*, estimated, actual) -> EventBuilder`
|
||||||
|
|
||||||
|
Set estimated and actual cost in microdollars. Both are keyword-only integers (default `0`).
|
||||||
|
|
||||||
|
#### `.quality(*, passed, gates_run=None, gates_failed=None) -> EventBuilder`
|
||||||
|
|
||||||
|
Set quality gate results. `passed` is required. `gates_run` and `gates_failed` are optional lists of `QualityGate` values.
|
||||||
|
|
||||||
|
#### `.context(*, compactions=0, rotations=0, utilization=0.0) -> EventBuilder`
|
||||||
|
|
||||||
|
Set context window metrics. All parameters are keyword-only with defaults.
|
||||||
|
|
||||||
|
#### `.build() -> TaskCompletionEvent`
|
||||||
|
|
||||||
|
Construct and return the `TaskCompletionEvent`. The builder can be reused after calling `.build()`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## TaskCompletionEvent
|
||||||
|
|
||||||
|
`mosaicstack_telemetry.TaskCompletionEvent`
|
||||||
|
|
||||||
|
Pydantic model representing a single telemetry event. Matches the server's v1 event schema (version 1.0).
|
||||||
|
|
||||||
|
### Fields
|
||||||
|
|
||||||
|
| Field | Type | Required | Constraints | Description |
|
||||||
|
|-------|------|----------|-------------|-------------|
|
||||||
|
| `instance_id` | `UUID` | Yes | Valid UUID | Mosaic Stack installation identifier |
|
||||||
|
| `event_id` | `UUID` | No | Valid UUID | Unique event identifier (default: auto-generated) |
|
||||||
|
| `schema_version` | `str` | No | -- | Event schema version (default: `"1.0"`) |
|
||||||
|
| `timestamp` | `datetime` | No | -- | When the task completed (default: now UTC) |
|
||||||
|
| `task_duration_ms` | `int` | Yes | 0--86,400,000 | Task wall-clock time in milliseconds |
|
||||||
|
| `task_type` | `TaskType` | Yes | Enum value | Type of work performed |
|
||||||
|
| `complexity` | `Complexity` | Yes | Enum value | Task complexity level |
|
||||||
|
| `harness` | `Harness` | Yes | Enum value | Coding tool / execution environment |
|
||||||
|
| `model` | `str` | Yes | 1--100 chars | Model identifier |
|
||||||
|
| `provider` | `Provider` | Yes | Enum value | LLM provider |
|
||||||
|
| `estimated_input_tokens` | `int` | Yes | 0--10,000,000 | Pre-task input token estimate |
|
||||||
|
| `estimated_output_tokens` | `int` | Yes | 0--10,000,000 | Pre-task output token estimate |
|
||||||
|
| `actual_input_tokens` | `int` | Yes | 0--10,000,000 | Actual input tokens consumed |
|
||||||
|
| `actual_output_tokens` | `int` | Yes | 0--10,000,000 | Actual output tokens generated |
|
||||||
|
| `estimated_cost_usd_micros` | `int` | Yes | 0--100,000,000 | Estimated cost in microdollars |
|
||||||
|
| `actual_cost_usd_micros` | `int` | Yes | 0--100,000,000 | Actual cost in microdollars |
|
||||||
|
| `quality_gate_passed` | `bool` | Yes | -- | Whether all quality gates passed |
|
||||||
|
| `quality_gates_run` | `list[QualityGate]` | No | -- | Gates executed (default: `[]`) |
|
||||||
|
| `quality_gates_failed` | `list[QualityGate]` | No | -- | Gates that failed (default: `[]`) |
|
||||||
|
| `context_compactions` | `int` | Yes | 0--100 | Context compaction events during task |
|
||||||
|
| `context_rotations` | `int` | Yes | 0--50 | Agent session rotations during task |
|
||||||
|
| `context_utilization_final` | `float` | Yes | 0.0--1.0 | Final context usage ratio |
|
||||||
|
| `outcome` | `Outcome` | Yes | Enum value | Final task result |
|
||||||
|
| `retry_count` | `int` | Yes | 0--20 | Retries before final outcome |
|
||||||
|
| `language` | `str \| None` | No | Max 30 chars | Primary programming language |
|
||||||
|
| `repo_size_category` | `RepoSizeCategory \| None` | No | Enum value | Repository size bucket |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prediction Types
|
||||||
|
|
||||||
|
### PredictionQuery
|
||||||
|
|
||||||
|
`mosaicstack_telemetry.PredictionQuery`
|
||||||
|
|
||||||
|
Query parameters for fetching a prediction.
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `task_type` | `TaskType` | Task type to predict for |
|
||||||
|
| `model` | `str` | Model identifier |
|
||||||
|
| `provider` | `Provider` | LLM provider |
|
||||||
|
| `complexity` | `Complexity` | Complexity level |
|
||||||
|
|
||||||
|
### PredictionResponse
|
||||||
|
|
||||||
|
`mosaicstack_telemetry.PredictionResponse`
|
||||||
|
|
||||||
|
Response from the prediction endpoint.
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `prediction` | `PredictionData \| None` | Prediction data, or `None` if no data is available |
|
||||||
|
| `metadata` | `PredictionMetadata` | Metadata about how the prediction was generated |
|
||||||
|
|
||||||
|
### PredictionData
|
||||||
|
|
||||||
|
`mosaicstack_telemetry.PredictionData`
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `input_tokens` | `TokenDistribution` | Input token distribution (p10/p25/median/p75/p90) |
|
||||||
|
| `output_tokens` | `TokenDistribution` | Output token distribution (p10/p25/median/p75/p90) |
|
||||||
|
| `cost_usd_micros` | `dict[str, int]` | `{"median": <value>}` -- median cost in microdollars |
|
||||||
|
| `duration_ms` | `dict[str, int]` | `{"median": <value>}` -- median duration in milliseconds |
|
||||||
|
| `correction_factors` | `CorrectionFactors` | Actual-to-estimated token ratios |
|
||||||
|
| `quality` | `QualityPrediction` | Gate pass rate and success rate |
|
||||||
|
|
||||||
|
### TokenDistribution
|
||||||
|
|
||||||
|
`mosaicstack_telemetry.TokenDistribution`
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `p10` | `int` | 10th percentile |
|
||||||
|
| `p25` | `int` | 25th percentile |
|
||||||
|
| `median` | `int` | 50th percentile (median) |
|
||||||
|
| `p75` | `int` | 75th percentile |
|
||||||
|
| `p90` | `int` | 90th percentile |
|
||||||
|
|
||||||
|
### CorrectionFactors
|
||||||
|
|
||||||
|
`mosaicstack_telemetry.CorrectionFactors`
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `input` | `float` | Ratio of actual to estimated input tokens (>1.0 = estimates too low) |
|
||||||
|
| `output` | `float` | Ratio of actual to estimated output tokens |
|
||||||
|
|
||||||
|
### QualityPrediction
|
||||||
|
|
||||||
|
`mosaicstack_telemetry.QualityPrediction`
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `gate_pass_rate` | `float` | Fraction of tasks where all quality gates passed |
|
||||||
|
| `success_rate` | `float` | Fraction of tasks with `outcome: success` |
|
||||||
|
|
||||||
|
### PredictionMetadata
|
||||||
|
|
||||||
|
`mosaicstack_telemetry.PredictionMetadata`
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `sample_size` | `int` | Number of events used to compute the prediction |
|
||||||
|
| `fallback_level` | `int` | `0` = exact match, `1+` = dimensions dropped, `-1` = no data |
|
||||||
|
| `confidence` | `str` | `"high"`, `"medium"`, `"low"`, or `"none"` |
|
||||||
|
| `last_updated` | `datetime \| None` | When the prediction was last computed |
|
||||||
|
| `dimensions_matched` | `dict[str, str \| None] \| None` | Which dimensions matched (`None` values = fallback) |
|
||||||
|
| `fallback_note` | `str \| None` | Explanation when fallback was used |
|
||||||
|
| `cache_hit` | `bool` | Whether the server served from its cache |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Batch Types
|
||||||
|
|
||||||
|
### BatchEventRequest
|
||||||
|
|
||||||
|
`mosaicstack_telemetry.BatchEventRequest`
|
||||||
|
|
||||||
|
Request body for `POST /v1/events/batch`. Used internally by the submitter.
|
||||||
|
|
||||||
|
| Field | Type | Constraints | Description |
|
||||||
|
|-------|------|-------------|-------------|
|
||||||
|
| `events` | `list[TaskCompletionEvent]` | 1--100 items | Events to submit |
|
||||||
|
|
||||||
|
### BatchEventResponse
|
||||||
|
|
||||||
|
`mosaicstack_telemetry.BatchEventResponse`
|
||||||
|
|
||||||
|
Response from the batch event endpoint.
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `accepted` | `int` | Count of accepted events |
|
||||||
|
| `rejected` | `int` | Count of rejected events |
|
||||||
|
| `results` | `list[BatchEventResult]` | Per-event results |
|
||||||
|
|
||||||
|
### BatchEventResult
|
||||||
|
|
||||||
|
`mosaicstack_telemetry.BatchEventResult`
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `event_id` | `UUID` | The event's unique identifier |
|
||||||
|
| `status` | `str` | `"accepted"` or `"rejected"` |
|
||||||
|
| `error` | `str \| None` | Error message if rejected |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Enumerations
|
||||||
|
|
||||||
|
All enums use `str, Enum` mixin (Python 3.10 compatible). Their `.value` is the lowercase string sent to the server.
|
||||||
|
|
||||||
|
### TaskType
|
||||||
|
|
||||||
|
`mosaicstack_telemetry.TaskType`
|
||||||
|
|
||||||
|
| Member | Value | Description |
|
||||||
|
|--------|-------|-------------|
|
||||||
|
| `PLANNING` | `"planning"` | Architecture design, task breakdown |
|
||||||
|
| `IMPLEMENTATION` | `"implementation"` | Writing new code |
|
||||||
|
| `CODE_REVIEW` | `"code_review"` | Reviewing existing code |
|
||||||
|
| `TESTING` | `"testing"` | Writing or running tests |
|
||||||
|
| `DEBUGGING` | `"debugging"` | Investigating and fixing bugs |
|
||||||
|
| `REFACTORING` | `"refactoring"` | Restructuring existing code |
|
||||||
|
| `DOCUMENTATION` | `"documentation"` | Writing docs, comments, READMEs |
|
||||||
|
| `CONFIGURATION` | `"configuration"` | Config files, CI/CD, infrastructure |
|
||||||
|
| `SECURITY_AUDIT` | `"security_audit"` | Security review, vulnerability analysis |
|
||||||
|
| `UNKNOWN` | `"unknown"` | Unclassified task type (fallback) |
|
||||||
|
|
||||||
|
### Complexity
|
||||||
|
|
||||||
|
`mosaicstack_telemetry.Complexity`
|
||||||
|
|
||||||
|
| Member | Value | Description |
|
||||||
|
|--------|-------|-------------|
|
||||||
|
| `LOW` | `"low"` | Simple fixes, typos, config changes |
|
||||||
|
| `MEDIUM` | `"medium"` | Standard features, moderate logic |
|
||||||
|
| `HIGH` | `"high"` | Complex features, multi-file changes |
|
||||||
|
| `CRITICAL` | `"critical"` | Major refactoring, architectural changes |
|
||||||
|
|
||||||
|
### Harness
|
||||||
|
|
||||||
|
`mosaicstack_telemetry.Harness`
|
||||||
|
|
||||||
|
| Member | Value | Description |
|
||||||
|
|--------|-------|-------------|
|
||||||
|
| `CLAUDE_CODE` | `"claude_code"` | Anthropic Claude Code CLI |
|
||||||
|
| `OPENCODE` | `"opencode"` | OpenCode CLI |
|
||||||
|
| `KILO_CODE` | `"kilo_code"` | Kilo Code VS Code extension |
|
||||||
|
| `AIDER` | `"aider"` | Aider AI pair programming |
|
||||||
|
| `API_DIRECT` | `"api_direct"` | Direct API calls (no harness) |
|
||||||
|
| `OLLAMA_LOCAL` | `"ollama_local"` | Ollama local inference |
|
||||||
|
| `CUSTOM` | `"custom"` | Custom or unrecognized harness |
|
||||||
|
| `UNKNOWN` | `"unknown"` | Harness not reported |
|
||||||
|
|
||||||
|
### Provider
|
||||||
|
|
||||||
|
`mosaicstack_telemetry.Provider`
|
||||||
|
|
||||||
|
| Member | Value | Description |
|
||||||
|
|--------|-------|-------------|
|
||||||
|
| `ANTHROPIC` | `"anthropic"` | Anthropic (Claude models) |
|
||||||
|
| `OPENAI` | `"openai"` | OpenAI (GPT models) |
|
||||||
|
| `OPENROUTER` | `"openrouter"` | OpenRouter (multi-provider routing) |
|
||||||
|
| `OLLAMA` | `"ollama"` | Ollama (local/self-hosted) |
|
||||||
|
| `GOOGLE` | `"google"` | Google (Gemini models) |
|
||||||
|
| `MISTRAL` | `"mistral"` | Mistral AI |
|
||||||
|
| `CUSTOM` | `"custom"` | Custom or unrecognized provider |
|
||||||
|
| `UNKNOWN` | `"unknown"` | Provider not reported |
|
||||||
|
|
||||||
|
### Outcome
|
||||||
|
|
||||||
|
`mosaicstack_telemetry.Outcome`
|
||||||
|
|
||||||
|
| Member | Value | Description |
|
||||||
|
|--------|-------|-------------|
|
||||||
|
| `SUCCESS` | `"success"` | Task completed, all quality gates passed |
|
||||||
|
| `FAILURE` | `"failure"` | Task failed after all retries |
|
||||||
|
| `PARTIAL` | `"partial"` | Task partially completed |
|
||||||
|
| `TIMEOUT` | `"timeout"` | Task exceeded time or token budget |
|
||||||
|
|
||||||
|
### QualityGate
|
||||||
|
|
||||||
|
`mosaicstack_telemetry.QualityGate`
|
||||||
|
|
||||||
|
| Member | Value | Description |
|
||||||
|
|--------|-------|-------------|
|
||||||
|
| `BUILD` | `"build"` | Code compiles/builds |
|
||||||
|
| `LINT` | `"lint"` | Linter passes |
|
||||||
|
| `TEST` | `"test"` | Tests pass |
|
||||||
|
| `COVERAGE` | `"coverage"` | Coverage meets threshold |
|
||||||
|
| `TYPECHECK` | `"typecheck"` | Type checker passes |
|
||||||
|
| `SECURITY` | `"security"` | Security scan passes |
|
||||||
|
|
||||||
|
### RepoSizeCategory
|
||||||
|
|
||||||
|
`mosaicstack_telemetry.RepoSizeCategory`
|
||||||
|
|
||||||
|
| Member | Value | Approximate LOC | Description |
|
||||||
|
|--------|-------|----------------|-------------|
|
||||||
|
| `TINY` | `"tiny"` | < 1,000 | Scripts, single-file projects |
|
||||||
|
| `SMALL` | `"small"` | 1,000--10,000 | Small libraries, tools |
|
||||||
|
| `MEDIUM` | `"medium"` | 10,000--100,000 | Standard applications |
|
||||||
|
| `LARGE` | `"large"` | 100,000--1,000,000 | Large applications, monorepos |
|
||||||
|
| `HUGE` | `"huge"` | > 1,000,000 | Enterprise codebases |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Exceptions
|
||||||
|
|
||||||
|
### TelemetryError
|
||||||
|
|
||||||
|
`mosaicstack_telemetry.TelemetryError`
|
||||||
|
|
||||||
|
Base exception for telemetry client errors. Extends `Exception`. Currently unused by the public API (since `track()` never throws), but available for custom error handling in integrations.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Internal Components
|
||||||
|
|
||||||
|
These are exported for advanced use cases but are managed automatically by `TelemetryClient`.
|
||||||
|
|
||||||
|
### EventQueue
|
||||||
|
|
||||||
|
`mosaicstack_telemetry.EventQueue`
|
||||||
|
|
||||||
|
Thread-safe bounded FIFO queue. When full, oldest events are evicted.
|
||||||
|
|
||||||
|
- `EventQueue(max_size: int = 1000)`
|
||||||
|
- `put(event: TaskCompletionEvent) -> None` -- Add event, evict oldest if full
|
||||||
|
- `drain(max_items: int) -> list[TaskCompletionEvent]` -- Remove and return up to N events
|
||||||
|
- `put_back(events: list[TaskCompletionEvent]) -> None` -- Re-queue events at the front (for retries)
|
||||||
|
- `size: int` -- Current queue length
|
||||||
|
- `is_empty: bool` -- Whether the queue is empty
|
||||||
|
|
||||||
|
### PredictionCache
|
||||||
|
|
||||||
|
`mosaicstack_telemetry.PredictionCache`
|
||||||
|
|
||||||
|
Thread-safe dict-based cache with TTL expiration.
|
||||||
|
|
||||||
|
- `PredictionCache(ttl_seconds: float = 21600.0)`
|
||||||
|
- `get(query: PredictionQuery) -> PredictionResponse | None` -- Get cached prediction
|
||||||
|
- `put(query: PredictionQuery, response: PredictionResponse) -> None` -- Store prediction
|
||||||
|
- `clear() -> None` -- Invalidate all entries
|
||||||
|
- `size: int` -- Number of entries (including possibly expired)
|
||||||
657
docs/integration-guide.md
Normal file
657
docs/integration-guide.md
Normal file
@@ -0,0 +1,657 @@
|
|||||||
|
# Integration Guide
|
||||||
|
|
||||||
|
This guide covers installing and integrating `mosaicstack-telemetry` into Python applications. The SDK reports AI coding task-completion telemetry to a [Mosaic Stack Telemetry](https://github.com/mosaicstack/telemetry) server and queries crowd-sourced predictions.
|
||||||
|
|
||||||
|
**Telemetry API version:** This SDK targets the Mosaic Telemetry API **v1** with event schema version **1.0**.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Install from the Mosaic Stack package registry:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install mosaicstack-telemetry --index-url https://git.mosaicstack.dev/api/packages/mosaic/pypi/simple/
|
||||||
|
```
|
||||||
|
|
||||||
|
Or with [uv](https://docs.astral.sh/uv/):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uv add mosaicstack-telemetry --index-url https://git.mosaicstack.dev/api/packages/mosaic/pypi/simple/
|
||||||
|
```
|
||||||
|
|
||||||
|
To avoid repeating the index URL, configure it in your project's `pyproject.toml`:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[[tool.uv.index]]
|
||||||
|
name = "mosaic"
|
||||||
|
url = "https://git.mosaicstack.dev/api/packages/mosaic/pypi/simple/"
|
||||||
|
```
|
||||||
|
|
||||||
|
Or in `pip.conf` (`~/.config/pip/pip.conf` on Linux):
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[global]
|
||||||
|
extra-index-url = https://git.mosaicstack.dev/api/packages/mosaic/pypi/simple/
|
||||||
|
```
|
||||||
|
|
||||||
|
**Requirements:** Python 3.10+. Runtime dependencies: `httpx` and `pydantic`.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
### Constructor Parameters
|
||||||
|
|
||||||
|
```python
|
||||||
|
from mosaicstack_telemetry import TelemetryConfig
|
||||||
|
|
||||||
|
config = TelemetryConfig(
|
||||||
|
server_url="https://tel-api.mosaicstack.dev",
|
||||||
|
api_key="your-64-char-hex-api-key-here...",
|
||||||
|
instance_id="a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
All three fields (`server_url`, `api_key`, `instance_id`) are required when telemetry is enabled. The `api_key` must be a 64-character hexadecimal string issued by a Mosaic Telemetry administrator. The `instance_id` is a UUID that identifies your Mosaic Stack installation and must match the instance associated with your API key on the server.
|
||||||
|
|
||||||
|
### Environment Variables
|
||||||
|
|
||||||
|
Instead of passing values to the constructor, set environment variables:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export MOSAIC_TELEMETRY_ENABLED=true
|
||||||
|
export MOSAIC_TELEMETRY_SERVER_URL=https://tel-api.mosaicstack.dev
|
||||||
|
export MOSAIC_TELEMETRY_API_KEY=your-64-char-hex-api-key
|
||||||
|
export MOSAIC_TELEMETRY_INSTANCE_ID=a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d
|
||||||
|
```
|
||||||
|
|
||||||
|
Then create the config with no arguments:
|
||||||
|
|
||||||
|
```python
|
||||||
|
config = TelemetryConfig() # Reads from environment
|
||||||
|
```
|
||||||
|
|
||||||
|
Constructor values take priority over environment variables.
|
||||||
|
|
||||||
|
### Full Configuration Reference
|
||||||
|
|
||||||
|
| Parameter | Type | Default | Env Var | Description |
|
||||||
|
|-----------|------|---------|---------|-------------|
|
||||||
|
| `server_url` | `str` | `""` (required) | `MOSAIC_TELEMETRY_SERVER_URL` | Telemetry API base URL (no trailing slash) |
|
||||||
|
| `api_key` | `str` | `""` (required) | `MOSAIC_TELEMETRY_API_KEY` | 64-character hex API key |
|
||||||
|
| `instance_id` | `str` | `""` (required) | `MOSAIC_TELEMETRY_INSTANCE_ID` | UUID identifying this Mosaic Stack instance |
|
||||||
|
| `enabled` | `bool` | `True` | `MOSAIC_TELEMETRY_ENABLED` | Enable/disable telemetry entirely |
|
||||||
|
| `submit_interval_seconds` | `float` | `300.0` | -- | How often the background submitter flushes queued events (seconds) |
|
||||||
|
| `max_queue_size` | `int` | `1000` | -- | Maximum events held in the in-memory queue |
|
||||||
|
| `batch_size` | `int` | `100` | -- | Events per batch (server maximum is 100) |
|
||||||
|
| `request_timeout_seconds` | `float` | `10.0` | -- | HTTP request timeout for API calls |
|
||||||
|
| `prediction_cache_ttl_seconds` | `float` | `21600.0` | -- | Prediction cache TTL (default 6 hours) |
|
||||||
|
| `dry_run` | `bool` | `False` | -- | Log batches but don't send to server |
|
||||||
|
| `max_retries` | `int` | `3` | -- | Retries on transient failures (429, timeouts, network errors) |
|
||||||
|
|
||||||
|
### Environment-Specific Configuration
|
||||||
|
|
||||||
|
**Development:**
|
||||||
|
|
||||||
|
```python
|
||||||
|
config = TelemetryConfig(
|
||||||
|
server_url="http://localhost:8000",
|
||||||
|
api_key="a" * 64,
|
||||||
|
instance_id="12345678-1234-1234-1234-123456789abc",
|
||||||
|
dry_run=True, # Log but don't send
|
||||||
|
submit_interval_seconds=10.0, # Flush quickly for testing
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Production:**
|
||||||
|
|
||||||
|
```python
|
||||||
|
config = TelemetryConfig(
|
||||||
|
server_url="https://tel-api.mosaicstack.dev",
|
||||||
|
api_key=os.environ["MOSAIC_TELEMETRY_API_KEY"],
|
||||||
|
instance_id=os.environ["MOSAIC_TELEMETRY_INSTANCE_ID"],
|
||||||
|
submit_interval_seconds=300.0, # Default: flush every 5 minutes
|
||||||
|
max_retries=3, # Retry transient failures
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Sync Usage (Threading)
|
||||||
|
|
||||||
|
Best for scripts, CLI tools, aider integrations, and non-async contexts. The SDK spawns a daemon thread that periodically flushes queued events.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from mosaicstack_telemetry import (
|
||||||
|
TelemetryClient,
|
||||||
|
TelemetryConfig,
|
||||||
|
EventBuilder,
|
||||||
|
TaskType,
|
||||||
|
Provider,
|
||||||
|
Harness,
|
||||||
|
Complexity,
|
||||||
|
Outcome,
|
||||||
|
QualityGate,
|
||||||
|
)
|
||||||
|
|
||||||
|
config = TelemetryConfig(
|
||||||
|
server_url="https://tel-api.mosaicstack.dev",
|
||||||
|
api_key="your-64-char-hex-api-key-here...",
|
||||||
|
instance_id="a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
|
||||||
|
)
|
||||||
|
|
||||||
|
client = TelemetryClient(config)
|
||||||
|
client.start() # Starts background daemon thread
|
||||||
|
|
||||||
|
event = (
|
||||||
|
EventBuilder(instance_id=config.instance_id)
|
||||||
|
.task_type(TaskType.IMPLEMENTATION)
|
||||||
|
.model("claude-sonnet-4-5-20250929")
|
||||||
|
.provider(Provider.ANTHROPIC)
|
||||||
|
.harness_type(Harness.CLAUDE_CODE)
|
||||||
|
.complexity_level(Complexity.MEDIUM)
|
||||||
|
.outcome_value(Outcome.SUCCESS)
|
||||||
|
.duration_ms(45000)
|
||||||
|
.tokens(estimated_in=105000, estimated_out=45000, actual_in=112340, actual_out=38760)
|
||||||
|
.cost(estimated=630000, actual=919200)
|
||||||
|
.quality(
|
||||||
|
passed=True,
|
||||||
|
gates_run=[QualityGate.BUILD, QualityGate.LINT, QualityGate.TEST, QualityGate.TYPECHECK],
|
||||||
|
)
|
||||||
|
.context(compactions=2, rotations=0, utilization=0.72)
|
||||||
|
.language("typescript")
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
|
||||||
|
client.track(event) # Non-blocking, thread-safe
|
||||||
|
|
||||||
|
client.stop() # Flushes remaining events and stops the thread
|
||||||
|
```
|
||||||
|
|
||||||
|
### Sync Context Manager
|
||||||
|
|
||||||
|
The context manager calls `start()` on entry and `stop()` (with flush) on exit:
|
||||||
|
|
||||||
|
```python
|
||||||
|
with TelemetryClient(config) as client:
|
||||||
|
client.track(event)
|
||||||
|
# Automatically flushed and stopped here
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Async Usage (asyncio)
|
||||||
|
|
||||||
|
For asyncio-based applications (FastAPI, aiohttp, etc.). The SDK creates an asyncio task that periodically flushes events.
|
||||||
|
|
||||||
|
```python
|
||||||
|
import asyncio
|
||||||
|
from mosaicstack_telemetry import TelemetryClient, TelemetryConfig
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
config = TelemetryConfig(
|
||||||
|
server_url="https://tel-api.mosaicstack.dev",
|
||||||
|
api_key="your-64-char-hex-api-key-here...",
|
||||||
|
instance_id="a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
|
||||||
|
)
|
||||||
|
|
||||||
|
client = TelemetryClient(config)
|
||||||
|
await client.start_async() # Starts asyncio background task
|
||||||
|
|
||||||
|
# track() is always synchronous and non-blocking
|
||||||
|
client.track(event)
|
||||||
|
|
||||||
|
await client.stop_async() # Flushes remaining events
|
||||||
|
|
||||||
|
asyncio.run(main())
|
||||||
|
```
|
||||||
|
|
||||||
|
### Async Context Manager
|
||||||
|
|
||||||
|
```python
|
||||||
|
async with TelemetryClient(config) as client:
|
||||||
|
client.track(event)
|
||||||
|
# Automatically flushed and stopped here
|
||||||
|
```
|
||||||
|
|
||||||
|
### Key Difference: Sync vs Async
|
||||||
|
|
||||||
|
| Aspect | Sync | Async |
|
||||||
|
|--------|------|-------|
|
||||||
|
| Start | `client.start()` | `await client.start_async()` |
|
||||||
|
| Stop | `client.stop()` | `await client.stop_async()` |
|
||||||
|
| Context manager | `with TelemetryClient(config)` | `async with TelemetryClient(config)` |
|
||||||
|
| Background mechanism | `threading.Timer` (daemon thread) | `asyncio.Task` |
|
||||||
|
| `track()` | Always synchronous | Always synchronous |
|
||||||
|
| `refresh_predictions` | `refresh_predictions_sync(queries)` | `await refresh_predictions(queries)` |
|
||||||
|
|
||||||
|
The `track()` method is always synchronous regardless of which mode you use. It simply appends to a thread-safe in-memory queue and returns immediately. The background submitter handles batching and sending.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Integration Examples
|
||||||
|
|
||||||
|
### Example 1: Instrumenting a FastAPI Service
|
||||||
|
|
||||||
|
```python
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
from contextlib import asynccontextmanager
|
||||||
|
from uuid import UUID
|
||||||
|
|
||||||
|
from fastapi import FastAPI
|
||||||
|
|
||||||
|
from mosaicstack_telemetry import (
|
||||||
|
Complexity,
|
||||||
|
EventBuilder,
|
||||||
|
Harness,
|
||||||
|
Outcome,
|
||||||
|
Provider,
|
||||||
|
QualityGate,
|
||||||
|
TaskType,
|
||||||
|
TelemetryClient,
|
||||||
|
TelemetryConfig,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Initialize telemetry once at startup
|
||||||
|
config = TelemetryConfig(
|
||||||
|
server_url=os.environ.get("MOSAIC_TELEMETRY_SERVER_URL", "https://tel-api.mosaicstack.dev"),
|
||||||
|
api_key=os.environ["MOSAIC_TELEMETRY_API_KEY"],
|
||||||
|
instance_id=os.environ["MOSAIC_TELEMETRY_INSTANCE_ID"],
|
||||||
|
)
|
||||||
|
|
||||||
|
telemetry = TelemetryClient(config)
|
||||||
|
|
||||||
|
|
||||||
|
@asynccontextmanager
|
||||||
|
async def lifespan(app: FastAPI):
|
||||||
|
"""Start telemetry on app startup, flush on shutdown."""
|
||||||
|
await telemetry.start_async()
|
||||||
|
yield
|
||||||
|
await telemetry.stop_async()
|
||||||
|
|
||||||
|
|
||||||
|
app = FastAPI(lifespan=lifespan)
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/tasks/complete")
|
||||||
|
async def complete_task(
|
||||||
|
task_type: str,
|
||||||
|
model: str,
|
||||||
|
provider: str,
|
||||||
|
complexity: str,
|
||||||
|
actual_input_tokens: int,
|
||||||
|
actual_output_tokens: int,
|
||||||
|
actual_cost_usd_micros: int,
|
||||||
|
duration_ms: int,
|
||||||
|
outcome: str,
|
||||||
|
):
|
||||||
|
"""Record a completed AI coding task."""
|
||||||
|
event = (
|
||||||
|
EventBuilder(instance_id=config.instance_id)
|
||||||
|
.task_type(TaskType(task_type))
|
||||||
|
.model(model)
|
||||||
|
.provider(Provider(provider))
|
||||||
|
.harness_type(Harness.CLAUDE_CODE)
|
||||||
|
.complexity_level(Complexity(complexity))
|
||||||
|
.outcome_value(Outcome(outcome))
|
||||||
|
.duration_ms(duration_ms)
|
||||||
|
.tokens(
|
||||||
|
estimated_in=0,
|
||||||
|
estimated_out=0,
|
||||||
|
actual_in=actual_input_tokens,
|
||||||
|
actual_out=actual_output_tokens,
|
||||||
|
)
|
||||||
|
.cost(estimated=0, actual=actual_cost_usd_micros)
|
||||||
|
.quality(passed=outcome == "success", gates_run=[QualityGate.BUILD, QualityGate.TEST])
|
||||||
|
.context(compactions=0, rotations=0, utilization=0.0)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
|
||||||
|
telemetry.track(event) # Non-blocking, never throws
|
||||||
|
return {"status": "tracked"}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 2: Instrumenting a Generic Python App
|
||||||
|
|
||||||
|
```python
|
||||||
|
"""
|
||||||
|
Generic Python script that tracks AI coding tasks.
|
||||||
|
Suitable for CLI tools, batch processors, or any non-async application.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
|
||||||
|
from mosaicstack_telemetry import (
|
||||||
|
Complexity,
|
||||||
|
EventBuilder,
|
||||||
|
Harness,
|
||||||
|
Outcome,
|
||||||
|
Provider,
|
||||||
|
QualityGate,
|
||||||
|
RepoSizeCategory,
|
||||||
|
TaskType,
|
||||||
|
TelemetryClient,
|
||||||
|
TelemetryConfig,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def run_coding_task() -> dict:
|
||||||
|
"""Simulate an AI coding task. Returns task metrics."""
|
||||||
|
start = time.monotonic()
|
||||||
|
|
||||||
|
# ... your AI coding logic here ...
|
||||||
|
|
||||||
|
elapsed_ms = int((time.monotonic() - start) * 1000)
|
||||||
|
return {
|
||||||
|
"duration_ms": elapsed_ms,
|
||||||
|
"actual_input_tokens": 4500,
|
||||||
|
"actual_output_tokens": 1800,
|
||||||
|
"actual_cost_usd_micros": 48000,
|
||||||
|
"outcome": "success",
|
||||||
|
"quality_gates_passed": True,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
config = TelemetryConfig() # Reads from MOSAIC_TELEMETRY_* env vars
|
||||||
|
|
||||||
|
with TelemetryClient(config) as client:
|
||||||
|
result = run_coding_task()
|
||||||
|
|
||||||
|
event = (
|
||||||
|
EventBuilder(instance_id=config.instance_id)
|
||||||
|
.task_type(TaskType.IMPLEMENTATION)
|
||||||
|
.model("claude-sonnet-4-5-20250929")
|
||||||
|
.provider(Provider.ANTHROPIC)
|
||||||
|
.harness_type(Harness.AIDER)
|
||||||
|
.complexity_level(Complexity.MEDIUM)
|
||||||
|
.outcome_value(Outcome(result["outcome"]))
|
||||||
|
.duration_ms(result["duration_ms"])
|
||||||
|
.tokens(
|
||||||
|
estimated_in=5000,
|
||||||
|
estimated_out=2000,
|
||||||
|
actual_in=result["actual_input_tokens"],
|
||||||
|
actual_out=result["actual_output_tokens"],
|
||||||
|
)
|
||||||
|
.cost(estimated=50000, actual=result["actual_cost_usd_micros"])
|
||||||
|
.quality(
|
||||||
|
passed=result["quality_gates_passed"],
|
||||||
|
gates_run=[QualityGate.BUILD, QualityGate.LINT, QualityGate.TEST],
|
||||||
|
)
|
||||||
|
.context(compactions=0, rotations=0, utilization=0.35)
|
||||||
|
.language("python")
|
||||||
|
.repo_size(RepoSizeCategory.MEDIUM)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
|
||||||
|
client.track(event)
|
||||||
|
print(f"Tracked task: {event.event_id}")
|
||||||
|
|
||||||
|
# Client is automatically flushed and stopped after the `with` block
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Building Events
|
||||||
|
|
||||||
|
The `EventBuilder` provides a fluent API for constructing `TaskCompletionEvent` objects with sensible defaults. All setter methods return the builder instance for chaining.
|
||||||
|
|
||||||
|
### Required Fields
|
||||||
|
|
||||||
|
Every event requires these fields to be set (either via builder methods or from defaults):
|
||||||
|
|
||||||
|
| Builder Method | Sets Field | Default |
|
||||||
|
|----------------|-----------|---------|
|
||||||
|
| `EventBuilder(instance_id=...)` | `instance_id` | (required) |
|
||||||
|
| `.task_type(TaskType.X)` | `task_type` | `unknown` |
|
||||||
|
| `.model("model-name")` | `model` | `"unknown"` |
|
||||||
|
| `.provider(Provider.X)` | `provider` | `unknown` |
|
||||||
|
| `.harness_type(Harness.X)` | `harness` | `unknown` |
|
||||||
|
| `.complexity_level(Complexity.X)` | `complexity` | `medium` |
|
||||||
|
| `.outcome_value(Outcome.X)` | `outcome` | `failure` |
|
||||||
|
| `.duration_ms(N)` | `task_duration_ms` | `0` |
|
||||||
|
| `.tokens(...)` | token fields | all `0` |
|
||||||
|
| `.cost(...)` | cost fields | all `0` |
|
||||||
|
| `.quality(...)` | quality fields | `passed=False, gates_run=[], gates_failed=[]` |
|
||||||
|
| `.context(...)` | context fields | all `0` / `0.0` |
|
||||||
|
|
||||||
|
### Auto-Generated Fields
|
||||||
|
|
||||||
|
| Field | Auto-generated Value |
|
||||||
|
|-------|---------------------|
|
||||||
|
| `event_id` | Random UUID (override with `.event_id(uuid)`) |
|
||||||
|
| `timestamp` | Current UTC time (override with `.timestamp(dt)`) |
|
||||||
|
| `schema_version` | `"1.0"` (set automatically by `TaskCompletionEvent`) |
|
||||||
|
|
||||||
|
### Optional Fields
|
||||||
|
|
||||||
|
| Builder Method | Sets Field | Default |
|
||||||
|
|----------------|-----------|---------|
|
||||||
|
| `.language("python")` | `language` | `None` |
|
||||||
|
| `.repo_size(RepoSizeCategory.MEDIUM)` | `repo_size_category` | `None` |
|
||||||
|
| `.retry_count(N)` | `retry_count` | `0` |
|
||||||
|
|
||||||
|
### Token and Cost Values
|
||||||
|
|
||||||
|
Costs are expressed in **microdollars** (1 USD = 1,000,000 microdollars). This avoids floating-point precision issues.
|
||||||
|
|
||||||
|
```python
|
||||||
|
event = (
|
||||||
|
EventBuilder(instance_id=config.instance_id)
|
||||||
|
# ... other fields ...
|
||||||
|
.tokens(
|
||||||
|
estimated_in=105000, # Pre-task estimate: input tokens
|
||||||
|
estimated_out=45000, # Pre-task estimate: output tokens
|
||||||
|
actual_in=112340, # Actual input tokens consumed
|
||||||
|
actual_out=38760, # Actual output tokens generated
|
||||||
|
)
|
||||||
|
.cost(
|
||||||
|
estimated=630000, # $0.63 in microdollars
|
||||||
|
actual=919200, # $0.92 in microdollars
|
||||||
|
)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Quality Gates
|
||||||
|
|
||||||
|
Record which quality gates were executed and their results:
|
||||||
|
|
||||||
|
```python
|
||||||
|
event = (
|
||||||
|
EventBuilder(instance_id=config.instance_id)
|
||||||
|
# ... other fields ...
|
||||||
|
.quality(
|
||||||
|
passed=False,
|
||||||
|
gates_run=[QualityGate.BUILD, QualityGate.LINT, QualityGate.TEST, QualityGate.COVERAGE],
|
||||||
|
gates_failed=[QualityGate.COVERAGE],
|
||||||
|
)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
Available gates: `BUILD`, `LINT`, `TEST`, `COVERAGE`, `TYPECHECK`, `SECURITY`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Querying Predictions
|
||||||
|
|
||||||
|
The SDK can query crowd-sourced predictions from the telemetry server. Predictions provide percentile distributions for token usage, cost, duration, and quality metrics based on aggregated data from all participating instances.
|
||||||
|
|
||||||
|
### Fetching Predictions
|
||||||
|
|
||||||
|
```python
|
||||||
|
from mosaicstack_telemetry import PredictionQuery, TaskType, Provider, Complexity
|
||||||
|
|
||||||
|
queries = [
|
||||||
|
PredictionQuery(
|
||||||
|
task_type=TaskType.IMPLEMENTATION,
|
||||||
|
model="claude-sonnet-4-5-20250929",
|
||||||
|
provider=Provider.ANTHROPIC,
|
||||||
|
complexity=Complexity.MEDIUM,
|
||||||
|
),
|
||||||
|
PredictionQuery(
|
||||||
|
task_type=TaskType.TESTING,
|
||||||
|
model="claude-haiku-4-5-20251001",
|
||||||
|
provider=Provider.ANTHROPIC,
|
||||||
|
complexity=Complexity.LOW,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Async
|
||||||
|
await client.refresh_predictions(queries)
|
||||||
|
|
||||||
|
# Sync
|
||||||
|
client.refresh_predictions_sync(queries)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Reading from Cache
|
||||||
|
|
||||||
|
Predictions are stored in a TTL-based in-memory cache (default: 6 hours):
|
||||||
|
|
||||||
|
```python
|
||||||
|
prediction = client.get_prediction(queries[0])
|
||||||
|
|
||||||
|
if prediction and prediction.prediction:
|
||||||
|
data = prediction.prediction
|
||||||
|
print(f"Input tokens (median): {data.input_tokens.median}")
|
||||||
|
print(f"Input tokens (p90): {data.input_tokens.p90}")
|
||||||
|
print(f"Output tokens (median): {data.output_tokens.median}")
|
||||||
|
print(f"Cost (median): ${data.cost_usd_micros['median'] / 1_000_000:.4f}")
|
||||||
|
print(f"Duration (median): {data.duration_ms['median'] / 1000:.1f}s")
|
||||||
|
print(f"Correction factor (input): {data.correction_factors.input:.2f}")
|
||||||
|
print(f"Quality gate pass rate: {data.quality.gate_pass_rate:.0%}")
|
||||||
|
print(f"Success rate: {data.quality.success_rate:.0%}")
|
||||||
|
|
||||||
|
meta = prediction.metadata
|
||||||
|
print(f"Sample size: {meta.sample_size}")
|
||||||
|
print(f"Confidence: {meta.confidence}")
|
||||||
|
if meta.fallback_note:
|
||||||
|
print(f"Note: {meta.fallback_note}")
|
||||||
|
else:
|
||||||
|
print("No prediction data available for this combination")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Prediction Confidence Levels
|
||||||
|
|
||||||
|
| Level | Meaning |
|
||||||
|
|-------|---------|
|
||||||
|
| `high` | 100+ samples, exact dimension match |
|
||||||
|
| `medium` | 30-99 samples, exact dimension match |
|
||||||
|
| `low` | <30 samples or fallback was used |
|
||||||
|
| `none` | No data available; `prediction` is `None` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
### The `track()` Contract
|
||||||
|
|
||||||
|
**`track()` never throws and never blocks the caller.** If anything goes wrong (queue full, telemetry disabled, unexpected error), the event is silently dropped and the error is logged. This ensures telemetry instrumentation never affects your application's behavior.
|
||||||
|
|
||||||
|
```python
|
||||||
|
# This is always safe, even if telemetry is misconfigured
|
||||||
|
client.track(event)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Queue Overflow
|
||||||
|
|
||||||
|
When the in-memory queue reaches `max_queue_size` (default 1000), the oldest events are evicted to make room for new ones. A warning is logged when this happens.
|
||||||
|
|
||||||
|
### Submission Retries
|
||||||
|
|
||||||
|
The background submitter retries transient failures with exponential backoff and jitter:
|
||||||
|
|
||||||
|
- **429 Too Many Requests**: Honors the server's `Retry-After` header
|
||||||
|
- **Timeouts**: Retried with backoff
|
||||||
|
- **Network errors**: Retried with backoff
|
||||||
|
- **403 Forbidden**: Not retried (configuration error)
|
||||||
|
|
||||||
|
Failed batches are re-queued for the next submission cycle (up to queue capacity).
|
||||||
|
|
||||||
|
### Logging
|
||||||
|
|
||||||
|
All SDK logging uses the `mosaicstack_telemetry` logger. Enable it to see submission activity:
|
||||||
|
|
||||||
|
```python
|
||||||
|
import logging
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
|
# Or target the SDK logger specifically:
|
||||||
|
logging.getLogger("mosaicstack_telemetry").setLevel(logging.DEBUG)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Dry-Run Mode
|
||||||
|
|
||||||
|
Test your integration without sending data to the server:
|
||||||
|
|
||||||
|
```python
|
||||||
|
config = TelemetryConfig(
|
||||||
|
server_url="https://tel-api.mosaicstack.dev",
|
||||||
|
api_key="a" * 64,
|
||||||
|
instance_id="12345678-1234-1234-1234-123456789abc",
|
||||||
|
dry_run=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
with TelemetryClient(config) as client:
|
||||||
|
client.track(event)
|
||||||
|
# Logs: "[DRY RUN] Would submit batch of 1 events to ..."
|
||||||
|
```
|
||||||
|
|
||||||
|
## Disabling Telemetry
|
||||||
|
|
||||||
|
Set `enabled=False` or the environment variable `MOSAIC_TELEMETRY_ENABLED=false`:
|
||||||
|
|
||||||
|
```python
|
||||||
|
config = TelemetryConfig(enabled=False)
|
||||||
|
|
||||||
|
with TelemetryClient(config) as client:
|
||||||
|
client.track(event) # Silently dropped, no background thread started
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Versioning
|
||||||
|
|
||||||
|
This SDK publishes two types of versions to the Mosaic Stack PyPI registry at `git.mosaicstack.dev`:
|
||||||
|
|
||||||
|
| Channel | Version Format | Example | Branch | Install |
|
||||||
|
|---------|---------------|---------|--------|---------|
|
||||||
|
| **Stable** | `X.Y.Z` | `0.1.0` | `main` / tags | `pip install mosaicstack-telemetry` |
|
||||||
|
| **Dev** | `X.Y.Z.devYYYYMMDDHHMMSS` | `0.1.0.dev20260215045901` | `develop` | `pip install mosaicstack-telemetry --pre` |
|
||||||
|
|
||||||
|
Dev versions use a UTC timestamp suffix following [PEP 440](https://peps.python.org/pep-0440/#developmental-releases). This convention is shared across Mosaic Stack SDKs:
|
||||||
|
|
||||||
|
| SDK | Dev Version Format | Example |
|
||||||
|
|-----|-------------------|---------|
|
||||||
|
| Python (this SDK) | `X.Y.Z.devYYYYMMDDHHMMSS` | `0.1.0.dev20260215045901` |
|
||||||
|
| JavaScript | `X.Y.Z-dev.YYYYMMDDHHmmss` | `0.1.0-dev.20260215045901` |
|
||||||
|
|
||||||
|
By default, `pip install` resolves only stable releases. To install the latest dev build:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install mosaicstack-telemetry --pre --index-url https://git.mosaicstack.dev/api/packages/mosaic/pypi/simple/
|
||||||
|
```
|
||||||
|
|
||||||
|
To pin to a specific dev version:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install mosaicstack-telemetry==0.1.0.dev20260215045901 --index-url https://git.mosaicstack.dev/api/packages/mosaic/pypi/simple/
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## API Compatibility
|
||||||
|
|
||||||
|
| SDK Version | Telemetry API | Event Schema | Notes |
|
||||||
|
|-------------|---------------|--------------|-------|
|
||||||
|
| 0.1.x | v1 (`/v1/*`) | 1.0 | Current release |
|
||||||
|
|
||||||
|
The SDK submits events to `POST /v1/events/batch` and queries predictions from `POST /v1/predictions/batch`. These are the only two server endpoints the SDK communicates with.
|
||||||
|
|
||||||
|
For the full server API documentation, see the [Mosaic Telemetry API Reference](https://github.com/mosaicstack/telemetry).
|
||||||
@@ -17,6 +17,8 @@ dev = [
|
|||||||
"ruff>=0.14.0",
|
"ruff>=0.14.0",
|
||||||
"mypy>=1.15",
|
"mypy>=1.15",
|
||||||
"respx>=0.21.0",
|
"respx>=0.21.0",
|
||||||
|
"bandit>=1.8.0",
|
||||||
|
"pip-audit>=2.7.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
|
|||||||
485
uv.lock
generated
485
uv.lock
generated
@@ -34,6 +34,48 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/a0/59/76ab57e3fe74484f48a53f8e337171b4a2349e506eabe136d7e01d059086/backports_asyncio_runner-1.2.0-py3-none-any.whl", hash = "sha256:0da0a936a8aeb554eccb426dc55af3ba63bcdc69fa1a600b5bb305413a4477b5", size = 12313, upload-time = "2025-07-02T02:27:14.263Z" },
|
{ url = "https://files.pythonhosted.org/packages/a0/59/76ab57e3fe74484f48a53f8e337171b4a2349e506eabe136d7e01d059086/backports_asyncio_runner-1.2.0-py3-none-any.whl", hash = "sha256:0da0a936a8aeb554eccb426dc55af3ba63bcdc69fa1a600b5bb305413a4477b5", size = 12313, upload-time = "2025-07-02T02:27:14.263Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bandit"
|
||||||
|
version = "1.9.3"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
||||||
|
{ name = "pyyaml" },
|
||||||
|
{ name = "rich" },
|
||||||
|
{ name = "stevedore" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/89/76/a7f3e639b78601118aaa4a394db2c66ae2597fbd8c39644c32874ed11e0c/bandit-1.9.3.tar.gz", hash = "sha256:ade4b9b7786f89ef6fc7344a52b34558caec5da74cb90373aed01de88472f774", size = 4242154, upload-time = "2026-01-19T04:05:22.802Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e0/0b/8bdc52111c83e2dc2f97403dc87c0830b8989d9ae45732b34b686326fb2c/bandit-1.9.3-py3-none-any.whl", hash = "sha256:4745917c88d2246def79748bde5e08b9d5e9b92f877863d43fab70cd8814ce6a", size = 134451, upload-time = "2026-01-19T04:05:20.938Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "boolean-py"
|
||||||
|
version = "5.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/c4/cf/85379f13b76f3a69bca86b60237978af17d6aa0bc5998978c3b8cf05abb2/boolean_py-5.0.tar.gz", hash = "sha256:60cbc4bad079753721d32649545505362c754e121570ada4658b852a3a318d95", size = 37047, upload-time = "2025-04-03T10:39:49.734Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e5/ca/78d423b324b8d77900030fa59c4aa9054261ef0925631cd2501dd015b7b7/boolean_py-5.0-py3-none-any.whl", hash = "sha256:ef28a70bd43115208441b53a045d1549e2f0ec6e3d08a9d142cbc41c1938e8d9", size = 26577, upload-time = "2025-04-03T10:39:48.449Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cachecontrol"
|
||||||
|
version = "0.14.4"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "msgpack" },
|
||||||
|
{ name = "requests" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/2d/f6/c972b32d80760fb79d6b9eeb0b3010a46b89c0b23cf6329417ff7886cd22/cachecontrol-0.14.4.tar.gz", hash = "sha256:e6220afafa4c22a47dd0badb319f84475d79108100d04e26e8542ef7d3ab05a1", size = 16150, upload-time = "2025-11-14T04:32:13.138Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ef/79/c45f2d53efe6ada1110cf6f9fca095e4ff47a0454444aefdde6ac4789179/cachecontrol-0.14.4-py3-none-any.whl", hash = "sha256:b7ac014ff72ee199b5f8af1de29d60239954f223e948196fa3d84adaffc71d2b", size = 22247, upload-time = "2025-11-14T04:32:11.733Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.optional-dependencies]
|
||||||
|
filecache = [
|
||||||
|
{ name = "filelock" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "certifi"
|
name = "certifi"
|
||||||
version = "2026.1.4"
|
version = "2026.1.4"
|
||||||
@@ -43,6 +85,95 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-py3-none-any.whl", hash = "sha256:9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c", size = 152900, upload-time = "2026-01-04T02:42:40.15Z" },
|
{ url = "https://files.pythonhosted.org/packages/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-py3-none-any.whl", hash = "sha256:9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c", size = 152900, upload-time = "2026-01-04T02:42:40.15Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "charset-normalizer"
|
||||||
|
version = "3.4.4"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418, upload-time = "2025-10-14T04:42:32.879Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1f/b8/6d51fc1d52cbd52cd4ccedd5b5b2f0f6a11bbf6765c782298b0f3e808541/charset_normalizer-3.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d", size = 209709, upload-time = "2025-10-14T04:40:11.385Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5c/af/1f9d7f7faafe2ddfb6f72a2e07a548a629c61ad510fe60f9630309908fef/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8", size = 148814, upload-time = "2025-10-14T04:40:13.135Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/79/3d/f2e3ac2bbc056ca0c204298ea4e3d9db9b4afe437812638759db2c976b5f/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:027f6de494925c0ab2a55eab46ae5129951638a49a34d87f4c3eda90f696b4ad", size = 144467, upload-time = "2025-10-14T04:40:14.728Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ec/85/1bf997003815e60d57de7bd972c57dc6950446a3e4ccac43bc3070721856/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f820802628d2694cb7e56db99213f930856014862f3fd943d290ea8438d07ca8", size = 162280, upload-time = "2025-10-14T04:40:16.14Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3e/8e/6aa1952f56b192f54921c436b87f2aaf7c7a7c3d0d1a765547d64fd83c13/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:798d75d81754988d2565bff1b97ba5a44411867c0cf32b77a7e8f8d84796b10d", size = 159454, upload-time = "2025-10-14T04:40:17.567Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/36/3b/60cbd1f8e93aa25d1c669c649b7a655b0b5fb4c571858910ea9332678558/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d1bb833febdff5c8927f922386db610b49db6e0d4f4ee29601d71e7c2694313", size = 153609, upload-time = "2025-10-14T04:40:19.08Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/64/91/6a13396948b8fd3c4b4fd5bc74d045f5637d78c9675585e8e9fbe5636554/charset_normalizer-3.4.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9cd98cdc06614a2f768d2b7286d66805f94c48cde050acdbbb7db2600ab3197e", size = 151849, upload-time = "2025-10-14T04:40:20.607Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b7/7a/59482e28b9981d105691e968c544cc0df3b7d6133152fb3dcdc8f135da7a/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:077fbb858e903c73f6c9db43374fd213b0b6a778106bc7032446a8e8b5b38b93", size = 151586, upload-time = "2025-10-14T04:40:21.719Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/92/59/f64ef6a1c4bdd2baf892b04cd78792ed8684fbc48d4c2afe467d96b4df57/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:244bfb999c71b35de57821b8ea746b24e863398194a4014e4c76adc2bbdfeff0", size = 145290, upload-time = "2025-10-14T04:40:23.069Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6b/63/3bf9f279ddfa641ffa1962b0db6a57a9c294361cc2f5fcac997049a00e9c/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:64b55f9dce520635f018f907ff1b0df1fdc31f2795a922fb49dd14fbcdf48c84", size = 163663, upload-time = "2025-10-14T04:40:24.17Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ed/09/c9e38fc8fa9e0849b172b581fd9803bdf6e694041127933934184e19f8c3/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:faa3a41b2b66b6e50f84ae4a68c64fcd0c44355741c6374813a800cd6695db9e", size = 151964, upload-time = "2025-10-14T04:40:25.368Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d2/d1/d28b747e512d0da79d8b6a1ac18b7ab2ecfd81b2944c4c710e166d8dd09c/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6515f3182dbe4ea06ced2d9e8666d97b46ef4c75e326b79bb624110f122551db", size = 161064, upload-time = "2025-10-14T04:40:26.806Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/bb/9a/31d62b611d901c3b9e5500c36aab0ff5eb442043fb3a1c254200d3d397d9/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cc00f04ed596e9dc0da42ed17ac5e596c6ccba999ba6bd92b0e0aef2f170f2d6", size = 155015, upload-time = "2025-10-14T04:40:28.284Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1f/f3/107e008fa2bff0c8b9319584174418e5e5285fef32f79d8ee6a430d0039c/charset_normalizer-3.4.4-cp310-cp310-win32.whl", hash = "sha256:f34be2938726fc13801220747472850852fe6b1ea75869a048d6f896838c896f", size = 99792, upload-time = "2025-10-14T04:40:29.613Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/eb/66/e396e8a408843337d7315bab30dbf106c38966f1819f123257f5520f8a96/charset_normalizer-3.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:a61900df84c667873b292c3de315a786dd8dac506704dea57bc957bd31e22c7d", size = 107198, upload-time = "2025-10-14T04:40:30.644Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b5/58/01b4f815bf0312704c267f2ccb6e5d42bcc7752340cd487bc9f8c3710597/charset_normalizer-3.4.4-cp310-cp310-win_arm64.whl", hash = "sha256:cead0978fc57397645f12578bfd2d5ea9138ea0fac82b2f63f7f7c6877986a69", size = 100262, upload-time = "2025-10-14T04:40:32.108Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ed/27/c6491ff4954e58a10f69ad90aca8a1b6fe9c5d3c6f380907af3c37435b59/charset_normalizer-3.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8", size = 206988, upload-time = "2025-10-14T04:40:33.79Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/94/59/2e87300fe67ab820b5428580a53cad894272dbb97f38a7a814a2a1ac1011/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0", size = 147324, upload-time = "2025-10-14T04:40:34.961Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/07/fb/0cf61dc84b2b088391830f6274cb57c82e4da8bbc2efeac8c025edb88772/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3", size = 142742, upload-time = "2025-10-14T04:40:36.105Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/62/8b/171935adf2312cd745d290ed93cf16cf0dfe320863ab7cbeeae1dcd6535f/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc", size = 160863, upload-time = "2025-10-14T04:40:37.188Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/09/73/ad875b192bda14f2173bfc1bc9a55e009808484a4b256748d931b6948442/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897", size = 157837, upload-time = "2025-10-14T04:40:38.435Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6d/fc/de9cce525b2c5b94b47c70a4b4fb19f871b24995c728e957ee68ab1671ea/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381", size = 151550, upload-time = "2025-10-14T04:40:40.053Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/55/c2/43edd615fdfba8c6f2dfbd459b25a6b3b551f24ea21981e23fb768503ce1/charset_normalizer-3.4.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815", size = 149162, upload-time = "2025-10-14T04:40:41.163Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/03/86/bde4ad8b4d0e9429a4e82c1e8f5c659993a9a863ad62c7df05cf7b678d75/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0", size = 150019, upload-time = "2025-10-14T04:40:42.276Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1f/86/a151eb2af293a7e7bac3a739b81072585ce36ccfb4493039f49f1d3cae8c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161", size = 143310, upload-time = "2025-10-14T04:40:43.439Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b5/fe/43dae6144a7e07b87478fdfc4dbe9efd5defb0e7ec29f5f58a55aeef7bf7/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4", size = 162022, upload-time = "2025-10-14T04:40:44.547Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/80/e6/7aab83774f5d2bca81f42ac58d04caf44f0cc2b65fc6db2b3b2e8a05f3b3/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89", size = 149383, upload-time = "2025-10-14T04:40:46.018Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/4f/e8/b289173b4edae05c0dde07f69f8db476a0b511eac556dfe0d6bda3c43384/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569", size = 159098, upload-time = "2025-10-14T04:40:47.081Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d8/df/fe699727754cae3f8478493c7f45f777b17c3ef0600e28abfec8619eb49c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224", size = 152991, upload-time = "2025-10-14T04:40:48.246Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1a/86/584869fe4ddb6ffa3bd9f491b87a01568797fb9bd8933f557dba9771beaf/charset_normalizer-3.4.4-cp311-cp311-win32.whl", hash = "sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a", size = 99456, upload-time = "2025-10-14T04:40:49.376Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/65/f6/62fdd5feb60530f50f7e38b4f6a1d5203f4d16ff4f9f0952962c044e919a/charset_normalizer-3.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016", size = 106978, upload-time = "2025-10-14T04:40:50.844Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7a/9d/0710916e6c82948b3be62d9d398cb4fcf4e97b56d6a6aeccd66c4b2f2bd5/charset_normalizer-3.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1", size = 99969, upload-time = "2025-10-14T04:40:52.272Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f3/85/1637cd4af66fa687396e757dec650f28025f2a2f5a5531a3208dc0ec43f2/charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394", size = 208425, upload-time = "2025-10-14T04:40:53.353Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/9d/6a/04130023fef2a0d9c62d0bae2649b69f7b7d8d24ea5536feef50551029df/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25", size = 148162, upload-time = "2025-10-14T04:40:54.558Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/78/29/62328d79aa60da22c9e0b9a66539feae06ca0f5a4171ac4f7dc285b83688/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef", size = 144558, upload-time = "2025-10-14T04:40:55.677Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/86/bb/b32194a4bf15b88403537c2e120b817c61cd4ecffa9b6876e941c3ee38fe/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d", size = 161497, upload-time = "2025-10-14T04:40:57.217Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/19/89/a54c82b253d5b9b111dc74aca196ba5ccfcca8242d0fb64146d4d3183ff1/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8", size = 159240, upload-time = "2025-10-14T04:40:58.358Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c0/10/d20b513afe03acc89ec33948320a5544d31f21b05368436d580dec4e234d/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86", size = 153471, upload-time = "2025-10-14T04:40:59.468Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/61/fa/fbf177b55bdd727010f9c0a3c49eefa1d10f960e5f09d1d887bf93c2e698/charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a", size = 150864, upload-time = "2025-10-14T04:41:00.623Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/05/12/9fbc6a4d39c0198adeebbde20b619790e9236557ca59fc40e0e3cebe6f40/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f", size = 150647, upload-time = "2025-10-14T04:41:01.754Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ad/1f/6a9a593d52e3e8c5d2b167daf8c6b968808efb57ef4c210acb907c365bc4/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc", size = 145110, upload-time = "2025-10-14T04:41:03.231Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/30/42/9a52c609e72471b0fc54386dc63c3781a387bb4fe61c20231a4ebcd58bdd/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf", size = 162839, upload-time = "2025-10-14T04:41:04.715Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c4/5b/c0682bbf9f11597073052628ddd38344a3d673fda35a36773f7d19344b23/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15", size = 150667, upload-time = "2025-10-14T04:41:05.827Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e4/24/a41afeab6f990cf2daf6cb8c67419b63b48cf518e4f56022230840c9bfb2/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9", size = 160535, upload-time = "2025-10-14T04:41:06.938Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2a/e5/6a4ce77ed243c4a50a1fecca6aaaab419628c818a49434be428fe24c9957/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0", size = 154816, upload-time = "2025-10-14T04:41:08.101Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a8/ef/89297262b8092b312d29cdb2517cb1237e51db8ecef2e9af5edbe7b683b1/charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26", size = 99694, upload-time = "2025-10-14T04:41:09.23Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3d/2d/1e5ed9dd3b3803994c155cd9aacb60c82c331bad84daf75bcb9c91b3295e/charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525", size = 107131, upload-time = "2025-10-14T04:41:10.467Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d0/d9/0ed4c7098a861482a7b6a95603edce4c0d9db2311af23da1fb2b75ec26fc/charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3", size = 100390, upload-time = "2025-10-14T04:41:11.915Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", size = 208091, upload-time = "2025-10-14T04:41:13.346Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7d/62/73a6d7450829655a35bb88a88fca7d736f9882a27eacdca2c6d505b57e2e/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", size = 147936, upload-time = "2025-10-14T04:41:14.461Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/89/c5/adb8c8b3d6625bef6d88b251bbb0d95f8205831b987631ab0c8bb5d937c2/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", size = 144180, upload-time = "2025-10-14T04:41:15.588Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/91/ed/9706e4070682d1cc219050b6048bfd293ccf67b3d4f5a4f39207453d4b99/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", size = 161346, upload-time = "2025-10-14T04:41:16.738Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d5/0d/031f0d95e4972901a2f6f09ef055751805ff541511dc1252ba3ca1f80cf5/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", size = 158874, upload-time = "2025-10-14T04:41:17.923Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", size = 153076, upload-time = "2025-10-14T04:41:19.106Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/75/1e/5ff781ddf5260e387d6419959ee89ef13878229732732ee73cdae01800f2/charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", size = 150601, upload-time = "2025-10-14T04:41:20.245Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d7/57/71be810965493d3510a6ca79b90c19e48696fb1ff964da319334b12677f0/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", size = 150376, upload-time = "2025-10-14T04:41:21.398Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e5/d5/c3d057a78c181d007014feb7e9f2e65905a6c4ef182c0ddf0de2924edd65/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", size = 144825, upload-time = "2025-10-14T04:41:22.583Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e6/8c/d0406294828d4976f275ffbe66f00266c4b3136b7506941d87c00cab5272/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", size = 162583, upload-time = "2025-10-14T04:41:23.754Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d7/24/e2aa1f18c8f15c4c0e932d9287b8609dd30ad56dbe41d926bd846e22fb8d/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", size = 150366, upload-time = "2025-10-14T04:41:25.27Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e4/5b/1e6160c7739aad1e2df054300cc618b06bf784a7a164b0f238360721ab86/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", size = 160300, upload-time = "2025-10-14T04:41:26.725Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7a/10/f882167cd207fbdd743e55534d5d9620e095089d176d55cb22d5322f2afd/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", size = 154465, upload-time = "2025-10-14T04:41:28.322Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", size = 99404, upload-time = "2025-10-14T04:41:29.95Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", size = 107092, upload-time = "2025-10-14T04:41:31.188Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", size = 100408, upload-time = "2025-10-14T04:41:32.624Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2a/35/7051599bd493e62411d6ede36fd5af83a38f37c4767b92884df7301db25d/charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", size = 207746, upload-time = "2025-10-14T04:41:33.773Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/10/9a/97c8d48ef10d6cd4fcead2415523221624bf58bcf68a802721a6bc807c8f/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", size = 147889, upload-time = "2025-10-14T04:41:34.897Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/10/bf/979224a919a1b606c82bd2c5fa49b5c6d5727aa47b4312bb27b1734f53cd/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", size = 143641, upload-time = "2025-10-14T04:41:36.116Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ba/33/0ad65587441fc730dc7bd90e9716b30b4702dc7b617e6ba4997dc8651495/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", size = 160779, upload-time = "2025-10-14T04:41:37.229Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/67/ed/331d6b249259ee71ddea93f6f2f0a56cfebd46938bde6fcc6f7b9a3d0e09/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", size = 159035, upload-time = "2025-10-14T04:41:38.368Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/67/ff/f6b948ca32e4f2a4576aa129d8bed61f2e0543bf9f5f2b7fc3758ed005c9/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", size = 152542, upload-time = "2025-10-14T04:41:39.862Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/16/85/276033dcbcc369eb176594de22728541a925b2632f9716428c851b149e83/charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", size = 149524, upload-time = "2025-10-14T04:41:41.319Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/9e/f2/6a2a1f722b6aba37050e626530a46a68f74e63683947a8acff92569f979a/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", size = 150395, upload-time = "2025-10-14T04:41:42.539Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/60/bb/2186cb2f2bbaea6338cad15ce23a67f9b0672929744381e28b0592676824/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", size = 143680, upload-time = "2025-10-14T04:41:43.661Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7d/a5/bf6f13b772fbb2a90360eb620d52ed8f796f3c5caee8398c3b2eb7b1c60d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", size = 162045, upload-time = "2025-10-14T04:41:44.821Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/df/c5/d1be898bf0dc3ef9030c3825e5d3b83f2c528d207d246cbabe245966808d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", size = 149687, upload-time = "2025-10-14T04:41:46.442Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a5/42/90c1f7b9341eef50c8a1cb3f098ac43b0508413f33affd762855f67a410e/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", size = 160014, upload-time = "2025-10-14T04:41:47.631Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/76/be/4d3ee471e8145d12795ab655ece37baed0929462a86e72372fd25859047c/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", size = 154044, upload-time = "2025-10-14T04:41:48.81Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", size = 99940, upload-time = "2025-10-14T04:41:49.946Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", size = 107104, upload-time = "2025-10-14T04:41:51.051Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", size = 100743, upload-time = "2025-10-14T04:41:52.122Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "colorama"
|
name = "colorama"
|
||||||
version = "0.4.6"
|
version = "0.4.6"
|
||||||
@@ -156,6 +287,31 @@ toml = [
|
|||||||
{ name = "tomli", marker = "python_full_version <= '3.11'" },
|
{ name = "tomli", marker = "python_full_version <= '3.11'" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cyclonedx-python-lib"
|
||||||
|
version = "11.6.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "license-expression" },
|
||||||
|
{ name = "packageurl-python" },
|
||||||
|
{ name = "py-serializable" },
|
||||||
|
{ name = "sortedcontainers" },
|
||||||
|
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/89/ed/54ecfa25fc145c58bf4f98090f7b6ffe5188d0759248c57dde44427ea239/cyclonedx_python_lib-11.6.0.tar.gz", hash = "sha256:7fb85a4371fa3a203e5be577ac22b7e9a7157f8b0058b7448731474d6dea7bf0", size = 1408147, upload-time = "2025-12-02T12:28:46.446Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c7/1b/534ad8a5e0f9470522811a8e5a9bc5d328fb7738ba29faf357467a4ef6d0/cyclonedx_python_lib-11.6.0-py3-none-any.whl", hash = "sha256:94f4aae97db42a452134dafdddcfab9745324198201c4777ed131e64c8380759", size = 511157, upload-time = "2025-12-02T12:28:44.158Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "defusedxml"
|
||||||
|
version = "0.7.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/0f/d5/c66da9b79e5bdb124974bfe172b4daf3c984ebd9c2a06e2b8a4dc7331c72/defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69", size = 75520, upload-time = "2021-03-08T10:59:26.269Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/07/6c/aa3f2f849e01cb6a001cd8554a88d4c77c5c1a31c95bdf1cf9301e6d9ef4/defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61", size = 25604, upload-time = "2021-03-08T10:59:24.45Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "exceptiongroup"
|
name = "exceptiongroup"
|
||||||
version = "1.3.1"
|
version = "1.3.1"
|
||||||
@@ -168,6 +324,15 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" },
|
{ url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "filelock"
|
||||||
|
version = "3.24.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/00/cd/fa3ab025a8f9772e8a9146d8fd8eef6d62649274d231ca84249f54a0de4a/filelock-3.24.0.tar.gz", hash = "sha256:aeeab479339ddf463a1cdd1f15a6e6894db976071e5883efc94d22ed5139044b", size = 37166, upload-time = "2026-02-14T16:05:28.723Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d9/dd/d7e7f4f49180e8591c9e1281d15ecf8e7f25eb2c829771d9682f1f9fe0c8/filelock-3.24.0-py3-none-any.whl", hash = "sha256:eebebb403d78363ef7be8e236b63cc6760b0004c7464dceaba3fd0afbd637ced", size = 23977, upload-time = "2026-02-14T16:05:27.578Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "h11"
|
name = "h11"
|
||||||
version = "0.16.0"
|
version = "0.16.0"
|
||||||
@@ -296,6 +461,39 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/fc/85/69f92b2a7b3c0f88ffe107c86b952b397004b5b8ea5a81da3d9c04c04422/librt-0.7.8-cp314-cp314t-win_arm64.whl", hash = "sha256:8766ece9de08527deabcd7cb1b4f1a967a385d26e33e536d6d8913db6ef74f06", size = 40550, upload-time = "2026-01-14T12:56:01.542Z" },
|
{ url = "https://files.pythonhosted.org/packages/fc/85/69f92b2a7b3c0f88ffe107c86b952b397004b5b8ea5a81da3d9c04c04422/librt-0.7.8-cp314-cp314t-win_arm64.whl", hash = "sha256:8766ece9de08527deabcd7cb1b4f1a967a385d26e33e536d6d8913db6ef74f06", size = 40550, upload-time = "2026-01-14T12:56:01.542Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "license-expression"
|
||||||
|
version = "30.4.4"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "boolean-py" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/40/71/d89bb0e71b1415453980fd32315f2a037aad9f7f70f695c7cec7035feb13/license_expression-30.4.4.tar.gz", hash = "sha256:73448f0aacd8d0808895bdc4b2c8e01a8d67646e4188f887375398c761f340fd", size = 186402, upload-time = "2025-07-22T11:13:32.17Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/af/40/791891d4c0c4dab4c5e187c17261cedc26285fd41541577f900470a45a4d/license_expression-30.4.4-py3-none-any.whl", hash = "sha256:421788fdcadb41f049d2dc934ce666626265aeccefddd25e162a26f23bcbf8a4", size = 120615, upload-time = "2025-07-22T11:13:31.217Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "markdown-it-py"
|
||||||
|
version = "4.0.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "mdurl" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" }
|
||||||
|
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 = "mdurl"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" }
|
||||||
|
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]]
|
[[package]]
|
||||||
name = "mosaicstack-telemetry"
|
name = "mosaicstack-telemetry"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -307,7 +505,9 @@ dependencies = [
|
|||||||
|
|
||||||
[package.optional-dependencies]
|
[package.optional-dependencies]
|
||||||
dev = [
|
dev = [
|
||||||
|
{ name = "bandit" },
|
||||||
{ name = "mypy" },
|
{ name = "mypy" },
|
||||||
|
{ name = "pip-audit" },
|
||||||
{ name = "pytest" },
|
{ name = "pytest" },
|
||||||
{ name = "pytest-asyncio" },
|
{ name = "pytest-asyncio" },
|
||||||
{ name = "pytest-cov" },
|
{ name = "pytest-cov" },
|
||||||
@@ -317,8 +517,10 @@ dev = [
|
|||||||
|
|
||||||
[package.metadata]
|
[package.metadata]
|
||||||
requires-dist = [
|
requires-dist = [
|
||||||
|
{ name = "bandit", marker = "extra == 'dev'", specifier = ">=1.8.0" },
|
||||||
{ name = "httpx", specifier = ">=0.25.0" },
|
{ name = "httpx", specifier = ">=0.25.0" },
|
||||||
{ name = "mypy", marker = "extra == 'dev'", specifier = ">=1.15" },
|
{ name = "mypy", marker = "extra == 'dev'", specifier = ">=1.15" },
|
||||||
|
{ name = "pip-audit", marker = "extra == 'dev'", specifier = ">=2.7.0" },
|
||||||
{ name = "pydantic", specifier = ">=2.5.0" },
|
{ name = "pydantic", specifier = ">=2.5.0" },
|
||||||
{ name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0" },
|
{ name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0" },
|
||||||
{ name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.24" },
|
{ name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.24" },
|
||||||
@@ -328,6 +530,67 @@ requires-dist = [
|
|||||||
]
|
]
|
||||||
provides-extras = ["dev"]
|
provides-extras = ["dev"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "msgpack"
|
||||||
|
version = "1.1.2"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/4d/f2/bfb55a6236ed8725a96b0aa3acbd0ec17588e6a2c3b62a93eb513ed8783f/msgpack-1.1.2.tar.gz", hash = "sha256:3b60763c1373dd60f398488069bcdc703cd08a711477b5d480eecc9f9626f47e", size = 173581, upload-time = "2025-10-08T09:15:56.596Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f5/a2/3b68a9e769db68668b25c6108444a35f9bd163bb848c0650d516761a59c0/msgpack-1.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0051fffef5a37ca2cd16978ae4f0aef92f164df86823871b5162812bebecd8e2", size = 81318, upload-time = "2025-10-08T09:14:38.722Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5b/e1/2b720cc341325c00be44e1ed59e7cfeae2678329fbf5aa68f5bda57fe728/msgpack-1.1.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a605409040f2da88676e9c9e5853b3449ba8011973616189ea5ee55ddbc5bc87", size = 83786, upload-time = "2025-10-08T09:14:40.082Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/71/e5/c2241de64bfceac456b140737812a2ab310b10538a7b34a1d393b748e095/msgpack-1.1.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b696e83c9f1532b4af884045ba7f3aa741a63b2bc22617293a2c6a7c645f251", size = 398240, upload-time = "2025-10-08T09:14:41.151Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b7/09/2a06956383c0fdebaef5aa9246e2356776f12ea6f2a44bd1368abf0e46c4/msgpack-1.1.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:365c0bbe981a27d8932da71af63ef86acc59ed5c01ad929e09a0b88c6294e28a", size = 406070, upload-time = "2025-10-08T09:14:42.821Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0e/74/2957703f0e1ef20637d6aead4fbb314330c26f39aa046b348c7edcf6ca6b/msgpack-1.1.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:41d1a5d875680166d3ac5c38573896453bbbea7092936d2e107214daf43b1d4f", size = 393403, upload-time = "2025-10-08T09:14:44.38Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a5/09/3bfc12aa90f77b37322fc33e7a8a7c29ba7c8edeadfa27664451801b9860/msgpack-1.1.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:354e81bcdebaab427c3df4281187edc765d5d76bfb3a7c125af9da7a27e8458f", size = 398947, upload-time = "2025-10-08T09:14:45.56Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/4b/4f/05fcebd3b4977cb3d840f7ef6b77c51f8582086de5e642f3fefee35c86fc/msgpack-1.1.2-cp310-cp310-win32.whl", hash = "sha256:e64c8d2f5e5d5fda7b842f55dec6133260ea8f53c4257d64494c534f306bf7a9", size = 64769, upload-time = "2025-10-08T09:14:47.334Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d0/3e/b4547e3a34210956382eed1c85935fff7e0f9b98be3106b3745d7dec9c5e/msgpack-1.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:db6192777d943bdaaafb6ba66d44bf65aa0e9c5616fa1d2da9bb08828c6b39aa", size = 71293, upload-time = "2025-10-08T09:14:48.665Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2c/97/560d11202bcd537abca693fd85d81cebe2107ba17301de42b01ac1677b69/msgpack-1.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2e86a607e558d22985d856948c12a3fa7b42efad264dca8a3ebbcfa2735d786c", size = 82271, upload-time = "2025-10-08T09:14:49.967Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/83/04/28a41024ccbd67467380b6fb440ae916c1e4f25e2cd4c63abe6835ac566e/msgpack-1.1.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:283ae72fc89da59aa004ba147e8fc2f766647b1251500182fac0350d8af299c0", size = 84914, upload-time = "2025-10-08T09:14:50.958Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/71/46/b817349db6886d79e57a966346cf0902a426375aadc1e8e7a86a75e22f19/msgpack-1.1.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:61c8aa3bd513d87c72ed0b37b53dd5c5a0f58f2ff9f26e1555d3bd7948fb7296", size = 416962, upload-time = "2025-10-08T09:14:51.997Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/da/e0/6cc2e852837cd6086fe7d8406af4294e66827a60a4cf60b86575a4a65ca8/msgpack-1.1.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:454e29e186285d2ebe65be34629fa0e8605202c60fbc7c4c650ccd41870896ef", size = 426183, upload-time = "2025-10-08T09:14:53.477Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/25/98/6a19f030b3d2ea906696cedd1eb251708e50a5891d0978b012cb6107234c/msgpack-1.1.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7bc8813f88417599564fafa59fd6f95be417179f76b40325b500b3c98409757c", size = 411454, upload-time = "2025-10-08T09:14:54.648Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b7/cd/9098fcb6adb32187a70b7ecaabf6339da50553351558f37600e53a4a2a23/msgpack-1.1.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bafca952dc13907bdfdedfc6a5f579bf4f292bdd506fadb38389afa3ac5b208e", size = 422341, upload-time = "2025-10-08T09:14:56.328Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e6/ae/270cecbcf36c1dc85ec086b33a51a4d7d08fc4f404bdbc15b582255d05ff/msgpack-1.1.2-cp311-cp311-win32.whl", hash = "sha256:602b6740e95ffc55bfb078172d279de3773d7b7db1f703b2f1323566b878b90e", size = 64747, upload-time = "2025-10-08T09:14:57.882Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2a/79/309d0e637f6f37e83c711f547308b91af02b72d2326ddd860b966080ef29/msgpack-1.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:d198d275222dc54244bf3327eb8cbe00307d220241d9cec4d306d49a44e85f68", size = 71633, upload-time = "2025-10-08T09:14:59.177Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/73/4d/7c4e2b3d9b1106cd0aa6cb56cc57c6267f59fa8bfab7d91df5adc802c847/msgpack-1.1.2-cp311-cp311-win_arm64.whl", hash = "sha256:86f8136dfa5c116365a8a651a7d7484b65b13339731dd6faebb9a0242151c406", size = 64755, upload-time = "2025-10-08T09:15:00.48Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ad/bd/8b0d01c756203fbab65d265859749860682ccd2a59594609aeec3a144efa/msgpack-1.1.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:70a0dff9d1f8da25179ffcf880e10cf1aad55fdb63cd59c9a49a1b82290062aa", size = 81939, upload-time = "2025-10-08T09:15:01.472Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/34/68/ba4f155f793a74c1483d4bdef136e1023f7bcba557f0db4ef3db3c665cf1/msgpack-1.1.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:446abdd8b94b55c800ac34b102dffd2f6aa0ce643c55dfc017ad89347db3dbdb", size = 85064, upload-time = "2025-10-08T09:15:03.764Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f2/60/a064b0345fc36c4c3d2c743c82d9100c40388d77f0b48b2f04d6041dbec1/msgpack-1.1.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c63eea553c69ab05b6747901b97d620bb2a690633c77f23feb0c6a947a8a7b8f", size = 417131, upload-time = "2025-10-08T09:15:05.136Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/65/92/a5100f7185a800a5d29f8d14041f61475b9de465ffcc0f3b9fba606e4505/msgpack-1.1.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:372839311ccf6bdaf39b00b61288e0557916c3729529b301c52c2d88842add42", size = 427556, upload-time = "2025-10-08T09:15:06.837Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f5/87/ffe21d1bf7d9991354ad93949286f643b2bb6ddbeab66373922b44c3b8cc/msgpack-1.1.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2929af52106ca73fcb28576218476ffbb531a036c2adbcf54a3664de124303e9", size = 404920, upload-time = "2025-10-08T09:15:08.179Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ff/41/8543ed2b8604f7c0d89ce066f42007faac1eaa7d79a81555f206a5cdb889/msgpack-1.1.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:be52a8fc79e45b0364210eef5234a7cf8d330836d0a64dfbb878efa903d84620", size = 415013, upload-time = "2025-10-08T09:15:09.83Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/41/0d/2ddfaa8b7e1cee6c490d46cb0a39742b19e2481600a7a0e96537e9c22f43/msgpack-1.1.2-cp312-cp312-win32.whl", hash = "sha256:1fff3d825d7859ac888b0fbda39a42d59193543920eda9d9bea44d958a878029", size = 65096, upload-time = "2025-10-08T09:15:11.11Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/8c/ec/d431eb7941fb55a31dd6ca3404d41fbb52d99172df2e7707754488390910/msgpack-1.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:1de460f0403172cff81169a30b9a92b260cb809c4cb7e2fc79ae8d0510c78b6b", size = 72708, upload-time = "2025-10-08T09:15:12.554Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c5/31/5b1a1f70eb0e87d1678e9624908f86317787b536060641d6798e3cf70ace/msgpack-1.1.2-cp312-cp312-win_arm64.whl", hash = "sha256:be5980f3ee0e6bd44f3a9e9dea01054f175b50c3e6cdb692bc9424c0bbb8bf69", size = 64119, upload-time = "2025-10-08T09:15:13.589Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6b/31/b46518ecc604d7edf3a4f94cb3bf021fc62aa301f0cb849936968164ef23/msgpack-1.1.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4efd7b5979ccb539c221a4c4e16aac1a533efc97f3b759bb5a5ac9f6d10383bf", size = 81212, upload-time = "2025-10-08T09:15:14.552Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/92/dc/c385f38f2c2433333345a82926c6bfa5ecfff3ef787201614317b58dd8be/msgpack-1.1.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:42eefe2c3e2af97ed470eec850facbe1b5ad1d6eacdbadc42ec98e7dcf68b4b7", size = 84315, upload-time = "2025-10-08T09:15:15.543Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d3/68/93180dce57f684a61a88a45ed13047558ded2be46f03acb8dec6d7c513af/msgpack-1.1.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1fdf7d83102bf09e7ce3357de96c59b627395352a4024f6e2458501f158bf999", size = 412721, upload-time = "2025-10-08T09:15:16.567Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5d/ba/459f18c16f2b3fc1a1ca871f72f07d70c07bf768ad0a507a698b8052ac58/msgpack-1.1.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fac4be746328f90caa3cd4bc67e6fe36ca2bf61d5c6eb6d895b6527e3f05071e", size = 424657, upload-time = "2025-10-08T09:15:17.825Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/38/f8/4398c46863b093252fe67368b44edc6c13b17f4e6b0e4929dbf0bdb13f23/msgpack-1.1.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:fffee09044073e69f2bad787071aeec727183e7580443dfeb8556cbf1978d162", size = 402668, upload-time = "2025-10-08T09:15:19.003Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/28/ce/698c1eff75626e4124b4d78e21cca0b4cc90043afb80a507626ea354ab52/msgpack-1.1.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5928604de9b032bc17f5099496417f113c45bc6bc21b5c6920caf34b3c428794", size = 419040, upload-time = "2025-10-08T09:15:20.183Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/67/32/f3cd1667028424fa7001d82e10ee35386eea1408b93d399b09fb0aa7875f/msgpack-1.1.2-cp313-cp313-win32.whl", hash = "sha256:a7787d353595c7c7e145e2331abf8b7ff1e6673a6b974ded96e6d4ec09f00c8c", size = 65037, upload-time = "2025-10-08T09:15:21.416Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/74/07/1ed8277f8653c40ebc65985180b007879f6a836c525b3885dcc6448ae6cb/msgpack-1.1.2-cp313-cp313-win_amd64.whl", hash = "sha256:a465f0dceb8e13a487e54c07d04ae3ba131c7c5b95e2612596eafde1dccf64a9", size = 72631, upload-time = "2025-10-08T09:15:22.431Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e5/db/0314e4e2db56ebcf450f277904ffd84a7988b9e5da8d0d61ab2d057df2b6/msgpack-1.1.2-cp313-cp313-win_arm64.whl", hash = "sha256:e69b39f8c0aa5ec24b57737ebee40be647035158f14ed4b40e6f150077e21a84", size = 64118, upload-time = "2025-10-08T09:15:23.402Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/22/71/201105712d0a2ff07b7873ed3c220292fb2ea5120603c00c4b634bcdafb3/msgpack-1.1.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e23ce8d5f7aa6ea6d2a2b326b4ba46c985dbb204523759984430db7114f8aa00", size = 81127, upload-time = "2025-10-08T09:15:24.408Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1b/9f/38ff9e57a2eade7bf9dfee5eae17f39fc0e998658050279cbb14d97d36d9/msgpack-1.1.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:6c15b7d74c939ebe620dd8e559384be806204d73b4f9356320632d783d1f7939", size = 84981, upload-time = "2025-10-08T09:15:25.812Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/8e/a9/3536e385167b88c2cc8f4424c49e28d49a6fc35206d4a8060f136e71f94c/msgpack-1.1.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:99e2cb7b9031568a2a5c73aa077180f93dd2e95b4f8d3b8e14a73ae94a9e667e", size = 411885, upload-time = "2025-10-08T09:15:27.22Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2f/40/dc34d1a8d5f1e51fc64640b62b191684da52ca469da9cd74e84936ffa4a6/msgpack-1.1.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:180759d89a057eab503cf62eeec0aa61c4ea1200dee709f3a8e9397dbb3b6931", size = 419658, upload-time = "2025-10-08T09:15:28.4Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3b/ef/2b92e286366500a09a67e03496ee8b8ba00562797a52f3c117aa2b29514b/msgpack-1.1.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:04fb995247a6e83830b62f0b07bf36540c213f6eac8e851166d8d86d83cbd014", size = 403290, upload-time = "2025-10-08T09:15:29.764Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/78/90/e0ea7990abea5764e4655b8177aa7c63cdfa89945b6e7641055800f6c16b/msgpack-1.1.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8e22ab046fa7ede9e36eeb4cfad44d46450f37bb05d5ec482b02868f451c95e2", size = 415234, upload-time = "2025-10-08T09:15:31.022Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/72/4e/9390aed5db983a2310818cd7d3ec0aecad45e1f7007e0cda79c79507bb0d/msgpack-1.1.2-cp314-cp314-win32.whl", hash = "sha256:80a0ff7d4abf5fecb995fcf235d4064b9a9a8a40a3ab80999e6ac1e30b702717", size = 66391, upload-time = "2025-10-08T09:15:32.265Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6e/f1/abd09c2ae91228c5f3998dbd7f41353def9eac64253de3c8105efa2082f7/msgpack-1.1.2-cp314-cp314-win_amd64.whl", hash = "sha256:9ade919fac6a3e7260b7f64cea89df6bec59104987cbea34d34a2fa15d74310b", size = 73787, upload-time = "2025-10-08T09:15:33.219Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6a/b0/9d9f667ab48b16ad4115c1935d94023b82b3198064cb84a123e97f7466c1/msgpack-1.1.2-cp314-cp314-win_arm64.whl", hash = "sha256:59415c6076b1e30e563eb732e23b994a61c159cec44deaf584e5cc1dd662f2af", size = 66453, upload-time = "2025-10-08T09:15:34.225Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/16/67/93f80545eb1792b61a217fa7f06d5e5cb9e0055bed867f43e2b8e012e137/msgpack-1.1.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:897c478140877e5307760b0ea66e0932738879e7aa68144d9b78ea4c8302a84a", size = 85264, upload-time = "2025-10-08T09:15:35.61Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/87/1c/33c8a24959cf193966ef11a6f6a2995a65eb066bd681fd085afd519a57ce/msgpack-1.1.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a668204fa43e6d02f89dbe79a30b0d67238d9ec4c5bd8a940fc3a004a47b721b", size = 89076, upload-time = "2025-10-08T09:15:36.619Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/fc/6b/62e85ff7193663fbea5c0254ef32f0c77134b4059f8da89b958beb7696f3/msgpack-1.1.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5559d03930d3aa0f3aacb4c42c776af1a2ace2611871c84a75afe436695e6245", size = 435242, upload-time = "2025-10-08T09:15:37.647Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c1/47/5c74ecb4cc277cf09f64e913947871682ffa82b3b93c8dad68083112f412/msgpack-1.1.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:70c5a7a9fea7f036b716191c29047374c10721c389c21e9ffafad04df8c52c90", size = 432509, upload-time = "2025-10-08T09:15:38.794Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/24/a4/e98ccdb56dc4e98c929a3f150de1799831c0a800583cde9fa022fa90602d/msgpack-1.1.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:f2cb069d8b981abc72b41aea1c580ce92d57c673ec61af4c500153a626cb9e20", size = 415957, upload-time = "2025-10-08T09:15:40.238Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/da/28/6951f7fb67bc0a4e184a6b38ab71a92d9ba58080b27a77d3e2fb0be5998f/msgpack-1.1.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d62ce1f483f355f61adb5433ebfd8868c5f078d1a52d042b0a998682b4fa8c27", size = 422910, upload-time = "2025-10-08T09:15:41.505Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f0/03/42106dcded51f0a0b5284d3ce30a671e7bd3f7318d122b2ead66ad289fed/msgpack-1.1.2-cp314-cp314t-win32.whl", hash = "sha256:1d1418482b1ee984625d88aa9585db570180c286d942da463533b238b98b812b", size = 75197, upload-time = "2025-10-08T09:15:42.954Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/15/86/d0071e94987f8db59d4eeb386ddc64d0bb9b10820a8d82bcd3e53eeb2da6/msgpack-1.1.2-cp314-cp314t-win_amd64.whl", hash = "sha256:5a46bf7e831d09470ad92dff02b8b1ac92175ca36b087f904a0519857c6be3ff", size = 85772, upload-time = "2025-10-08T09:15:43.954Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/81/f2/08ace4142eb281c12701fc3b93a10795e4d4dc7f753911d836675050f886/msgpack-1.1.2-cp314-cp314t-win_arm64.whl", hash = "sha256:d99ef64f349d5ec3293688e91486c5fdb925ed03807f64d98d205d2713c60b46", size = 70868, upload-time = "2025-10-08T09:15:44.959Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mypy"
|
name = "mypy"
|
||||||
version = "1.19.1"
|
version = "1.19.1"
|
||||||
@@ -383,6 +646,15 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" },
|
{ url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "packageurl-python"
|
||||||
|
version = "0.17.6"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/f5/d6/3b5a4e3cfaef7a53869a26ceb034d1ff5e5c27c814ce77260a96d50ab7bb/packageurl_python-0.17.6.tar.gz", hash = "sha256:1252ce3a102372ca6f86eb968e16f9014c4ba511c5c37d95a7f023e2ca6e5c25", size = 50618, upload-time = "2025-11-24T15:20:17.998Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b1/2f/c7277b7615a93f51b5fbc1eacfc1b75e8103370e786fd8ce2abf6e5c04ab/packageurl_python-0.17.6-py3-none-any.whl", hash = "sha256:31a85c2717bc41dd818f3c62908685ff9eebcb68588213745b14a6ee9e7df7c9", size = 36776, upload-time = "2025-11-24T15:20:16.962Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "packaging"
|
name = "packaging"
|
||||||
version = "26.0"
|
version = "26.0"
|
||||||
@@ -401,6 +673,70 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl", hash = "sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723", size = 55206, upload-time = "2026-01-27T03:59:45.137Z" },
|
{ url = "https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl", hash = "sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723", size = 55206, upload-time = "2026-01-27T03:59:45.137Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pip"
|
||||||
|
version = "26.0.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/48/83/0d7d4e9efe3344b8e2fe25d93be44f64b65364d3c8d7bc6dc90198d5422e/pip-26.0.1.tar.gz", hash = "sha256:c4037d8a277c89b320abe636d59f91e6d0922d08a05b60e85e53b296613346d8", size = 1812747, upload-time = "2026-02-05T02:20:18.702Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/de/f0/c81e05b613866b76d2d1066490adf1a3dbc4ee9d9c839961c3fc8a6997af/pip-26.0.1-py3-none-any.whl", hash = "sha256:bdb1b08f4274833d62c1aa29e20907365a2ceb950410df15fc9521bad440122b", size = 1787723, upload-time = "2026-02-05T02:20:16.416Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pip-api"
|
||||||
|
version = "0.0.34"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "pip" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/b9/f1/ee85f8c7e82bccf90a3c7aad22863cc6e20057860a1361083cd2adacb92e/pip_api-0.0.34.tar.gz", hash = "sha256:9b75e958f14c5a2614bae415f2adf7eeb54d50a2cfbe7e24fd4826471bac3625", size = 123017, upload-time = "2024-07-09T20:32:30.641Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/91/f7/ebf5003e1065fd00b4cbef53bf0a65c3d3e1b599b676d5383ccb7a8b88ba/pip_api-0.0.34-py3-none-any.whl", hash = "sha256:8b2d7d7c37f2447373aa2cf8b1f60a2f2b27a84e1e9e0294a3f6ef10eb3ba6bb", size = 120369, upload-time = "2024-07-09T20:32:29.099Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pip-audit"
|
||||||
|
version = "2.10.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "cachecontrol", extra = ["filecache"] },
|
||||||
|
{ name = "cyclonedx-python-lib" },
|
||||||
|
{ name = "packaging" },
|
||||||
|
{ name = "pip-api" },
|
||||||
|
{ name = "pip-requirements-parser" },
|
||||||
|
{ name = "platformdirs" },
|
||||||
|
{ name = "requests" },
|
||||||
|
{ name = "rich" },
|
||||||
|
{ name = "tomli" },
|
||||||
|
{ name = "tomli-w" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/bd/89/0e999b413facab81c33d118f3ac3739fd02c0622ccf7c4e82e37cebd8447/pip_audit-2.10.0.tar.gz", hash = "sha256:427ea5bf61d1d06b98b1ae29b7feacc00288a2eced52c9c58ceed5253ef6c2a4", size = 53776, upload-time = "2025-12-01T23:42:40.612Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/be/f3/4888f895c02afa085630a3a3329d1b18b998874642ad4c530e9a4d7851fe/pip_audit-2.10.0-py3-none-any.whl", hash = "sha256:16e02093872fac97580303f0848fa3ad64f7ecf600736ea7835a2b24de49613f", size = 61518, upload-time = "2025-12-01T23:42:39.193Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pip-requirements-parser"
|
||||||
|
version = "32.0.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "packaging" },
|
||||||
|
{ name = "pyparsing" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/5e/2a/63b574101850e7f7b306ddbdb02cb294380d37948140eecd468fae392b54/pip-requirements-parser-32.0.1.tar.gz", hash = "sha256:b4fa3a7a0be38243123cf9d1f3518da10c51bdb165a2b2985566247f9155a7d3", size = 209359, upload-time = "2022-12-21T15:25:22.732Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/54/d0/d04f1d1e064ac901439699ee097f58688caadea42498ec9c4b4ad2ef84ab/pip_requirements_parser-32.0.1-py3-none-any.whl", hash = "sha256:4659bc2a667783e7a15d190f6fccf8b2486685b6dba4c19c3876314769c57526", size = 35648, upload-time = "2022-12-21T15:25:21.046Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "platformdirs"
|
||||||
|
version = "4.9.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/6c/d5/763666321efaded11112de8b7a7f2273dd8d1e205168e73c334e54b0ab9a/platformdirs-4.9.1.tar.gz", hash = "sha256:f310f16e89c4e29117805d8328f7c10876eeff36c94eac879532812110f7d39f", size = 28392, upload-time = "2026-02-14T21:02:44.973Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/70/77/e8c95e95f1d4cdd88c90a96e31980df7e709e51059fac150046ad67fac63/platformdirs-4.9.1-py3-none-any.whl", hash = "sha256:61d8b967d34791c162d30d60737369cbbd77debad5b981c4bfda1842e71e0d66", size = 21307, upload-time = "2026-02-14T21:02:43.492Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pluggy"
|
name = "pluggy"
|
||||||
version = "1.6.0"
|
version = "1.6.0"
|
||||||
@@ -410,6 +746,18 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
|
{ url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "py-serializable"
|
||||||
|
version = "2.1.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "defusedxml" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/73/21/d250cfca8ff30c2e5a7447bc13861541126ce9bd4426cd5d0c9f08b5547d/py_serializable-2.1.0.tar.gz", hash = "sha256:9d5db56154a867a9b897c0163b33a793c804c80cee984116d02d49e4578fc103", size = 52368, upload-time = "2025-07-21T09:56:48.07Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/9b/bf/7595e817906a29453ba4d99394e781b6fabe55d21f3c15d240f85dd06bb1/py_serializable-2.1.0-py3-none-any.whl", hash = "sha256:b56d5d686b5a03ba4f4db5e769dc32336e142fc3bd4d68a8c25579ebb0a67304", size = 23045, upload-time = "2025-07-21T09:56:46.848Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pydantic"
|
name = "pydantic"
|
||||||
version = "2.12.5"
|
version = "2.12.5"
|
||||||
@@ -552,6 +900,15 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" },
|
{ url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pyparsing"
|
||||||
|
version = "3.3.2"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/f3/91/9c6ee907786a473bf81c5f53cf703ba0957b23ab84c264080fb5a450416f/pyparsing-3.3.2.tar.gz", hash = "sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc", size = 6851574, upload-time = "2026-01-21T03:57:59.36Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl", hash = "sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d", size = 122781, upload-time = "2026-01-21T03:57:55.912Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pytest"
|
name = "pytest"
|
||||||
version = "9.0.2"
|
version = "9.0.2"
|
||||||
@@ -598,6 +955,85 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl", hash = "sha256:3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861", size = 22424, upload-time = "2025-09-09T10:57:00.695Z" },
|
{ url = "https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl", hash = "sha256:3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861", size = 22424, upload-time = "2025-09-09T10:57:00.695Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pyyaml"
|
||||||
|
version = "6.0.3"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f4/a0/39350dd17dd6d6c6507025c0e53aef67a9293a6d37d3511f23ea510d5800/pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b", size = 184227, upload-time = "2025-09-25T21:31:46.04Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/05/14/52d505b5c59ce73244f59c7a50ecf47093ce4765f116cdb98286a71eeca2/pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956", size = 174019, upload-time = "2025-09-25T21:31:47.706Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/43/f7/0e6a5ae5599c838c696adb4e6330a59f463265bfa1e116cfd1fbb0abaaae/pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8", size = 740646, upload-time = "2025-09-25T21:31:49.21Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2f/3a/61b9db1d28f00f8fd0ae760459a5c4bf1b941baf714e207b6eb0657d2578/pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198", size = 840793, upload-time = "2025-09-25T21:31:50.735Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7a/1e/7acc4f0e74c4b3d9531e24739e0ab832a5edf40e64fbae1a9c01941cabd7/pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b", size = 770293, upload-time = "2025-09-25T21:31:51.828Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/8b/ef/abd085f06853af0cd59fa5f913d61a8eab65d7639ff2a658d18a25d6a89d/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0", size = 732872, upload-time = "2025-09-25T21:31:53.282Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1f/15/2bc9c8faf6450a8b3c9fc5448ed869c599c0a74ba2669772b1f3a0040180/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69", size = 758828, upload-time = "2025-09-25T21:31:54.807Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a3/00/531e92e88c00f4333ce359e50c19b8d1de9fe8d581b1534e35ccfbc5f393/pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e", size = 142415, upload-time = "2025-09-25T21:31:55.885Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2a/fa/926c003379b19fca39dd4634818b00dec6c62d87faf628d1394e137354d4/pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c", size = 158561, upload-time = "2025-09-25T21:31:57.406Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826, upload-time = "2025-09-25T21:31:58.655Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577, upload-time = "2025-09-25T21:32:00.088Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556, upload-time = "2025-09-25T21:32:01.31Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114, upload-time = "2025-09-25T21:32:03.376Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638, upload-time = "2025-09-25T21:32:04.553Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463, upload-time = "2025-09-25T21:32:06.152Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986, upload-time = "2025-09-25T21:32:07.367Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543, upload-time = "2025-09-25T21:32:08.95Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763, upload-time = "2025-09-25T21:32:09.96Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" },
|
||||||
|
{ 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 = "requests"
|
||||||
|
version = "2.32.5"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "certifi" },
|
||||||
|
{ name = "charset-normalizer" },
|
||||||
|
{ name = "idna" },
|
||||||
|
{ name = "urllib3" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "respx"
|
name = "respx"
|
||||||
version = "0.22.0"
|
version = "0.22.0"
|
||||||
@@ -610,6 +1046,19 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/8e/67/afbb0978d5399bc9ea200f1d4489a23c9a1dad4eee6376242b8182389c79/respx-0.22.0-py2.py3-none-any.whl", hash = "sha256:631128d4c9aba15e56903fb5f66fb1eff412ce28dd387ca3a81339e52dbd3ad0", size = 25127, upload-time = "2024-12-19T22:33:57.837Z" },
|
{ url = "https://files.pythonhosted.org/packages/8e/67/afbb0978d5399bc9ea200f1d4489a23c9a1dad4eee6376242b8182389c79/respx-0.22.0-py2.py3-none-any.whl", hash = "sha256:631128d4c9aba15e56903fb5f66fb1eff412ce28dd387ca3a81339e52dbd3ad0", size = 25127, upload-time = "2024-12-19T22:33:57.837Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rich"
|
||||||
|
version = "14.3.2"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "markdown-it-py" },
|
||||||
|
{ name = "pygments" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/74/99/a4cab2acbb884f80e558b0771e97e21e939c5dfb460f488d19df485e8298/rich-14.3.2.tar.gz", hash = "sha256:e712f11c1a562a11843306f5ed999475f09ac31ffb64281f73ab29ffdda8b3b8", size = 230143, upload-time = "2026-02-01T16:20:47.908Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ef/45/615f5babd880b4bd7d405cc0dc348234c5ffb6ed1ea33e152ede08b2072d/rich-14.3.2-py3-none-any.whl", hash = "sha256:08e67c3e90884651da3239ea668222d19bea7b589149d8014a21c633420dbb69", size = 309963, upload-time = "2026-02-01T16:20:46.078Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruff"
|
name = "ruff"
|
||||||
version = "0.15.0"
|
version = "0.15.0"
|
||||||
@@ -635,6 +1084,24 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/f6/b0/2d823f6e77ebe560f4e397d078487e8d52c1516b331e3521bc75db4272ca/ruff-0.15.0-py3-none-win_arm64.whl", hash = "sha256:c480d632cc0ca3f0727acac8b7d053542d9e114a462a145d0b00e7cd658c515a", size = 10865753, upload-time = "2026-02-03T17:53:03.014Z" },
|
{ url = "https://files.pythonhosted.org/packages/f6/b0/2d823f6e77ebe560f4e397d078487e8d52c1516b331e3521bc75db4272ca/ruff-0.15.0-py3-none-win_arm64.whl", hash = "sha256:c480d632cc0ca3f0727acac8b7d053542d9e114a462a145d0b00e7cd658c515a", size = 10865753, upload-time = "2026-02-03T17:53:03.014Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sortedcontainers"
|
||||||
|
version = "2.4.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/e8/c4/ba2f8066cceb6f23394729afe52f3bf7adec04bf9ed2c820b39e19299111/sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", size = 30594, upload-time = "2021-05-16T22:03:42.897Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0", size = 29575, upload-time = "2021-05-16T22:03:41.177Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "stevedore"
|
||||||
|
version = "5.6.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/96/5b/496f8abebd10c3301129abba7ddafd46c71d799a70c44ab080323987c4c9/stevedore-5.6.0.tar.gz", hash = "sha256:f22d15c6ead40c5bbfa9ca54aa7e7b4a07d59b36ae03ed12ced1a54cf0b51945", size = 516074, upload-time = "2025-11-20T10:06:07.264Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f4/40/8561ce06dc46fd17242c7724ab25b257a2ac1b35f4ebf551b40ce6105cfa/stevedore-5.6.0-py3-none-any.whl", hash = "sha256:4a36dccefd7aeea0c70135526cecb7766c4c84c473b1af68db23d541b6dc1820", size = 54428, upload-time = "2025-11-20T10:06:05.946Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tomli"
|
name = "tomli"
|
||||||
version = "2.4.0"
|
version = "2.4.0"
|
||||||
@@ -689,6 +1156,15 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/23/d1/136eb2cb77520a31e1f64cbae9d33ec6df0d78bdf4160398e86eec8a8754/tomli-2.4.0-py3-none-any.whl", hash = "sha256:1f776e7d669ebceb01dee46484485f43a4048746235e683bcdffacdf1fb4785a", size = 14477, upload-time = "2026-01-11T11:22:37.446Z" },
|
{ url = "https://files.pythonhosted.org/packages/23/d1/136eb2cb77520a31e1f64cbae9d33ec6df0d78bdf4160398e86eec8a8754/tomli-2.4.0-py3-none-any.whl", hash = "sha256:1f776e7d669ebceb01dee46484485f43a4048746235e683bcdffacdf1fb4785a", size = 14477, upload-time = "2026-01-11T11:22:37.446Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tomli-w"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/19/75/241269d1da26b624c0d5e110e8149093c759b7a286138f4efd61a60e75fe/tomli_w-1.2.0.tar.gz", hash = "sha256:2dd14fac5a47c27be9cd4c976af5a12d87fb1f0b4512f81d69cce3b35ae25021", size = 7184, upload-time = "2025-01-15T12:07:24.262Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c7/18/c86eb8e0202e32dd3df50d43d7ff9854f8e0603945ff398974c1d91ac1ef/tomli_w-1.2.0-py3-none-any.whl", hash = "sha256:188306098d013b691fcadc011abd66727d3c414c571bb01b1a174ba8c983cf90", size = 6675, upload-time = "2025-01-15T12:07:22.074Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typing-extensions"
|
name = "typing-extensions"
|
||||||
version = "4.15.0"
|
version = "4.15.0"
|
||||||
@@ -709,3 +1185,12 @@ sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac
|
|||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" },
|
{ url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "urllib3"
|
||||||
|
version = "2.6.3"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" }
|
||||||
|
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" },
|
||||||
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user