diff --git a/.env.example b/.env.example index 5c830b8..f62c539 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,4 @@ POSTGRES_USER=fabled POSTGRES_PASSWORD=fabled -POSTGRES_DB=fabledassistant +POSTGRES_DB=scribe SECRET_KEY=dev-secret-change-me diff --git a/.remember/tmp/save-session.pid b/.remember/tmp/save-session.pid index 4724b9a..b2256fa 100644 --- a/.remember/tmp/save-session.pid +++ b/.remember/tmp/save-session.pid @@ -1 +1 @@ -632037 +1425947 diff --git a/Dockerfile b/Dockerfile index 37fcb07..4c5e8c9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,7 +17,7 @@ COPY src/ src/ RUN --mount=type=cache,target=/root/.cache/pip \ pip install . -COPY --from=build-frontend /build/dist/ src/fabledassistant/static/ +COPY --from=build-frontend /build/dist/ src/scribe/static/ COPY alembic.ini . COPY alembic/ alembic/ @@ -30,4 +30,4 @@ ARG BUILD_VERSION=dev ENV APP_VERSION=$BUILD_VERSION EXPOSE 5000 -CMD ["sh", "-c", "alembic upgrade head && hypercorn 'fabledassistant.app:create_app()' --bind 0.0.0.0:5000 --keep-alive 600"] +CMD ["sh", "-c", "alembic upgrade head && hypercorn 'scribe.app:create_app()' --bind 0.0.0.0:5000 --keep-alive 600"] diff --git a/alembic/env.py b/alembic/env.py index badf43c..a7af4f8 100644 --- a/alembic/env.py +++ b/alembic/env.py @@ -4,8 +4,8 @@ from logging.config import fileConfig from alembic import context from sqlalchemy.ext.asyncio import create_async_engine -from fabledassistant.config import Config -from fabledassistant.models import Base +from scribe.config import Config +from scribe.models import Base config = context.config if config.config_file_name is not None: diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 5a89b8e..3d07c21 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -2,13 +2,13 @@ services: app: image: git.fabledsword.com/bvandeusen/fabledscribe:latest environment: - DATABASE_URL: "postgresql+asyncpg://fabled:${DB_PASSWORD}@db:5432/fabledassistant" + DATABASE_URL: "postgresql+asyncpg://scribe:${DB_PASSWORD}@db:5432/scribe" SECRET_KEY: "${SECRET_KEY}" LOG_LEVEL: "${LOG_LEVEL:-INFO}" TRUST_PROXY_HEADERS: "true" SECURE_COOKIES: "true" networks: - - fabledassistant_backend + - scribe_backend healthcheck: test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:5000/api/health')"] interval: 30s @@ -26,16 +26,16 @@ services: volumes: - pgdata:/var/lib/postgresql/data environment: - POSTGRES_USER: fabled + POSTGRES_USER: scribe POSTGRES_PASSWORD: "${DB_PASSWORD}" - POSTGRES_DB: fabledassistant + POSTGRES_DB: scribe networks: - - fabledassistant_backend + - scribe_backend # Lenient by design: a transient host exec/healthcheck stall (incident: # runc setns failures -> "unhealthy" -> SIGKILL -> crash loop) must never # escalate to killing the DB. Health here only gates app startup order. healthcheck: - test: ["CMD-SHELL", "pg_isready -U fabled"] + test: ["CMD-SHELL", "pg_isready -U scribe"] interval: 30s timeout: 10s retries: 10 @@ -51,5 +51,5 @@ volumes: pgdata: networks: - fabledassistant_backend: + scribe_backend: driver: overlay diff --git a/docker-compose.quickstart.yml b/docker-compose.quickstart.yml index 0e7e00b..6fd53d0 100644 --- a/docker-compose.quickstart.yml +++ b/docker-compose.quickstart.yml @@ -18,7 +18,7 @@ services: ports: - "5000:5000" environment: - DATABASE_URL: "postgresql+asyncpg://fabled:fabled@db:5432/fabledassistant" + DATABASE_URL: "postgresql+asyncpg://scribe:scribe@db:5432/scribe" SECRET_KEY: "${SECRET_KEY:-change-me-in-production}" LOG_LEVEL: "${LOG_LEVEL:-INFO}" volumes: @@ -40,13 +40,13 @@ services: volumes: - pgdata:/var/lib/postgresql/data environment: - POSTGRES_USER: fabled - POSTGRES_PASSWORD: fabled - POSTGRES_DB: fabledassistant + POSTGRES_USER: scribe + POSTGRES_PASSWORD: scribe + POSTGRES_DB: scribe # Lenient by design: a transient host exec/healthcheck stall must never # escalate to killing the DB. Health here only gates app startup order. healthcheck: - test: ["CMD-SHELL", "pg_isready -U fabled"] + test: ["CMD-SHELL", "pg_isready -U scribe"] interval: 30s timeout: 10s retries: 10 diff --git a/docker-compose.yml b/docker-compose.yml index 22602be..eba260d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,7 +11,7 @@ services: # To use a bind mount instead (gives direct host access to all app data): # - ./data:/data environment: - DATABASE_URL: "postgresql+asyncpg://${POSTGRES_USER:-fabled}:${POSTGRES_PASSWORD:-fabled}@db:5432/${POSTGRES_DB:-fabledassistant}" + DATABASE_URL: "postgresql+asyncpg://${POSTGRES_USER:-scribe}:${POSTGRES_PASSWORD:-scribe}@db:5432/${POSTGRES_DB:-scribe}" SECRET_KEY: "${SECRET_KEY:-dev-secret-change-me}" # Uncomment if you have a SearXNG instance you want to surface in the # Integrations tab as a configured web-search backend: @@ -30,13 +30,13 @@ services: volumes: - pgdata:/var/lib/postgresql/data environment: - POSTGRES_USER: ${POSTGRES_USER:-fabled} - POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-fabled} - POSTGRES_DB: ${POSTGRES_DB:-fabledassistant} + POSTGRES_USER: ${POSTGRES_USER:-scribe} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-scribe} + POSTGRES_DB: ${POSTGRES_DB:-scribe} # Lenient by design: a transient host exec/healthcheck stall must never # escalate to killing the DB. Health here only gates app startup order. healthcheck: - test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-fabled}"] + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-scribe}"] interval: 30s timeout: 10s retries: 10 diff --git a/docs/2026-03-23-fable-mcp-design.md b/docs/2026-03-23-fable-mcp-design.md index aa1a548..3edeeb2 100644 --- a/docs/2026-03-23-fable-mcp-design.md +++ b/docs/2026-03-23-fable-mcp-design.md @@ -12,7 +12,7 @@ A Python MCP (Model Context Protocol) server that lets Claude directly interface The work is split into two sub-projects: -1. **Fable API Key Feature** — additions to the main `fabledassistant` project to support bearer token authentication +1. **Fable API Key Feature** — additions to the main `scribe` project to support bearer token authentication 2. **Fable MCP Server** — a new standalone Python package at `fable-mcp/` in the same repo root A third sub-project (Forgejo MCP for CI/CD automation) is planned as a follow-on after the Fable MCP is working. @@ -134,7 +134,7 @@ The MCP tool reads tokens until it receives `type: "done"`, then returns `respon ### Location -`fable-mcp/` at the repository root, alongside `src/`, `frontend/`, `alembic/`. It is **not** part of the main Docker build and has no import relationship with `fabledassistant`. It will be extracted to its own Forgejo repo once stable. +`fable-mcp/` at the repository root, alongside `src/`, `frontend/`, `alembic/`. It is **not** part of the main Docker build and has no import relationship with `scribe`. It will be extracted to its own Forgejo repo once stable. ### Package Structure @@ -256,7 +256,7 @@ Claude Code spawns the process over stdio automatically. No Docker, no daemon. ## Build & Repo Plan -1. Implement and test within `fabledassistant/fable-mcp/` +1. Implement and test within `scribe/fable-mcp/` 2. Once stable, extract to a new Forgejo repo (`bvandeusen/fable-mcp`) 3. Forgejo MCP (Gitea MCP) added as a second MCP server to automate build/push/config workflows — separate spec when ready diff --git a/docs/2026-03-23-fable-mcp-plan.md b/docs/2026-03-23-fable-mcp-plan.md index 8071a2e..fa061a4 100644 --- a/docs/2026-03-23-fable-mcp-plan.md +++ b/docs/2026-03-23-fable-mcp-plan.md @@ -6,7 +6,7 @@ **Architecture:** Phase 1 adds bearer token auth to the existing Quart app (new `api_keys` table, updated `_check_auth`, settings UI tab). Phase 2 is a standalone `fable-mcp/` Python package using the `mcp[cli]` SDK that calls the Fable HTTP API via `httpx`. The two phases are sequential — Phase 2 can be built/tested independently using a write-scoped API key once Phase 1 is done. -**Deployment decision:** `fable-mcp/` stays permanently inside the `fabledassistant` repo. It will always be versioned alongside the backend it targets. Future Task A will serve the package from the running Fable Docker image so users can install it directly from their instance. +**Deployment decision:** `fable-mcp/` stays permanently inside the `scribe` repo. It will always be versioned alongside the backend it targets. Future Task A will serve the package from the running Fable Docker image so users can install it directly from their instance. **Tech Stack:** Python 3.12, Quart (Phase 1); `mcp[cli]`, `httpx`, `python-dotenv` (Phase 2); Vue 3 + TypeScript (Settings UI); pytest for both. @@ -23,16 +23,16 @@ | Action | File | Purpose | |--------|------|---------| | Create | `alembic/versions/0027_add_api_keys.py` | DB migration for `api_keys` table | -| Create | `src/fabledassistant/models/api_key.py` | `ApiKey` SQLAlchemy model | -| Modify | `src/fabledassistant/models/__init__.py` | Export `ApiKey` | -| Create | `src/fabledassistant/services/api_keys.py` | create/list/revoke/lookup service functions | -| Modify | `src/fabledassistant/auth.py` | Add bearer token check before session fallback | -| Create | `src/fabledassistant/routes/api_keys.py` | GET/POST/DELETE `/api/api-keys` blueprint | -| Modify | `src/fabledassistant/app.py` | Register `api_keys_bp` and `search_bp` | -| Modify | `src/fabledassistant/services/chat.py:17-30` | Add `conversation_type` param to `create_conversation` | -| Modify | `src/fabledassistant/services/chat.py:123-135` | Exclude `"mcp"` type from `cleanup_old_conversations` | -| Modify | `src/fabledassistant/routes/chat.py:73-79` | Pass `conversation_type` from POST body | -| Create | `src/fabledassistant/routes/search.py` | `GET /api/search` semantic search endpoint | +| Create | `src/scribe/models/api_key.py` | `ApiKey` SQLAlchemy model | +| Modify | `src/scribe/models/__init__.py` | Export `ApiKey` | +| Create | `src/scribe/services/api_keys.py` | create/list/revoke/lookup service functions | +| Modify | `src/scribe/auth.py` | Add bearer token check before session fallback | +| Create | `src/scribe/routes/api_keys.py` | GET/POST/DELETE `/api/api-keys` blueprint | +| Modify | `src/scribe/app.py` | Register `api_keys_bp` and `search_bp` | +| Modify | `src/scribe/services/chat.py:17-30` | Add `conversation_type` param to `create_conversation` | +| Modify | `src/scribe/services/chat.py:123-135` | Exclude `"mcp"` type from `cleanup_old_conversations` | +| Modify | `src/scribe/routes/chat.py:73-79` | Pass `conversation_type` from POST body | +| Create | `src/scribe/routes/search.py` | `GET /api/search` semantic search endpoint | | Modify | `frontend/src/views/SettingsView.vue` | Add "API Keys" tab | | Create | `tests/test_api_keys.py` | Unit tests for service + auth | | Create | `tests/test_search_route.py` | Unit test for search endpoint | @@ -42,13 +42,13 @@ ### Task 1: ApiKey model + migration **Files:** -- Create: `src/fabledassistant/models/api_key.py` +- Create: `src/scribe/models/api_key.py` - Create: `alembic/versions/0027_add_api_keys.py` -- Modify: `src/fabledassistant/models/__init__.py` +- Modify: `src/scribe/models/__init__.py` - [ ] **Step 1: Write the model** -Create `src/fabledassistant/models/api_key.py`: +Create `src/scribe/models/api_key.py`: ```python from datetime import datetime, timezone @@ -56,8 +56,8 @@ from datetime import datetime, timezone from sqlalchemy import DateTime, ForeignKey, Index, Integer, Text from sqlalchemy.orm import Mapped, mapped_column -from fabledassistant.models import Base -from fabledassistant.models.base import CreatedAtMixin +from scribe.models import Base +from scribe.models.base import CreatedAtMixin class ApiKey(Base, CreatedAtMixin): @@ -97,10 +97,10 @@ class ApiKey(Base, CreatedAtMixin): - [ ] **Step 2: Export from models __init__** -In `src/fabledassistant/models/__init__.py`, add after the last import line: +In `src/scribe/models/__init__.py`, add after the last import line: ```python -from fabledassistant.models.api_key import ApiKey # noqa: E402, F401 +from scribe.models.api_key import ApiKey # noqa: E402, F401 ``` - [ ] **Step 3: Write the migration** @@ -155,8 +155,8 @@ Expected: `Running upgrade 0026 -> 0027, add api_keys table` - [ ] **Step 5: Commit** ```bash -git add src/fabledassistant/models/api_key.py \ - src/fabledassistant/models/__init__.py \ +git add src/scribe/models/api_key.py \ + src/scribe/models/__init__.py \ alembic/versions/0027_add_api_keys.py git commit -m "feat: add ApiKey model and migration 0027" ``` @@ -166,7 +166,7 @@ git commit -m "feat: add ApiKey model and migration 0027" ### Task 2: ApiKey service **Files:** -- Create: `src/fabledassistant/services/api_keys.py` +- Create: `src/scribe/services/api_keys.py` - Create: `tests/test_api_keys.py` (service tests) - [ ] **Step 1: Write the failing tests** @@ -181,7 +181,7 @@ from unittest.mock import AsyncMock, MagicMock, patch import pytest -from fabledassistant.services.api_keys import ( +from scribe.services.api_keys import ( _hash_key, generate_key, create_api_key, @@ -212,7 +212,7 @@ def test_hash_key_is_sha256(): def test_generate_key_prefix(): key = "fmcp_abcdefghijklmnop" # prefix is first 12 chars of the full key - from fabledassistant.services.api_keys import _key_prefix + from scribe.services.api_keys import _key_prefix assert _key_prefix(key) == "fmcp_abcdefg" # first 12 chars @@ -222,7 +222,7 @@ async def test_create_api_key_returns_full_key(): mock_key_obj.id = 1 mock_key_obj.to_dict.return_value = {"id": 1, "name": "test", "scope": "read", "key_prefix": "fmcp_xxx"} - with patch("fabledassistant.services.api_keys.async_session") as mock_session_ctx: + with patch("scribe.services.api_keys.async_session") as mock_session_ctx: mock_session = AsyncMock() mock_session.__aenter__ = AsyncMock(return_value=mock_session) mock_session.__aexit__ = AsyncMock(return_value=False) @@ -238,7 +238,7 @@ async def test_create_api_key_returns_full_key(): @pytest.mark.asyncio async def test_lookup_key_returns_none_for_unknown(): - with patch("fabledassistant.services.api_keys.async_session") as mock_session_ctx: + with patch("scribe.services.api_keys.async_session") as mock_session_ctx: mock_session = AsyncMock() mock_session.__aenter__ = AsyncMock(return_value=mock_session) mock_session.__aexit__ = AsyncMock(return_value=False) @@ -262,7 +262,7 @@ Expected: `ImportError` or `ModuleNotFoundError` (service doesn't exist yet) - [ ] **Step 3: Write the service** -Create `src/fabledassistant/services/api_keys.py`: +Create `src/scribe/services/api_keys.py`: ```python import hashlib @@ -271,8 +271,8 @@ from datetime import datetime, timezone from sqlalchemy import select -from fabledassistant.models import async_session -from fabledassistant.models.api_key import ApiKey +from scribe.models import async_session +from scribe.models.api_key import ApiKey def generate_key() -> str: @@ -365,7 +365,7 @@ Expected: all tests pass - [ ] **Step 5: Commit** ```bash -git add src/fabledassistant/services/api_keys.py tests/test_api_keys.py +git add src/scribe/services/api_keys.py tests/test_api_keys.py git commit -m "feat: add ApiKey service with create/list/revoke/lookup" ``` @@ -374,7 +374,7 @@ git commit -m "feat: add ApiKey service with create/list/revoke/lookup" ### Task 3: Auth middleware — bearer token support **Files:** -- Modify: `src/fabledassistant/auth.py` +- Modify: `src/scribe/auth.py` - Modify: `tests/test_api_keys.py` (add auth middleware tests) - [ ] **Step 1: Add auth middleware tests** @@ -400,7 +400,7 @@ def test_scope_validation(): async def test_bearer_token_path_sets_g_user(monkeypatch): """Valid bearer token authenticates and sets g.user and g.api_key.""" from unittest.mock import AsyncMock, MagicMock - from fabledassistant.auth import _check_auth + from scribe.auth import _check_auth # Mock ApiKey object fake_key = MagicMock() @@ -411,8 +411,8 @@ async def test_bearer_token_path_sets_g_user(monkeypatch): fake_user = MagicMock() fake_user.role = "user" - monkeypatch.setattr("fabledassistant.auth.lookup_key", AsyncMock(return_value=fake_key)) - monkeypatch.setattr("fabledassistant.auth.get_user_by_id", AsyncMock(return_value=fake_user)) + monkeypatch.setattr("scribe.auth.lookup_key", AsyncMock(return_value=fake_key)) + monkeypatch.setattr("scribe.auth.get_user_by_id", AsyncMock(return_value=fake_user)) called_with_user = {} @@ -434,7 +434,7 @@ async def test_bearer_token_path_sets_g_user(monkeypatch): async with app.test_request_context("/test", method="GET", headers={"Authorization": "Bearer fmcp_valid"}): # Just verify _check_auth calls lookup_key with the right token - import fabledassistant.auth as auth_module + import scribe.auth as auth_module auth_module.lookup_key.assert_called_with # callable @@ -442,7 +442,7 @@ async def test_bearer_token_path_sets_g_user(monkeypatch): async def test_read_only_key_blocked_on_post(): """Read-only API key returns 403 on non-GET requests.""" from unittest.mock import AsyncMock, MagicMock - from fabledassistant.auth import _check_auth + from scribe.auth import _check_auth from quart import Quart fake_key = MagicMock() @@ -459,8 +459,8 @@ async def test_read_only_key_blocked_on_post(): headers={"Authorization": "Bearer fmcp_readonly"}), ): from unittest.mock import patch - with patch("fabledassistant.auth.lookup_key", AsyncMock(return_value=fake_key)), \ - patch("fabledassistant.auth.get_user_by_id", AsyncMock(return_value=fake_user)): + with patch("scribe.auth.lookup_key", AsyncMock(return_value=fake_key)), \ + patch("scribe.auth.get_user_by_id", AsyncMock(return_value=fake_user)): async def dummy(): return "ok" @@ -481,15 +481,15 @@ docker compose run --rm app pytest tests/test_api_keys.py -v - [ ] **Step 3: Update auth.py** -Replace `src/fabledassistant/auth.py` with: +Replace `src/scribe/auth.py` with: ```python import functools from quart import g, jsonify, request, session -from fabledassistant.services.auth import get_user_by_id -from fabledassistant.services.api_keys import lookup_key +from scribe.services.auth import get_user_by_id +from scribe.services.api_keys import lookup_key def _check_auth(f, required_role: str | None = None): @@ -556,7 +556,7 @@ Expected: all existing tests still pass - [ ] **Step 5: Commit** ```bash -git add src/fabledassistant/auth.py tests/test_api_keys.py +git add src/scribe/auth.py tests/test_api_keys.py git commit -m "feat: add bearer token auth to _check_auth, falls back to session" ``` @@ -565,18 +565,18 @@ git commit -m "feat: add bearer token auth to _check_auth, falls back to session ### Task 4: API key routes + app registration **Files:** -- Create: `src/fabledassistant/routes/api_keys.py` -- Modify: `src/fabledassistant/app.py` +- Create: `src/scribe/routes/api_keys.py` +- Modify: `src/scribe/app.py` - [ ] **Step 1: Write the routes** -Create `src/fabledassistant/routes/api_keys.py`: +Create `src/scribe/routes/api_keys.py`: ```python from quart import Blueprint, jsonify, request -from fabledassistant.auth import login_required, get_current_user_id -from fabledassistant.services.api_keys import create_api_key, list_api_keys, revoke_api_key +from scribe.auth import login_required, get_current_user_id +from scribe.services.api_keys import create_api_key, list_api_keys, revoke_api_key api_keys_bp = Blueprint("api_keys", __name__, url_prefix="/api/api-keys") @@ -619,10 +619,10 @@ async def revoke_key_route(key_id: int): - [ ] **Step 2: Register in app.py** -In `src/fabledassistant/app.py`, add the import alongside the other route imports: +In `src/scribe/app.py`, add the import alongside the other route imports: ```python -from fabledassistant.routes.api_keys import api_keys_bp +from scribe.routes.api_keys import api_keys_bp ``` And add the registration line after `app.register_blueprint(users_bp)`: @@ -651,7 +651,7 @@ curl -s http://localhost:8080/api/auth/me \ - [ ] **Step 4: Commit** ```bash -git add src/fabledassistant/routes/api_keys.py src/fabledassistant/app.py +git add src/scribe/routes/api_keys.py src/scribe/app.py git commit -m "feat: add API key CRUD routes and register blueprint" ``` @@ -660,14 +660,14 @@ git commit -m "feat: add API key CRUD routes and register blueprint" ### Task 5: Conversation type wiring **Files:** -- Modify: `src/fabledassistant/services/chat.py` (lines 17-30 and 123-135) -- Modify: `src/fabledassistant/routes/chat.py` (lines 73-79) +- Modify: `src/scribe/services/chat.py` (lines 17-30 and 123-135) +- Modify: `src/scribe/routes/chat.py` (lines 73-79) Note: `Conversation.conversation_type` already exists in the model. `list_conversations` already filters by `conv_type`. This task only wires up creation and retention exclusion. - [ ] **Step 1: Update `create_conversation` service** -In `src/fabledassistant/services/chat.py`, change the function signature at line 17: +In `src/scribe/services/chat.py`, change the function signature at line 17: ```python async def create_conversation( @@ -692,7 +692,7 @@ async def create_conversation( - [ ] **Step 2: Update `cleanup_old_conversations` to exclude "mcp"** -In `src/fabledassistant/services/chat.py`, update the WHERE clause at line 130: +In `src/scribe/services/chat.py`, update the WHERE clause at line 130: ```python result = await session.execute( @@ -708,7 +708,7 @@ result = await session.execute( - [ ] **Step 3: Update the POST route to accept conversation_type** -In `src/fabledassistant/routes/chat.py`, update `create_conversation_route` (around line 73): +In `src/scribe/routes/chat.py`, update `create_conversation_route` (around line 73): ```python @chat_bp.route("/conversations", methods=["POST"]) @@ -737,7 +737,7 @@ Expected: all tests pass - [ ] **Step 5: Commit** ```bash -git add src/fabledassistant/services/chat.py src/fabledassistant/routes/chat.py +git add src/scribe/services/chat.py src/scribe/routes/chat.py git commit -m "feat: wire conversation_type through create_conversation, exclude mcp from retention sweep" ``` @@ -746,8 +746,8 @@ git commit -m "feat: wire conversation_type through create_conversation, exclude ### Task 6: Semantic search endpoint **Files:** -- Create: `src/fabledassistant/routes/search.py` -- Modify: `src/fabledassistant/app.py` +- Create: `src/scribe/routes/search.py` +- Modify: `src/scribe/app.py` - Create: `tests/test_search_route.py` - [ ] **Step 1: Write the failing test** @@ -762,7 +762,7 @@ from unittest.mock import patch, AsyncMock def test_content_type_mapping(): """Verify content_type string maps to correct is_task value.""" - from fabledassistant.routes.search import _content_type_to_is_task + from scribe.routes.search import _content_type_to_is_task assert _content_type_to_is_task("note") is False assert _content_type_to_is_task("task") is True assert _content_type_to_is_task("all") is None @@ -779,13 +779,13 @@ Expected: `ImportError` (module doesn't exist yet) - [ ] **Step 3: Write the route** -Create `src/fabledassistant/routes/search.py`: +Create `src/scribe/routes/search.py`: ```python from quart import Blueprint, jsonify, request -from fabledassistant.auth import login_required, get_current_user_id -from fabledassistant.services.embeddings import semantic_search_notes +from scribe.auth import login_required, get_current_user_id +from scribe.services.embeddings import semantic_search_notes search_bp = Blueprint("search", __name__, url_prefix="/api/search") @@ -834,9 +834,9 @@ Note: check `services/embeddings.py` to confirm the return type of `semantic_sea - [ ] **Step 4: Register in app.py** -Add to imports in `src/fabledassistant/app.py`: +Add to imports in `src/scribe/app.py`: ```python -from fabledassistant.routes.search import search_bp +from scribe.routes.search import search_bp ``` Add registration: @@ -855,8 +855,8 @@ Expected: all tests pass - [ ] **Step 6: Commit** ```bash -git add src/fabledassistant/routes/search.py \ - src/fabledassistant/app.py \ +git add src/scribe/routes/search.py \ + src/scribe/app.py \ tests/test_search_route.py git commit -m "feat: add GET /api/search semantic search endpoint" ``` @@ -1905,7 +1905,7 @@ async def send_message( return f"Error: {e}" ``` -Note: verify the Fable message POST endpoint path by checking `src/fabledassistant/routes/chat.py` — search for the route that accepts a user message and triggers generation. Adjust `/api/chat/conversations/{conv_id}/messages` if the actual path differs. +Note: verify the Fable message POST endpoint path by checking `src/scribe/routes/chat.py` — search for the route that accepts a user message and triggers generation. Adjust `/api/chat/conversations/{conv_id}/messages` if the actual path differs. - [ ] **Step 4: Run tests** diff --git a/docs/2026-03-29-voice-s2s-design.md b/docs/2026-03-29-voice-s2s-design.md index 3e0e88d..8a31dc2 100644 --- a/docs/2026-03-29-voice-s2s-design.md +++ b/docs/2026-03-29-voice-s2s-design.md @@ -39,20 +39,20 @@ ## New Backend Files -### `src/fabledassistant/services/stt.py` +### `src/scribe/services/stt.py` Lazy singleton `WhisperModel` loader. Public API: - `load_stt_model()` — called at startup via `asyncio.create_task` - `transcribe(audio_bytes, mime_type) -> str` — runs in `run_in_executor`; writes bytes to `NamedTemporaryFile`, returns concatenated segment text - `stt_available() -> bool` -### `src/fabledassistant/services/tts.py` +### `src/scribe/services/tts.py` Lazy singleton `KPipeline` loader. Public API: - `load_tts_model()` — called at startup - `synthesise(text, voice, speed) -> bytes` — runs in `run_in_executor`; returns WAV bytes (24kHz, 16-bit mono) - `list_voices() -> list[dict]` — returns static list of known Kokoro voice IDs + labels - `tts_available() -> bool` -### `src/fabledassistant/routes/voice.py` +### `src/scribe/routes/voice.py` Blueprint at `/api/voice`, all routes `@login_required`. | Endpoint | Method | Description | @@ -66,27 +66,27 @@ Blueprint at `/api/voice`, all routes `@login_required`. ## Modified Backend Files -### `src/fabledassistant/app.py` +### `src/scribe/app.py` - Register `voice_bp` blueprint - In `startup()`: `asyncio.create_task(load_stt_model())` + `asyncio.create_task(load_tts_model())` when `VOICE_ENABLED` -### `src/fabledassistant/config.py` +### `src/scribe/config.py` - Add 4 new env var attributes - Add validation in `validate()` -### `src/fabledassistant/services/llm.py` +### `src/scribe/services/llm.py` - Add `voice_mode: bool = False` and `voice_speech_style: str = "conversational"` to `build_context()` - When `voice_mode=True`, prepend: *"Respond naturally as if speaking aloud. No markdown, bullet points, headers, or code blocks. Complete sentences only."* - Append style modifier based on `voice_speech_style` -### `src/fabledassistant/services/generation_task.py` +### `src/scribe/services/generation_task.py` - Add `voice_mode: bool = False` to `run_generation()` - Read `voice_speech_style` from settings when voice_mode; pass both to `build_context()` -### `src/fabledassistant/routes/chat.py` +### `src/scribe/routes/chat.py` - Allow `"voice"` in `conversation_type` whitelist -### `src/fabledassistant/services/chat.py` +### `src/scribe/services/chat.py` - Exclude `conversation_type == "voice"` from auto-cleanup retention --- diff --git a/docs/architecture.md b/docs/architecture.md index 2c8cf3c..6b800b9 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -20,7 +20,7 @@ │ Docker Compose │ │ │ │ ┌──────────────────────┐ ┌────────────┐ │ -│ │ fabledassistant │ │ ollama │ │ +│ │ scribe │ │ ollama │ │ │ │ ┌────────────────┐ │ │ │ │ │ │ │ Quart Server │ │ │ LLM API │ │ │ │ │ ┌──────────┐ │ │ │ │ │ @@ -43,7 +43,7 @@ ## Project Structure ``` -fabledassistant/ +scribe/ ├── docker-compose.yml # Development stack ├── docker-compose.prod.yml # Production stack (Docker Swarm) ├── Dockerfile # Multi-stage build (Node → Python) @@ -54,7 +54,7 @@ fabledassistant/ │ ├── server.py # FastMCP tool registrations │ ├── client.py # FableClient (httpx wrapper) │ └── tools/ # Tool modules (notes, tasks, projects, …) -├── src/fabledassistant/ +├── src/scribe/ │ ├── app.py # Quart app factory + blueprint registration │ ├── config.py # Config class (reads env vars) │ ├── auth.py # login_required decorator, session checks @@ -169,7 +169,7 @@ Permission resolution is centralised in `services/access.py`. `get_project_permi ## Detailed File Reference -### Backend (`src/fabledassistant/`) +### Backend (`src/scribe/`) | File | Responsibility | |------|---------------| diff --git a/docs/configuration.md b/docs/configuration.md index f8a76a8..f456ed3 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -8,7 +8,7 @@ Configuration is via environment variables. The `docker-compose.yml` file sets d | Variable | Default | Description | |----------|---------|-------------| -| `DATABASE_URL` | `postgresql+asyncpg://fabled:fabled@db/fabledassistant` | PostgreSQL async connection string | +| `DATABASE_URL` | `postgresql+asyncpg://fabled:fabled@db/scribe` | PostgreSQL async connection string | | `SECRET_KEY` | `dev-secret-change-me` | Session signing key — **change this in production** | | `SECRET_KEY_FILE` | — | Path to a Docker secret file containing the key (alternative to `SECRET_KEY`) | | `LOG_LEVEL` | `INFO` | Logging verbosity (`DEBUG`, `INFO`, `WARNING`, `ERROR`) | @@ -91,7 +91,7 @@ The production compose file adds: ```bash # Create Docker secrets echo "$(python3 -c 'import secrets; print(secrets.token_hex(32))')" | docker secret create fabled_secret_key - -echo "postgresql+asyncpg://fabled:strongpassword@db/fabledassistant" | docker secret create fabled_db_url - +echo "postgresql+asyncpg://fabled:strongpassword@db/scribe" | docker secret create fabled_db_url - # Deploy docker stack deploy -c docker-compose.prod.yml fabled diff --git a/docs/development.md b/docs/development.md index cbc4fa7..8a9a9f2 100644 --- a/docs/development.md +++ b/docs/development.md @@ -93,7 +93,7 @@ config on the runner host. ### Docker Registry -Images pushed to: `git.fabledsword.com/bvandeusen/fabledassistant` +Images pushed to: `git.fabledsword.com/bvandeusen/scribe` Cache tag: `:cache` (reduces build time ~80%) Required secrets (repo → Settings → Secrets → Actions): @@ -140,8 +140,8 @@ Current migration sequence (all idempotent raw SQL): ### Backend -- Services: `async with async_session() as session:` — import from `fabledassistant.models` -- No `fabledassistant.database` module +- Services: `async with async_session() as session:` — import from `scribe.models` +- No `scribe.database` module - Blueprint per resource: `routes/notes.py`, `routes/tasks.py`, etc. - All business logic in `services/`; routes are thin wrappers - Permission checks via `services/access.py` — never inline ownership checks in routes diff --git a/frontend/package-lock.json b/frontend/package-lock.json index a460207..dc64e61 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,11 +1,11 @@ { - "name": "fabledassistant-frontend", + "name": "scribe-frontend", "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "fabledassistant-frontend", + "name": "scribe-frontend", "version": "0.1.0", "dependencies": { "@fullcalendar/core": "^6.1.20", diff --git a/frontend/package.json b/frontend/package.json index 8d17f4b..927b780 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,5 +1,5 @@ { - "name": "fabledassistant-frontend", + "name": "scribe-frontend", "private": true, "version": "0.1.0", "type": "module", diff --git a/frontend/src/views/SettingsView.vue b/frontend/src/views/SettingsView.vue index b95baef..25c2c19 100644 --- a/frontend/src/views/SettingsView.vue +++ b/frontend/src/views/SettingsView.vue @@ -510,7 +510,7 @@ async function exportData(scope: "user" | "full") { const blob = new Blob([JSON.stringify(data, null, 2)], { type: "application/json" }); const a = document.createElement("a"); a.href = URL.createObjectURL(blob); - a.download = `fabledassistant-backup-${scope}-${new Date().toISOString().slice(0, 10)}.json`; + a.download = `scribe-backup-${scope}-${new Date().toISOString().slice(0, 10)}.json`; a.click(); URL.revokeObjectURL(a.href); toastStore.show("Backup downloaded"); @@ -533,7 +533,7 @@ async function exportNotes(format: "markdown" | "json") { const stamp = new Date().toISOString().slice(0, 10); const a = document.createElement("a"); a.href = URL.createObjectURL(blob); - a.download = `fabledassistant-${stamp}.${ext}`; + a.download = `scribe-${stamp}.${ext}`; a.click(); URL.revokeObjectURL(a.href); toastStore.show("Export downloaded"); diff --git a/pyproject.toml b/pyproject.toml index a5c927e..7c19f4e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ requires = ["setuptools>=68.0", "setuptools-scm>=8.0"] build-backend = "setuptools.build_meta" [project] -name = "fabledassistant" +name = "scribe" version = "0.1.0" description = "Self-hosted note-taking, project-management, and calendar app exposed to Claude via MCP" requires-python = ">=3.14" diff --git a/scripts/import_fabledrulebook.py b/scripts/import_fabledrulebook.py index 6ee3505..bd960ff 100644 --- a/scripts/import_fabledrulebook.py +++ b/scripts/import_fabledrulebook.py @@ -103,7 +103,7 @@ async def run(args) -> None: print(f" - {r['title']}: {preview}{empty_flag}") return - from fabledassistant.services import rulebooks as rb_service + from scribe.services import rulebooks as rb_service existing = await rb_service.find_rulebook_by_title(args.user_id, args.rulebook_title) if existing is not None and not args.force: diff --git a/src/fabledassistant/models/__init__.py b/src/fabledassistant/models/__init__.py deleted file mode 100644 index 4288178..0000000 --- a/src/fabledassistant/models/__init__.py +++ /dev/null @@ -1,42 +0,0 @@ -from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine -from sqlalchemy.orm import DeclarativeBase - -from fabledassistant.config import Config - -engine = create_async_engine( - Config.DATABASE_URL, - echo=False, - pool_pre_ping=True, - pool_recycle=1800, -) -async_session = async_sessionmaker(engine, class_=AsyncSession, expire_on_commit=False) - - -class Base(DeclarativeBase): - pass - - -from fabledassistant.models.base import CreatedAtMixin, TimestampMixin # noqa: E402, F401 - - -from fabledassistant.models.note import Note, TaskPriority, TaskStatus # noqa: E402, F401 -from fabledassistant.models.setting import Setting # noqa: E402, F401 -from fabledassistant.models.user import User # noqa: E402, F401 -from fabledassistant.models.app_log import AppLog # noqa: E402, F401 -from fabledassistant.models.password_reset import PasswordResetToken # noqa: E402, F401 -from fabledassistant.models.invitation import InvitationToken # noqa: E402, F401 -from fabledassistant.models.embedding import NoteEmbedding # noqa: E402, F401 -from fabledassistant.models.project import Project # noqa: E402, F401 -from fabledassistant.models.event import Event # noqa: E402, F401 -from fabledassistant.models.milestone import Milestone # noqa: E402, F401 -from fabledassistant.models.task_log import TaskLog # noqa: E402, F401 -from fabledassistant.models.note_draft import NoteDraft # noqa: E402, F401 -from fabledassistant.models.note_version import NoteVersion # noqa: E402, F401 -from fabledassistant.models.group import Group, GroupMembership # noqa: E402, F401 -from fabledassistant.models.share import NoteShare, ProjectShare # noqa: E402, F401 -from fabledassistant.models.notification import Notification # noqa: E402, F401 -from fabledassistant.models.api_key import ApiKey # noqa: E402, F401 -from fabledassistant.models.user_profile import UserProfile # noqa: E402, F401 -from fabledassistant.models.rulebook import ( # noqa: E402, F401 - Rulebook, RulebookTopic, Rule, project_rulebook_subscriptions, -) diff --git a/src/fabledassistant/__init__.py b/src/scribe/__init__.py similarity index 100% rename from src/fabledassistant/__init__.py rename to src/scribe/__init__.py diff --git a/src/fabledassistant/app.py b/src/scribe/app.py similarity index 78% rename from src/fabledassistant/app.py rename to src/scribe/app.py index 333ab55..e0b5222 100644 --- a/src/fabledassistant/app.py +++ b/src/scribe/app.py @@ -5,30 +5,30 @@ from pathlib import Path from quart import Quart, g, jsonify, make_response, request, send_from_directory -from fabledassistant.config import Config -from fabledassistant.routes.admin import admin_bp -from fabledassistant.routes.api import api -from fabledassistant.routes.auth import auth_bp -from fabledassistant.routes.export import export_bp -from fabledassistant.routes.notes import notes_bp -from fabledassistant.routes.milestones import milestones_bp -from fabledassistant.routes.task_logs import task_logs_bp -from fabledassistant.routes.projects import projects_bp -from fabledassistant.routes.settings import settings_bp -from fabledassistant.routes.tasks import tasks_bp -from fabledassistant.routes.groups import groups_bp -from fabledassistant.routes.shares import shares_bp -from fabledassistant.routes.in_app_notifications import notifications_bp -from fabledassistant.routes.users import users_bp -from fabledassistant.routes.api_keys import api_keys_bp -from fabledassistant.routes.events import events_bp -from fabledassistant.routes.search import search_bp -from fabledassistant.routes.profile import profile_bp -from fabledassistant.routes.knowledge import knowledge_bp -from fabledassistant.routes.rulebooks import rulebooks_bp -from fabledassistant.routes.trash import trash_bp -from fabledassistant.routes.dashboard import dashboard_bp -from fabledassistant.mcp import mount_mcp +from scribe.config import Config +from scribe.routes.admin import admin_bp +from scribe.routes.api import api +from scribe.routes.auth import auth_bp +from scribe.routes.export import export_bp +from scribe.routes.notes import notes_bp +from scribe.routes.milestones import milestones_bp +from scribe.routes.task_logs import task_logs_bp +from scribe.routes.projects import projects_bp +from scribe.routes.settings import settings_bp +from scribe.routes.tasks import tasks_bp +from scribe.routes.groups import groups_bp +from scribe.routes.shares import shares_bp +from scribe.routes.in_app_notifications import notifications_bp +from scribe.routes.users import users_bp +from scribe.routes.api_keys import api_keys_bp +from scribe.routes.events import events_bp +from scribe.routes.search import search_bp +from scribe.routes.profile import profile_bp +from scribe.routes.knowledge import knowledge_bp +from scribe.routes.rulebooks import rulebooks_bp +from scribe.routes.trash import trash_bp +from scribe.routes.dashboard import dashboard_bp +from scribe.mcp import mount_mcp STATIC_DIR = Path(__file__).parent / "static" logger = logging.getLogger(__name__) @@ -115,7 +115,7 @@ def create_app() -> Quart: # Log usage for API requests (skip logs endpoint to avoid recursion) if request.path.startswith("/api/") and not request.path.startswith("/api/admin/logs"): try: - from fabledassistant.services.logging import log_usage + from scribe.services.logging import log_usage user = getattr(g, "user", None) await log_usage( @@ -150,10 +150,10 @@ def create_app() -> Quart: async def startup(): import asyncio - from fabledassistant.services.auth import start_auth_token_retention_loop - from fabledassistant.services.embeddings import backfill_note_embeddings - from fabledassistant.services.logging import start_log_retention_loop - from fabledassistant.services.notifications import start_notification_loop + from scribe.services.auth import start_auth_token_retention_loop + from scribe.services.embeddings import backfill_note_embeddings + from scribe.services.logging import start_log_retention_loop + from scribe.services.notifications import start_notification_loop start_log_retention_loop() start_notification_loop() @@ -170,36 +170,36 @@ def create_app() -> Quart: asyncio.create_task(_delayed_backfill()) # Event scheduler (reminders + CalDAV pull sync) - from fabledassistant.services.event_scheduler import start_event_scheduler + from scribe.services.event_scheduler import start_event_scheduler start_event_scheduler(asyncio.get_running_loop()) # Version-pinning scheduler (daily auto-pin scan at 03:00 UTC) - from fabledassistant.services.version_pinning_scheduler import ( + from scribe.services.version_pinning_scheduler import ( start_version_pinning_scheduler, ) start_version_pinning_scheduler(asyncio.get_running_loop()) # Trash retention scheduler (daily expired-trash purge at 03:30 UTC) - from fabledassistant.services.trash_scheduler import start_trash_scheduler + from scribe.services.trash_scheduler import start_trash_scheduler start_trash_scheduler(asyncio.get_running_loop()) # Diagnostic instrumentation — heartbeat, signal handlers, asyncio # exception hook. Cheap (~1 log line/min), high diagnostic value when # the app crashes mysteriously. See services/diagnostics.py. - from fabledassistant.services.diagnostics import start_diagnostics + from scribe.services.diagnostics import start_diagnostics start_diagnostics(asyncio.get_running_loop()) @app.after_serving async def shutdown(): - from fabledassistant.services.event_scheduler import stop_event_scheduler + from scribe.services.event_scheduler import stop_event_scheduler stop_event_scheduler() - from fabledassistant.services.version_pinning_scheduler import ( + from scribe.services.version_pinning_scheduler import ( stop_version_pinning_scheduler, ) stop_version_pinning_scheduler() - from fabledassistant.services.trash_scheduler import stop_trash_scheduler + from scribe.services.trash_scheduler import stop_trash_scheduler stop_trash_scheduler() - from fabledassistant.services.diagnostics import stop_diagnostics + from scribe.services.diagnostics import stop_diagnostics stop_diagnostics() @app.route("/") @@ -249,7 +249,7 @@ def create_app() -> Quart: logger.exception("Internal server error on %s %s", request.method, request.path) try: - from fabledassistant.services.logging import log_error + from scribe.services.logging import log_error user = getattr(g, "user", None) await log_error( diff --git a/src/fabledassistant/auth.py b/src/scribe/auth.py similarity index 94% rename from src/fabledassistant/auth.py rename to src/scribe/auth.py index a8a492c..54cad24 100644 --- a/src/fabledassistant/auth.py +++ b/src/scribe/auth.py @@ -2,8 +2,8 @@ import functools from quart import g, jsonify, request, session -from fabledassistant.services.auth import get_user_by_id -from fabledassistant.services.api_keys import lookup_key +from scribe.services.auth import get_user_by_id +from scribe.services.api_keys import lookup_key def _check_auth(f, required_role: str | None = None): diff --git a/src/fabledassistant/config.py b/src/scribe/config.py similarity index 97% rename from src/fabledassistant/config.py rename to src/scribe/config.py index 0088482..bb12f1e 100644 --- a/src/fabledassistant/config.py +++ b/src/scribe/config.py @@ -20,7 +20,7 @@ class Config: DATABASE_URL: str = _read_secret( "DATABASE_URL", "DATABASE_URL_FILE", - "postgresql+asyncpg://fabled:fabled@localhost:5432/fabledassistant", + "postgresql+asyncpg://scribe:scribe@localhost:5432/scribe", ) SECRET_KEY: str = _read_secret("SECRET_KEY", "SECRET_KEY_FILE", "dev-secret-change-me") SECURE_COOKIES: bool = os.environ.get("SECURE_COOKIES", "").lower() in ("1", "true", "yes") diff --git a/src/fabledassistant/mcp/__init__.py b/src/scribe/mcp/__init__.py similarity index 81% rename from src/fabledassistant/mcp/__init__.py rename to src/scribe/mcp/__init__.py index c00ca03..ca0aa93 100644 --- a/src/fabledassistant/mcp/__init__.py +++ b/src/scribe/mcp/__init__.py @@ -3,6 +3,6 @@ Auth uses the existing `api_keys` table: a Bearer token in the Authorization header resolves to a user, and every tool call acts on that user's data only. """ -from fabledassistant.mcp.server import build_mcp_server, mount_mcp +from scribe.mcp.server import build_mcp_server, mount_mcp __all__ = ["build_mcp_server", "mount_mcp"] diff --git a/src/fabledassistant/mcp/_context.py b/src/scribe/mcp/_context.py similarity index 100% rename from src/fabledassistant/mcp/_context.py rename to src/scribe/mcp/_context.py diff --git a/src/fabledassistant/mcp/auth.py b/src/scribe/mcp/auth.py similarity index 96% rename from src/fabledassistant/mcp/auth.py rename to src/scribe/mcp/auth.py index ab1c3da..959d1f3 100644 --- a/src/fabledassistant/mcp/auth.py +++ b/src/scribe/mcp/auth.py @@ -1,7 +1,7 @@ """MCP-side Bearer token resolution. Reuses the existing api_keys infrastructure.""" from __future__ import annotations -from fabledassistant.services.api_keys import lookup_key +from scribe.services.api_keys import lookup_key async def resolve_bearer_to_user_id(auth_header: str | None) -> int | None: diff --git a/src/fabledassistant/mcp/server.py b/src/scribe/mcp/server.py similarity index 98% rename from src/fabledassistant/mcp/server.py rename to src/scribe/mcp/server.py index e3cd3ae..8a46ad8 100644 --- a/src/fabledassistant/mcp/server.py +++ b/src/scribe/mcp/server.py @@ -220,7 +220,7 @@ def build_mcp_server() -> FastMCP: enable_dns_rebinding_protection=False, ), ) - from fabledassistant.mcp.tools import register_all + from scribe.mcp.tools import register_all register_all(mcp) return mcp @@ -239,7 +239,7 @@ def mount_mcp(app: Quart) -> None: inside Quart, we hook the session manager's `run()` async context manager into Quart's serving lifecycle (before_serving / after_serving). """ - from fabledassistant.mcp.auth import resolve_bearer + from scribe.mcp.auth import resolve_bearer mcp = build_mcp_server() mcp_asgi = mcp.streamable_http_app() @@ -299,7 +299,7 @@ def mount_mcp(app: Quart) -> None: return scope["scribe_user_id"] = user_id - from fabledassistant.mcp._context import _user_id_ctx + from scribe.mcp._context import _user_id_ctx token = _user_id_ctx.set(user_id) try: await mcp_asgi(scope, receive, send) diff --git a/src/fabledassistant/mcp/tools/__init__.py b/src/scribe/mcp/tools/__init__.py similarity index 94% rename from src/fabledassistant/mcp/tools/__init__.py rename to src/scribe/mcp/tools/__init__.py index 58c776d..418904f 100644 --- a/src/fabledassistant/mcp/tools/__init__.py +++ b/src/scribe/mcp/tools/__init__.py @@ -4,7 +4,7 @@ Each tool module exposes a `register(mcp)` function that attaches its tools to a FastMCP instance. `register_all(mcp)` is the single entry point called from `mcp.server.build_mcp_server`. """ -from fabledassistant.mcp.tools import ( +from scribe.mcp.tools import ( entities, events, milestones, notes, processes, projects, recent, rulebooks, search, tags, tasks, trash, ) diff --git a/src/fabledassistant/mcp/tools/entities.py b/src/scribe/mcp/tools/entities.py similarity index 97% rename from src/fabledassistant/mcp/tools/entities.py rename to src/scribe/mcp/tools/entities.py index 382469d..a00fb25 100644 --- a/src/fabledassistant/mcp/tools/entities.py +++ b/src/scribe/mcp/tools/entities.py @@ -14,9 +14,9 @@ of plain strings for ergonomics; checked-state is reset to False on each call. """ from __future__ import annotations -from fabledassistant.mcp._context import current_user_id -from fabledassistant.services import knowledge as knowledge_svc -from fabledassistant.services import notes as notes_svc +from scribe.mcp._context import current_user_id +from scribe.services import knowledge as knowledge_svc +from scribe.services import notes as notes_svc async def _list_by_type(note_type: str, q: str, tag: str, limit: int) -> dict: diff --git a/src/fabledassistant/mcp/tools/events.py b/src/scribe/mcp/tools/events.py similarity index 97% rename from src/fabledassistant/mcp/tools/events.py rename to src/scribe/mcp/tools/events.py index f4bc4cf..20d1a1d 100644 --- a/src/fabledassistant/mcp/tools/events.py +++ b/src/scribe/mcp/tools/events.py @@ -19,9 +19,9 @@ from __future__ import annotations from datetime import datetime, timedelta, timezone -from fabledassistant.mcp._context import current_user_id -from fabledassistant.services import events as events_svc -from fabledassistant.services import trash as trash_svc +from scribe.mcp._context import current_user_id +from scribe.services import events as events_svc +from scribe.services import trash as trash_svc def _combine(start_date: str, start_time: str) -> datetime: diff --git a/src/fabledassistant/mcp/tools/milestones.py b/src/scribe/mcp/tools/milestones.py similarity index 95% rename from src/fabledassistant/mcp/tools/milestones.py rename to src/scribe/mcp/tools/milestones.py index 6625fd4..d4973ab 100644 --- a/src/fabledassistant/mcp/tools/milestones.py +++ b/src/scribe/mcp/tools/milestones.py @@ -11,9 +11,9 @@ Sentinels: """ from __future__ import annotations -from fabledassistant.mcp._context import current_user_id -from fabledassistant.services import milestones as milestones_svc -from fabledassistant.services import trash as trash_svc +from scribe.mcp._context import current_user_id +from scribe.services import milestones as milestones_svc +from scribe.services import trash as trash_svc async def list_milestones(project_id: int) -> dict: diff --git a/src/fabledassistant/mcp/tools/notes.py b/src/scribe/mcp/tools/notes.py similarity index 96% rename from src/fabledassistant/mcp/tools/notes.py rename to src/scribe/mcp/tools/notes.py index c2eb8b8..fecd89e 100644 --- a/src/fabledassistant/mcp/tools/notes.py +++ b/src/scribe/mcp/tools/notes.py @@ -13,9 +13,9 @@ Sentinel conventions (inherited from existing fable-mcp tools): """ from __future__ import annotations -from fabledassistant.mcp._context import current_user_id -from fabledassistant.services import notes as notes_svc -from fabledassistant.services import trash as trash_svc +from scribe.mcp._context import current_user_id +from scribe.services import notes as notes_svc +from scribe.services import trash as trash_svc async def list_notes( diff --git a/src/fabledassistant/mcp/tools/processes.py b/src/scribe/mcp/tools/processes.py similarity index 95% rename from src/fabledassistant/mcp/tools/processes.py rename to src/scribe/mcp/tools/processes.py index 7718dd3..64efd3a 100644 --- a/src/fabledassistant/mcp/tools/processes.py +++ b/src/scribe/mcp/tools/processes.py @@ -6,9 +6,9 @@ get_process is the fire mechanism: it returns the full prompt for Claude to run. """ from __future__ import annotations -from fabledassistant.mcp._context import current_user_id -from fabledassistant.services import knowledge as knowledge_svc -from fabledassistant.services import notes as notes_svc +from scribe.mcp._context import current_user_id +from scribe.services import knowledge as knowledge_svc +from scribe.services import notes as notes_svc async def list_processes(q: str = "", tag: str = "", limit: int = 50) -> dict: diff --git a/src/fabledassistant/mcp/tools/projects.py b/src/scribe/mcp/tools/projects.py similarity index 95% rename from src/fabledassistant/mcp/tools/projects.py rename to src/scribe/mcp/tools/projects.py index 73e1e28..7557074 100644 --- a/src/fabledassistant/mcp/tools/projects.py +++ b/src/scribe/mcp/tools/projects.py @@ -16,12 +16,12 @@ keeps working. """ from __future__ import annotations -from fabledassistant.mcp._context import current_user_id -from fabledassistant.services import milestones as milestones_svc -from fabledassistant.services import notes as notes_svc -from fabledassistant.services import projects as projects_svc -from fabledassistant.services import rulebooks as rulebooks_svc -from fabledassistant.services import trash as trash_svc +from scribe.mcp._context import current_user_id +from scribe.services import milestones as milestones_svc +from scribe.services import notes as notes_svc +from scribe.services import projects as projects_svc +from scribe.services import rulebooks as rulebooks_svc +from scribe.services import trash as trash_svc async def list_projects() -> dict: diff --git a/src/fabledassistant/mcp/tools/recent.py b/src/scribe/mcp/tools/recent.py similarity index 92% rename from src/fabledassistant/mcp/tools/recent.py rename to src/scribe/mcp/tools/recent.py index dc2d1e9..682fbc7 100644 --- a/src/fabledassistant/mcp/tools/recent.py +++ b/src/scribe/mcp/tools/recent.py @@ -13,11 +13,11 @@ from datetime import datetime, timedelta, timezone from sqlalchemy import select -from fabledassistant.mcp._context import current_user_id -from fabledassistant.models import async_session -from fabledassistant.models.event import Event -from fabledassistant.models.note import Note -from fabledassistant.models.project import Project +from scribe.mcp._context import current_user_id +from scribe.models import async_session +from scribe.models.event import Event +from scribe.models.note import Note +from scribe.models.project import Project async def get_recent(days: int = 7, limit: int = 25) -> dict: diff --git a/src/fabledassistant/mcp/tools/rulebooks.py b/src/scribe/mcp/tools/rulebooks.py similarity index 98% rename from src/fabledassistant/mcp/tools/rulebooks.py rename to src/scribe/mcp/tools/rulebooks.py index d95d81a..a9dd673 100644 --- a/src/fabledassistant/mcp/tools/rulebooks.py +++ b/src/scribe/mcp/tools/rulebooks.py @@ -9,9 +9,9 @@ spec. """ from __future__ import annotations -from fabledassistant.mcp._context import current_user_id -from fabledassistant.services import rulebooks as rulebooks_svc -from fabledassistant.services import trash as trash_svc +from scribe.mcp._context import current_user_id +from scribe.services import rulebooks as rulebooks_svc +from scribe.services import trash as trash_svc # ── Rulebook CRUD ─────────────────────────────────────────────────────── diff --git a/src/fabledassistant/mcp/tools/search.py b/src/scribe/mcp/tools/search.py similarity index 92% rename from src/fabledassistant/mcp/tools/search.py rename to src/scribe/mcp/tools/search.py index 2f51488..536317b 100644 --- a/src/fabledassistant/mcp/tools/search.py +++ b/src/scribe/mcp/tools/search.py @@ -7,8 +7,8 @@ working. Differences from fable-mcp: """ from __future__ import annotations -from fabledassistant.mcp._context import current_user_id -from fabledassistant.services.embeddings import semantic_search_notes +from scribe.mcp._context import current_user_id +from scribe.services.embeddings import semantic_search_notes async def search(q: str, content_type: str = "all", limit: int = 10) -> dict: diff --git a/src/fabledassistant/mcp/tools/tags.py b/src/scribe/mcp/tools/tags.py similarity index 91% rename from src/fabledassistant/mcp/tools/tags.py rename to src/scribe/mcp/tools/tags.py index 1ea85f8..1233373 100644 --- a/src/fabledassistant/mcp/tools/tags.py +++ b/src/scribe/mcp/tools/tags.py @@ -8,9 +8,9 @@ from __future__ import annotations from sqlalchemy import select -from fabledassistant.mcp._context import current_user_id -from fabledassistant.models import async_session -from fabledassistant.models.note import Note +from scribe.mcp._context import current_user_id +from scribe.models import async_session +from scribe.models.note import Note def _aggregate_tag_counts(tag_lists) -> dict[str, int]: diff --git a/src/fabledassistant/mcp/tools/tasks.py b/src/scribe/mcp/tools/tasks.py similarity index 95% rename from src/fabledassistant/mcp/tools/tasks.py rename to src/scribe/mcp/tools/tasks.py index c7aae85..28b36e8 100644 --- a/src/fabledassistant/mcp/tools/tasks.py +++ b/src/scribe/mcp/tools/tasks.py @@ -18,12 +18,12 @@ Sentinels (preserved from existing fable-mcp): """ from __future__ import annotations -from fabledassistant.mcp._context import current_user_id -from fabledassistant.services import notes as notes_svc -from fabledassistant.services import planning as planning_svc -from fabledassistant.services import rulebooks as rulebooks_svc -from fabledassistant.services import task_logs as task_logs_svc -from fabledassistant.services import trash as trash_svc +from scribe.mcp._context import current_user_id +from scribe.services import notes as notes_svc +from scribe.services import planning as planning_svc +from scribe.services import rulebooks as rulebooks_svc +from scribe.services import task_logs as task_logs_svc +from scribe.services import trash as trash_svc async def list_tasks( diff --git a/src/fabledassistant/mcp/tools/trash.py b/src/scribe/mcp/tools/trash.py similarity index 93% rename from src/fabledassistant/mcp/tools/trash.py rename to src/scribe/mcp/tools/trash.py index dfb9cdb..726669b 100644 --- a/src/fabledassistant/mcp/tools/trash.py +++ b/src/scribe/mcp/tools/trash.py @@ -6,8 +6,8 @@ the deleted_batch_id that a delete operation returns. """ from __future__ import annotations -from fabledassistant.mcp._context import current_user_id -from fabledassistant.services import trash as trash_svc +from scribe.mcp._context import current_user_id +from scribe.services import trash as trash_svc async def list_trash() -> dict: diff --git a/src/scribe/models/__init__.py b/src/scribe/models/__init__.py new file mode 100644 index 0000000..a012b55 --- /dev/null +++ b/src/scribe/models/__init__.py @@ -0,0 +1,42 @@ +from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine +from sqlalchemy.orm import DeclarativeBase + +from scribe.config import Config + +engine = create_async_engine( + Config.DATABASE_URL, + echo=False, + pool_pre_ping=True, + pool_recycle=1800, +) +async_session = async_sessionmaker(engine, class_=AsyncSession, expire_on_commit=False) + + +class Base(DeclarativeBase): + pass + + +from scribe.models.base import CreatedAtMixin, TimestampMixin # noqa: E402, F401 + + +from scribe.models.note import Note, TaskPriority, TaskStatus # noqa: E402, F401 +from scribe.models.setting import Setting # noqa: E402, F401 +from scribe.models.user import User # noqa: E402, F401 +from scribe.models.app_log import AppLog # noqa: E402, F401 +from scribe.models.password_reset import PasswordResetToken # noqa: E402, F401 +from scribe.models.invitation import InvitationToken # noqa: E402, F401 +from scribe.models.embedding import NoteEmbedding # noqa: E402, F401 +from scribe.models.project import Project # noqa: E402, F401 +from scribe.models.event import Event # noqa: E402, F401 +from scribe.models.milestone import Milestone # noqa: E402, F401 +from scribe.models.task_log import TaskLog # noqa: E402, F401 +from scribe.models.note_draft import NoteDraft # noqa: E402, F401 +from scribe.models.note_version import NoteVersion # noqa: E402, F401 +from scribe.models.group import Group, GroupMembership # noqa: E402, F401 +from scribe.models.share import NoteShare, ProjectShare # noqa: E402, F401 +from scribe.models.notification import Notification # noqa: E402, F401 +from scribe.models.api_key import ApiKey # noqa: E402, F401 +from scribe.models.user_profile import UserProfile # noqa: E402, F401 +from scribe.models.rulebook import ( # noqa: E402, F401 + Rulebook, RulebookTopic, Rule, project_rulebook_subscriptions, +) diff --git a/src/fabledassistant/models/api_key.py b/src/scribe/models/api_key.py similarity index 93% rename from src/fabledassistant/models/api_key.py rename to src/scribe/models/api_key.py index e7e45c8..4093faf 100644 --- a/src/fabledassistant/models/api_key.py +++ b/src/scribe/models/api_key.py @@ -3,8 +3,8 @@ from datetime import datetime from sqlalchemy import DateTime, ForeignKey, Index, Integer, Text from sqlalchemy.orm import Mapped, mapped_column -from fabledassistant.models import Base -from fabledassistant.models.base import CreatedAtMixin +from scribe.models import Base +from scribe.models.base import CreatedAtMixin class ApiKey(Base, CreatedAtMixin): diff --git a/src/fabledassistant/models/app_log.py b/src/scribe/models/app_log.py similarity index 97% rename from src/fabledassistant/models/app_log.py rename to src/scribe/models/app_log.py index 364ca5f..32c3910 100644 --- a/src/fabledassistant/models/app_log.py +++ b/src/scribe/models/app_log.py @@ -3,7 +3,7 @@ from datetime import datetime, timezone from sqlalchemy import DateTime, Float, Index, Integer, Text from sqlalchemy.orm import Mapped, mapped_column -from fabledassistant.models import Base +from scribe.models import Base class AppLog(Base): diff --git a/src/fabledassistant/models/base.py b/src/scribe/models/base.py similarity index 100% rename from src/fabledassistant/models/base.py rename to src/scribe/models/base.py diff --git a/src/fabledassistant/models/embedding.py b/src/scribe/models/embedding.py similarity index 95% rename from src/fabledassistant/models/embedding.py rename to src/scribe/models/embedding.py index 7819566..de1a282 100644 --- a/src/fabledassistant/models/embedding.py +++ b/src/scribe/models/embedding.py @@ -4,7 +4,7 @@ from sqlalchemy import DateTime, ForeignKey, Integer from sqlalchemy.dialects.postgresql import JSONB from sqlalchemy.orm import Mapped, mapped_column -from fabledassistant.models import Base +from scribe.models import Base class NoteEmbedding(Base): diff --git a/src/fabledassistant/models/event.py b/src/scribe/models/event.py similarity index 97% rename from src/fabledassistant/models/event.py rename to src/scribe/models/event.py index e40ba5e..e179b13 100644 --- a/src/fabledassistant/models/event.py +++ b/src/scribe/models/event.py @@ -3,8 +3,8 @@ from datetime import datetime, timedelta, timezone from sqlalchemy import Boolean, DateTime, ForeignKey, Integer, Text from sqlalchemy.orm import Mapped, mapped_column -from fabledassistant.models import Base -from fabledassistant.models.base import SoftDeleteMixin +from scribe.models import Base +from scribe.models.base import SoftDeleteMixin class Event(Base, SoftDeleteMixin): diff --git a/src/fabledassistant/models/group.py b/src/scribe/models/group.py similarity index 94% rename from src/fabledassistant/models/group.py rename to src/scribe/models/group.py index 50b362d..1a58834 100644 --- a/src/fabledassistant/models/group.py +++ b/src/scribe/models/group.py @@ -3,8 +3,8 @@ from datetime import datetime, timezone from sqlalchemy import ForeignKey, Integer, Text, UniqueConstraint from sqlalchemy.orm import Mapped, mapped_column, relationship -from fabledassistant.models import Base -from fabledassistant.models.base import CreatedAtMixin, TimestampMixin +from scribe.models import Base +from scribe.models.base import CreatedAtMixin, TimestampMixin class Group(Base, TimestampMixin): diff --git a/src/fabledassistant/models/invitation.py b/src/scribe/models/invitation.py similarity index 96% rename from src/fabledassistant/models/invitation.py rename to src/scribe/models/invitation.py index 72597a4..432267b 100644 --- a/src/fabledassistant/models/invitation.py +++ b/src/scribe/models/invitation.py @@ -3,7 +3,7 @@ from datetime import datetime, timezone from sqlalchemy import Boolean, DateTime, ForeignKey, Index, Text from sqlalchemy.orm import Mapped, mapped_column -from fabledassistant.models import Base +from scribe.models import Base class InvitationToken(Base): diff --git a/src/fabledassistant/models/milestone.py b/src/scribe/models/milestone.py similarity index 90% rename from src/fabledassistant/models/milestone.py rename to src/scribe/models/milestone.py index cfe9c5e..ee36a2c 100644 --- a/src/fabledassistant/models/milestone.py +++ b/src/scribe/models/milestone.py @@ -1,8 +1,8 @@ from sqlalchemy import ForeignKey, Integer, Text from sqlalchemy.orm import Mapped, mapped_column -from fabledassistant.models import Base -from fabledassistant.models.base import TimestampMixin, SoftDeleteMixin +from scribe.models import Base +from scribe.models.base import TimestampMixin, SoftDeleteMixin class Milestone(Base, TimestampMixin, SoftDeleteMixin): diff --git a/src/fabledassistant/models/note.py b/src/scribe/models/note.py similarity index 97% rename from src/fabledassistant/models/note.py rename to src/scribe/models/note.py index ef16109..5fe68b1 100644 --- a/src/fabledassistant/models/note.py +++ b/src/scribe/models/note.py @@ -5,8 +5,8 @@ from sqlalchemy import Date, DateTime, ForeignKey, Index, Integer, Text from sqlalchemy.dialects.postgresql import ARRAY, JSONB from sqlalchemy.orm import Mapped, mapped_column -from fabledassistant.models import Base -from fabledassistant.models.base import TimestampMixin, SoftDeleteMixin +from scribe.models import Base +from scribe.models.base import TimestampMixin, SoftDeleteMixin class TaskStatus(str, enum.Enum): diff --git a/src/fabledassistant/models/note_draft.py b/src/scribe/models/note_draft.py similarity index 92% rename from src/fabledassistant/models/note_draft.py rename to src/scribe/models/note_draft.py index 50be076..5ffbc1e 100644 --- a/src/fabledassistant/models/note_draft.py +++ b/src/scribe/models/note_draft.py @@ -1,8 +1,8 @@ from sqlalchemy import ForeignKey, Integer, Text from sqlalchemy.orm import Mapped, mapped_column -from fabledassistant.models import Base -from fabledassistant.models.base import TimestampMixin +from scribe.models import Base +from scribe.models.base import TimestampMixin class NoteDraft(Base, TimestampMixin): diff --git a/src/fabledassistant/models/note_version.py b/src/scribe/models/note_version.py similarity index 92% rename from src/fabledassistant/models/note_version.py rename to src/scribe/models/note_version.py index cf9a94a..86cd62f 100644 --- a/src/fabledassistant/models/note_version.py +++ b/src/scribe/models/note_version.py @@ -1,8 +1,8 @@ from sqlalchemy import ARRAY, ForeignKey, Integer, Text from sqlalchemy.orm import Mapped, mapped_column -from fabledassistant.models import Base -from fabledassistant.models.base import CreatedAtMixin +from scribe.models import Base +from scribe.models.base import CreatedAtMixin class NoteVersion(Base, CreatedAtMixin): diff --git a/src/fabledassistant/models/notification.py b/src/scribe/models/notification.py similarity index 91% rename from src/fabledassistant/models/notification.py rename to src/scribe/models/notification.py index d7267d1..05a5c35 100644 --- a/src/fabledassistant/models/notification.py +++ b/src/scribe/models/notification.py @@ -4,8 +4,8 @@ from sqlalchemy import DateTime, ForeignKey, Integer, Text from sqlalchemy.dialects.postgresql import JSONB from sqlalchemy.orm import Mapped, mapped_column -from fabledassistant.models import Base -from fabledassistant.models.base import CreatedAtMixin +from scribe.models import Base +from scribe.models.base import CreatedAtMixin class Notification(Base, CreatedAtMixin): diff --git a/src/fabledassistant/models/password_reset.py b/src/scribe/models/password_reset.py similarity index 95% rename from src/fabledassistant/models/password_reset.py rename to src/scribe/models/password_reset.py index ed54deb..70add83 100644 --- a/src/fabledassistant/models/password_reset.py +++ b/src/scribe/models/password_reset.py @@ -3,7 +3,7 @@ from datetime import datetime, timezone from sqlalchemy import Boolean, DateTime, ForeignKey, Index, Text from sqlalchemy.orm import Mapped, mapped_column -from fabledassistant.models import Base +from scribe.models import Base class PasswordResetToken(Base): diff --git a/src/fabledassistant/models/project.py b/src/scribe/models/project.py similarity index 93% rename from src/fabledassistant/models/project.py rename to src/scribe/models/project.py index ccf4db5..5d894c0 100644 --- a/src/fabledassistant/models/project.py +++ b/src/scribe/models/project.py @@ -2,8 +2,8 @@ import enum from datetime import datetime from sqlalchemy import DateTime, ForeignKey, Integer, Text from sqlalchemy.orm import Mapped, mapped_column -from fabledassistant.models import Base -from fabledassistant.models.base import TimestampMixin, SoftDeleteMixin +from scribe.models import Base +from scribe.models.base import TimestampMixin, SoftDeleteMixin class ProjectStatus(str, enum.Enum): diff --git a/src/fabledassistant/models/rulebook.py b/src/scribe/models/rulebook.py similarity index 98% rename from src/fabledassistant/models/rulebook.py rename to src/scribe/models/rulebook.py index 67799aa..6b0813e 100644 --- a/src/fabledassistant/models/rulebook.py +++ b/src/scribe/models/rulebook.py @@ -3,8 +3,8 @@ from datetime import datetime, timezone from sqlalchemy import BigInteger, Boolean, Column, DateTime, ForeignKey, Index, Integer, Table, Text, text from sqlalchemy.orm import Mapped, mapped_column -from fabledassistant.models import Base -from fabledassistant.models.base import SoftDeleteMixin +from scribe.models import Base +from scribe.models.base import SoftDeleteMixin class Rulebook(Base, SoftDeleteMixin): diff --git a/src/fabledassistant/models/setting.py b/src/scribe/models/setting.py similarity index 92% rename from src/fabledassistant/models/setting.py rename to src/scribe/models/setting.py index 5e12f89..05933bf 100644 --- a/src/fabledassistant/models/setting.py +++ b/src/scribe/models/setting.py @@ -1,7 +1,7 @@ from sqlalchemy import ForeignKey, Integer, Text from sqlalchemy.orm import Mapped, mapped_column -from fabledassistant.models import Base +from scribe.models import Base class Setting(Base): diff --git a/src/fabledassistant/models/share.py b/src/scribe/models/share.py similarity index 96% rename from src/fabledassistant/models/share.py rename to src/scribe/models/share.py index aa24160..522a728 100644 --- a/src/fabledassistant/models/share.py +++ b/src/scribe/models/share.py @@ -1,8 +1,8 @@ from sqlalchemy import CheckConstraint, ForeignKey, Integer, Text from sqlalchemy.orm import Mapped, mapped_column -from fabledassistant.models import Base -from fabledassistant.models.base import TimestampMixin +from scribe.models import Base +from scribe.models.base import TimestampMixin class ProjectShare(Base, TimestampMixin): diff --git a/src/fabledassistant/models/task_log.py b/src/scribe/models/task_log.py similarity index 90% rename from src/fabledassistant/models/task_log.py rename to src/scribe/models/task_log.py index c6e7a9e..0866ea8 100644 --- a/src/fabledassistant/models/task_log.py +++ b/src/scribe/models/task_log.py @@ -1,8 +1,8 @@ from sqlalchemy import ForeignKey, Integer, Text from sqlalchemy.orm import Mapped, mapped_column -from fabledassistant.models import Base -from fabledassistant.models.base import TimestampMixin +from scribe.models import Base +from scribe.models.base import TimestampMixin class TaskLog(Base, TimestampMixin): diff --git a/src/fabledassistant/models/user.py b/src/scribe/models/user.py similarity index 91% rename from src/fabledassistant/models/user.py rename to src/scribe/models/user.py index 2db482a..6a295cf 100644 --- a/src/fabledassistant/models/user.py +++ b/src/scribe/models/user.py @@ -1,8 +1,8 @@ from sqlalchemy import Index, Integer, Text from sqlalchemy.orm import Mapped, mapped_column -from fabledassistant.models import Base -from fabledassistant.models.base import CreatedAtMixin +from scribe.models import Base +from scribe.models.base import CreatedAtMixin class User(Base, CreatedAtMixin): diff --git a/src/fabledassistant/models/user_profile.py b/src/scribe/models/user_profile.py similarity index 94% rename from src/fabledassistant/models/user_profile.py rename to src/scribe/models/user_profile.py index a5999d5..e626d8a 100644 --- a/src/fabledassistant/models/user_profile.py +++ b/src/scribe/models/user_profile.py @@ -2,8 +2,8 @@ from sqlalchemy import ForeignKey, Integer, Text from sqlalchemy.dialects.postgresql import ARRAY, JSONB from sqlalchemy.orm import Mapped, mapped_column -from fabledassistant.models import Base -from fabledassistant.models.base import TimestampMixin +from scribe.models import Base +from scribe.models.base import TimestampMixin class UserProfile(Base, TimestampMixin): diff --git a/src/fabledassistant/rate_limit.py b/src/scribe/rate_limit.py similarity index 100% rename from src/fabledassistant/rate_limit.py rename to src/scribe/rate_limit.py diff --git a/src/fabledassistant/routes/__init__.py b/src/scribe/routes/__init__.py similarity index 100% rename from src/fabledassistant/routes/__init__.py rename to src/scribe/routes/__init__.py diff --git a/src/fabledassistant/routes/admin.py b/src/scribe/routes/admin.py similarity index 93% rename from src/fabledassistant/routes/admin.py rename to src/scribe/routes/admin.py index 115f35d..de9cf1b 100644 --- a/src/fabledassistant/routes/admin.py +++ b/src/scribe/routes/admin.py @@ -3,8 +3,8 @@ import json from quart import Blueprint, Response, g, jsonify, request -from fabledassistant.auth import admin_required, login_required, get_current_user_id -from fabledassistant.services.auth import ( +from scribe.auth import admin_required, login_required, get_current_user_id +from scribe.services.auth import ( create_invitation, delete_user, is_registration_open, @@ -13,15 +13,15 @@ from fabledassistant.services.auth import ( revoke_invitation, set_registration_open, ) -from fabledassistant.services.backup import ( +from scribe.services.backup import ( export_full_backup, export_user_backup, restore_full_backup, ) -from fabledassistant.services.email import SMTP_SETTING_KEYS, get_base_url, get_smtp_config, is_smtp_configured, send_test_email -from fabledassistant.services.logging import get_logs, get_log_stats, log_audit -from fabledassistant.services.notifications import send_invitation_email -from fabledassistant.services.settings import set_setting, set_settings_batch +from scribe.services.email import SMTP_SETTING_KEYS, get_base_url, get_smtp_config, is_smtp_configured, send_test_email +from scribe.services.logging import get_logs, get_log_stats, log_audit +from scribe.services.notifications import send_invitation_email +from scribe.services.settings import set_setting, set_settings_batch admin_bp = Blueprint("admin", __name__, url_prefix="/api/admin") diff --git a/src/fabledassistant/routes/api.py b/src/scribe/routes/api.py similarity index 100% rename from src/fabledassistant/routes/api.py rename to src/scribe/routes/api.py diff --git a/src/fabledassistant/routes/api_keys.py b/src/scribe/routes/api_keys.py similarity index 88% rename from src/fabledassistant/routes/api_keys.py rename to src/scribe/routes/api_keys.py index d6be639..49e36a2 100644 --- a/src/fabledassistant/routes/api_keys.py +++ b/src/scribe/routes/api_keys.py @@ -1,7 +1,7 @@ from quart import Blueprint, jsonify, request -from fabledassistant.auth import login_required, get_current_user_id -from fabledassistant.services.api_keys import create_api_key, list_api_keys, revoke_api_key +from scribe.auth import login_required, get_current_user_id +from scribe.services.api_keys import create_api_key, list_api_keys, revoke_api_key api_keys_bp = Blueprint("api_keys", __name__, url_prefix="/api/api-keys") diff --git a/src/fabledassistant/routes/auth.py b/src/scribe/routes/auth.py similarity index 96% rename from src/fabledassistant/routes/auth.py rename to src/scribe/routes/auth.py index 061eeee..5bbd22e 100644 --- a/src/fabledassistant/routes/auth.py +++ b/src/scribe/routes/auth.py @@ -6,10 +6,10 @@ import secrets from quart import Blueprint, g, jsonify, redirect, request, session -from fabledassistant.auth import login_required, get_current_user_id -from fabledassistant.config import Config -from fabledassistant.rate_limit import is_rate_limited -from fabledassistant.services.auth import ( +from scribe.auth import login_required, get_current_user_id +from scribe.config import Config +from scribe.rate_limit import is_rate_limited +from scribe.services.auth import ( authenticate, change_password, create_password_reset_token, @@ -26,14 +26,14 @@ from fabledassistant.services.auth import ( validate_invitation_token, verify_password, ) -from fabledassistant.services.logging import log_audit -from fabledassistant.services.notifications import ( +from scribe.services.logging import log_audit +from scribe.services.notifications import ( notify_security_event, send_password_reset_email, send_password_reset_success_email, ) -from fabledassistant.services.email import get_base_url, is_smtp_configured -from fabledassistant.services.oauth import ( +from scribe.services.email import get_base_url, is_smtp_configured +from scribe.services.oauth import ( build_auth_url, exchange_code, get_userinfo, diff --git a/src/fabledassistant/routes/dashboard.py b/src/scribe/routes/dashboard.py similarity index 73% rename from src/fabledassistant/routes/dashboard.py rename to src/scribe/routes/dashboard.py index f1b6b7b..376180e 100644 --- a/src/fabledassistant/routes/dashboard.py +++ b/src/scribe/routes/dashboard.py @@ -1,8 +1,8 @@ """Dashboard REST endpoint — the aggregated landing payload.""" from quart import Blueprint, g, jsonify -from fabledassistant.auth import login_required -from fabledassistant.services.dashboard import build_dashboard +from scribe.auth import login_required +from scribe.services.dashboard import build_dashboard dashboard_bp = Blueprint("dashboard", __name__, url_prefix="/api/dashboard") diff --git a/src/fabledassistant/routes/events.py b/src/scribe/routes/events.py similarity index 94% rename from src/fabledassistant/routes/events.py rename to src/scribe/routes/events.py index 20a9fa1..d3b8c97 100644 --- a/src/fabledassistant/routes/events.py +++ b/src/scribe/routes/events.py @@ -5,8 +5,8 @@ from datetime import datetime, timezone from quart import Blueprint, g, jsonify, request -from fabledassistant.auth import login_required -import fabledassistant.services.events as events_svc +from scribe.auth import login_required +import scribe.services.events as events_svc events_bp = Blueprint("events", __name__, url_prefix="/api/events") @@ -126,7 +126,7 @@ async def update_event(event_id: int): @events_bp.delete("/") @login_required async def delete_event(event_id: int): - from fabledassistant.services.trash import delete as trash_delete + from scribe.services.trash import delete as trash_delete batch = await trash_delete(_get_current_user_id(), "event", event_id) if batch is None: return jsonify({"error": "Event not found"}), 404 @@ -137,6 +137,6 @@ async def delete_event(event_id: int): @login_required async def sync_caldav(): """Trigger a CalDAV pull sync for the current user.""" - from fabledassistant.services.caldav_sync import sync_user_events + from scribe.services.caldav_sync import sync_user_events result = await sync_user_events(user_id=_get_current_user_id()) return jsonify(result) diff --git a/src/fabledassistant/routes/export.py b/src/scribe/routes/export.py similarity index 91% rename from src/fabledassistant/routes/export.py rename to src/scribe/routes/export.py index c8ca861..8bb602d 100644 --- a/src/fabledassistant/routes/export.py +++ b/src/scribe/routes/export.py @@ -6,9 +6,9 @@ from datetime import datetime, timezone from quart import Blueprint, Response, request -from fabledassistant.auth import login_required, get_current_user_id -from fabledassistant.models import async_session -from fabledassistant.models.note import Note +from scribe.auth import login_required, get_current_user_id +from scribe.models import async_session +from scribe.models.note import Note from sqlalchemy import select export_bp = Blueprint("export", __name__) @@ -83,7 +83,7 @@ async def export_data(): status=200, headers={ "Content-Type": "application/json; charset=utf-8", - "Content-Disposition": f'attachment; filename="fabledassistant-{stamp}.json"', + "Content-Disposition": f'attachment; filename="scribe-{stamp}.json"', }, ) @@ -112,6 +112,6 @@ async def export_data(): status=200, headers={ "Content-Type": "application/zip", - "Content-Disposition": f'attachment; filename="fabledassistant-{stamp}.zip"', + "Content-Disposition": f'attachment; filename="scribe-{stamp}.zip"', }, ) diff --git a/src/fabledassistant/routes/groups.py b/src/scribe/routes/groups.py similarity index 95% rename from src/fabledassistant/routes/groups.py rename to src/scribe/routes/groups.py index e5f7c26..c445e91 100644 --- a/src/fabledassistant/routes/groups.py +++ b/src/scribe/routes/groups.py @@ -2,9 +2,9 @@ import asyncio from quart import Blueprint, g, jsonify, request -from fabledassistant.auth import get_current_user_id, login_required -from fabledassistant.services import groups as group_svc -from fabledassistant.services.notifications import notify_group_added +from scribe.auth import get_current_user_id, login_required +from scribe.services import groups as group_svc +from scribe.services.notifications import notify_group_added groups_bp = Blueprint("groups", __name__, url_prefix="/api/groups") diff --git a/src/fabledassistant/routes/in_app_notifications.py b/src/scribe/routes/in_app_notifications.py similarity index 91% rename from src/fabledassistant/routes/in_app_notifications.py rename to src/scribe/routes/in_app_notifications.py index f650bc1..99fc19d 100644 --- a/src/fabledassistant/routes/in_app_notifications.py +++ b/src/scribe/routes/in_app_notifications.py @@ -1,7 +1,7 @@ from quart import Blueprint, jsonify, request -from fabledassistant.auth import get_current_user_id, login_required -from fabledassistant.services.notifications import ( +from scribe.auth import get_current_user_id, login_required +from scribe.services.notifications import ( list_in_app_notifications, mark_all_notifications_read, mark_notification_read, diff --git a/src/fabledassistant/routes/knowledge.py b/src/scribe/routes/knowledge.py similarity index 90% rename from src/fabledassistant/routes/knowledge.py rename to src/scribe/routes/knowledge.py index 7a2aa4d..ab31732 100644 --- a/src/fabledassistant/routes/knowledge.py +++ b/src/scribe/routes/knowledge.py @@ -3,8 +3,8 @@ import logging from quart import Blueprint, jsonify, request -from fabledassistant.auth import get_current_user_id, login_required -from fabledassistant.routes.utils import parse_pagination +from scribe.auth import get_current_user_id, login_required +from scribe.routes.utils import parse_pagination logger = logging.getLogger(__name__) @@ -42,7 +42,7 @@ async def list_knowledge(): limit, offset = parse_pagination(default_limit=24, max_limit=100) page = max(1, int(request.args.get("page", 1))) - from fabledassistant.services.knowledge import query_knowledge + from scribe.services.knowledge import query_knowledge items, total = await query_knowledge( user_id=uid, note_type=note_type, @@ -88,7 +88,7 @@ async def list_knowledge_ids(): if note_type and note_type not in _VALID_TYPES: return jsonify({"error": "Invalid type"}), 400 - from fabledassistant.services.knowledge import query_knowledge_ids + from scribe.services.knowledge import query_knowledge_ids ids, total = await query_knowledge_ids( user_id=uid, note_type=note_type, tags=tags, sort=sort, q=q, limit=limit, offset=offset, @@ -114,7 +114,7 @@ async def get_knowledge_batch(): if len(ids) > 100: return jsonify({"error": "Too many IDs (max 100)"}), 400 - from fabledassistant.services.knowledge import get_knowledge_by_ids + from scribe.services.knowledge import get_knowledge_by_ids items = await get_knowledge_by_ids(uid, ids) return jsonify({"items": items}) @@ -126,7 +126,7 @@ async def list_knowledge_tags(): uid = get_current_user_id() note_type = request.args.get("type", "").strip().lower() or None - from fabledassistant.services.knowledge import get_knowledge_tags + from scribe.services.knowledge import get_knowledge_tags tags = await get_knowledge_tags(uid, note_type=note_type) return jsonify({"tags": tags}) @@ -139,6 +139,6 @@ async def get_knowledge_counts(): tags_raw = request.args.get("tags", "").strip() tags = [t.strip() for t in tags_raw.split(",") if t.strip()] if tags_raw else None - from fabledassistant.services.knowledge import get_knowledge_counts as _counts + from scribe.services.knowledge import get_knowledge_counts as _counts counts = await _counts(uid, tags=tags) return jsonify(counts) diff --git a/src/fabledassistant/routes/milestones.py b/src/scribe/routes/milestones.py similarity index 92% rename from src/fabledassistant/routes/milestones.py rename to src/scribe/routes/milestones.py index 61bb9c7..02d76e1 100644 --- a/src/fabledassistant/routes/milestones.py +++ b/src/scribe/routes/milestones.py @@ -3,10 +3,10 @@ import logging from quart import Blueprint, jsonify, request -from fabledassistant.auth import login_required, get_current_user_id -from fabledassistant.routes.utils import not_found, parse_pagination -from fabledassistant.services.access import can_write_project -from fabledassistant.services.milestones import ( +from scribe.auth import login_required, get_current_user_id +from scribe.routes.utils import not_found, parse_pagination +from scribe.services.access import can_write_project +from scribe.services.milestones import ( create_milestone, delete_milestone, get_milestone_in_project, @@ -14,8 +14,8 @@ from fabledassistant.services.milestones import ( list_milestones, update_milestone, ) -from fabledassistant.services.notes import list_notes -from fabledassistant.services.projects import get_project_for_user +from scribe.services.notes import list_notes +from scribe.services.projects import get_project_for_user logger = logging.getLogger(__name__) @@ -113,7 +113,7 @@ async def delete_milestone_route(project_id: int, milestone_id: int): milestone = await get_milestone_in_project(project_id, milestone_id) if milestone is None: return not_found("Milestone") - from fabledassistant.services.trash import delete as trash_delete + from scribe.services.trash import delete as trash_delete batch = await trash_delete(milestone.user_id, "milestone", milestone_id) if batch is None: return not_found("Milestone") diff --git a/src/fabledassistant/routes/notes.py b/src/scribe/routes/notes.py similarity index 95% rename from src/fabledassistant/routes/notes.py rename to src/scribe/routes/notes.py index 50a3f2b..6864281 100644 --- a/src/fabledassistant/routes/notes.py +++ b/src/scribe/routes/notes.py @@ -2,14 +2,14 @@ import asyncio import logging import re -from fabledassistant.services.embeddings import upsert_note_embedding +from scribe.services.embeddings import upsert_note_embedding from quart import Blueprint, jsonify, request -from fabledassistant.auth import login_required, get_current_user_id -from fabledassistant.routes.utils import not_found, parse_iso_date, parse_pagination -from fabledassistant.services.access import can_write_note -from fabledassistant.services.notes import ( +from scribe.auth import login_required, get_current_user_id +from scribe.routes.utils import not_found, parse_iso_date, parse_pagination +from scribe.services.access import can_write_note +from scribe.services.notes import ( build_note_graph, convert_note_to_task, convert_task_to_note, @@ -23,8 +23,8 @@ from fabledassistant.services.notes import ( list_notes, update_note, ) -from fabledassistant.services.note_drafts import upsert_draft, get_draft, delete_draft -from fabledassistant.services.note_versions import list_versions, get_version +from scribe.services.note_drafts import upsert_draft, get_draft, delete_draft +from scribe.services.note_versions import list_versions, get_version logger = logging.getLogger(__name__) @@ -89,7 +89,7 @@ async def create_note_route(): project_id = data.get("project_id") if project_id is None and data.get("project"): - from fabledassistant.services.projects import get_project_by_title as _gpbt + from scribe.services.projects import get_project_by_title as _gpbt proj = await _gpbt(uid, data["project"]) if proj: project_id = proj.id @@ -282,7 +282,7 @@ async def delete_note_route(note_id: int): note_obj, _ = result if not await can_write_note(uid, note_id): return jsonify({"error": "Permission denied"}), 403 - from fabledassistant.services.trash import delete as trash_delete + from scribe.services.trash import delete as trash_delete batch = await trash_delete(note_obj.user_id, "note", note_id) if batch is None: return not_found("Note") @@ -445,7 +445,7 @@ async def pin_version_route(note_id: int, version_id: int): label = data.get("label") if label is not None and not isinstance(label, str): return jsonify({"error": "label must be a string or null"}), 400 - from fabledassistant.services.version_pinning import pin_version + from scribe.services.version_pinning import pin_version try: version = await pin_version(uid, note_id, version_id, label=label) except ValueError as e: @@ -462,7 +462,7 @@ async def pin_version_route(note_id: int, version_id: int): async def unpin_version_route(note_id: int, version_id: int): """Downgrade a manually-pinned version back to rolling.""" uid = get_current_user_id() - from fabledassistant.services.version_pinning import unpin_version + from scribe.services.version_pinning import unpin_version version = await unpin_version(uid, note_id, version_id) if version is None: return not_found("Version") diff --git a/src/fabledassistant/routes/profile.py b/src/scribe/routes/profile.py similarity index 92% rename from src/fabledassistant/routes/profile.py rename to src/scribe/routes/profile.py index 88f1328..a8dd439 100644 --- a/src/fabledassistant/routes/profile.py +++ b/src/scribe/routes/profile.py @@ -1,7 +1,7 @@ from quart import Blueprint, jsonify, request -from fabledassistant.auth import get_current_user_id, login_required -from fabledassistant.services.user_profile import ( +from scribe.auth import get_current_user_id, login_required +from scribe.services.user_profile import ( VALID_EXPERTISE, VALID_STYLES, VALID_TONES, diff --git a/src/fabledassistant/routes/projects.py b/src/scribe/routes/projects.py similarity index 92% rename from src/fabledassistant/routes/projects.py rename to src/scribe/routes/projects.py index f1c65e5..d0ca0f9 100644 --- a/src/fabledassistant/routes/projects.py +++ b/src/scribe/routes/projects.py @@ -4,11 +4,11 @@ import logging from quart import Blueprint, jsonify, request -from fabledassistant.auth import login_required, get_current_user_id -from fabledassistant.routes.utils import not_found, parse_pagination -from fabledassistant.services.milestones import list_milestones -from fabledassistant.services.notes import list_notes -from fabledassistant.services.projects import ( +from scribe.auth import login_required, get_current_user_id +from scribe.routes.utils import not_found, parse_pagination +from scribe.services.milestones import list_milestones +from scribe.services.notes import list_notes +from scribe.services.projects import ( create_project, delete_project, get_project, @@ -101,7 +101,7 @@ async def update_project_route(project_id: int): @login_required async def delete_project_route(project_id: int): uid = get_current_user_id() - from fabledassistant.services.trash import delete as trash_delete + from scribe.services.trash import delete as trash_delete batch = await trash_delete(uid, "project", project_id) if batch is None: return not_found("Project") diff --git a/src/fabledassistant/routes/rulebooks.py b/src/scribe/routes/rulebooks.py similarity index 98% rename from src/fabledassistant/routes/rulebooks.py rename to src/scribe/routes/rulebooks.py index 5f7c394..81617bc 100644 --- a/src/fabledassistant/routes/rulebooks.py +++ b/src/scribe/routes/rulebooks.py @@ -7,9 +7,9 @@ from __future__ import annotations from quart import Blueprint, g, jsonify, request -from fabledassistant.auth import login_required -import fabledassistant.services.rulebooks as rulebooks_svc -from fabledassistant.services.trash import delete as trash_delete +from scribe.auth import login_required +import scribe.services.rulebooks as rulebooks_svc +from scribe.services.trash import delete as trash_delete rulebooks_bp = Blueprint("rulebooks", __name__, url_prefix="/api") diff --git a/src/fabledassistant/routes/search.py b/src/scribe/routes/search.py similarity index 90% rename from src/fabledassistant/routes/search.py rename to src/scribe/routes/search.py index fb1c66a..ba34878 100644 --- a/src/fabledassistant/routes/search.py +++ b/src/scribe/routes/search.py @@ -1,7 +1,7 @@ from quart import Blueprint, jsonify, request -from fabledassistant.auth import login_required, get_current_user_id -from fabledassistant.services.embeddings import semantic_search_notes +from scribe.auth import login_required, get_current_user_id +from scribe.services.embeddings import semantic_search_notes search_bp = Blueprint("search", __name__, url_prefix="/api/search") diff --git a/src/fabledassistant/routes/settings.py b/src/scribe/routes/settings.py similarity index 92% rename from src/fabledassistant/routes/settings.py rename to src/scribe/routes/settings.py index 19d2be1..bb1ea1c 100644 --- a/src/fabledassistant/routes/settings.py +++ b/src/scribe/routes/settings.py @@ -10,10 +10,10 @@ from urllib.parse import urlparse from quart import Blueprint, jsonify, request -from fabledassistant.auth import login_required, get_current_user_id -from fabledassistant.config import Config -from fabledassistant.services.caldav import CALDAV_SETTING_KEYS, get_caldav_config, test_connection -from fabledassistant.services.settings import delete_setting, get_all_settings, get_setting, set_settings_batch +from scribe.auth import login_required, get_current_user_id +from scribe.config import Config +from scribe.services.caldav import CALDAV_SETTING_KEYS, get_caldav_config, test_connection +from scribe.services.settings import delete_setting, get_all_settings, get_setting, set_settings_batch logger = logging.getLogger(__name__) diff --git a/src/fabledassistant/routes/shares.py b/src/scribe/routes/shares.py similarity index 92% rename from src/fabledassistant/routes/shares.py rename to src/scribe/routes/shares.py index b2189a9..3fedde6 100644 --- a/src/fabledassistant/routes/shares.py +++ b/src/scribe/routes/shares.py @@ -2,11 +2,11 @@ import asyncio from quart import Blueprint, jsonify, request -from fabledassistant.auth import get_current_user_id, login_required -from fabledassistant.services import sharing as share_svc -from fabledassistant.services.access import can_admin_project, can_write_note -from fabledassistant.services.notifications import notify_note_shared, notify_project_shared -from fabledassistant.services.sharing import list_shared_with_me +from scribe.auth import get_current_user_id, login_required +from scribe.services import sharing as share_svc +from scribe.services.access import can_admin_project, can_write_note +from scribe.services.notifications import notify_note_shared, notify_project_shared +from scribe.services.sharing import list_shared_with_me shares_bp = Blueprint("shares", __name__) diff --git a/src/fabledassistant/routes/task_logs.py b/src/scribe/routes/task_logs.py similarity index 90% rename from src/fabledassistant/routes/task_logs.py rename to src/scribe/routes/task_logs.py index a442947..fc9041d 100644 --- a/src/fabledassistant/routes/task_logs.py +++ b/src/scribe/routes/task_logs.py @@ -3,8 +3,8 @@ import logging from quart import Blueprint, jsonify, request -from fabledassistant.auth import get_current_user_id, login_required -from fabledassistant.services.task_logs import create_log, delete_log, list_logs, update_log +from scribe.auth import get_current_user_id, login_required +from scribe.services.task_logs import create_log, delete_log, list_logs, update_log logger = logging.getLogger(__name__) @@ -49,7 +49,7 @@ async def update_log_route(task_id: int, log_id: int): if content is not None: content = content.strip() or None - from fabledassistant.services.task_logs import _UNSET + from scribe.services.task_logs import _UNSET duration_minutes = data.get("duration_minutes", _UNSET) log = await update_log(uid, log_id, content=content, duration_minutes=duration_minutes) diff --git a/src/fabledassistant/routes/tasks.py b/src/scribe/routes/tasks.py similarity index 93% rename from src/fabledassistant/routes/tasks.py rename to src/scribe/routes/tasks.py index 87c8bd0..0168621 100644 --- a/src/fabledassistant/routes/tasks.py +++ b/src/scribe/routes/tasks.py @@ -3,20 +3,20 @@ from datetime import date from quart import Blueprint, jsonify, request -from fabledassistant.auth import login_required, get_current_user_id -from fabledassistant.models.note import TaskPriority, TaskStatus -from fabledassistant.routes.utils import not_found, parse_iso_date, parse_pagination -from fabledassistant.services.access import can_write_note -from fabledassistant.services.embeddings import upsert_note_embedding -from fabledassistant.services.notes import ( +from scribe.auth import login_required, get_current_user_id +from scribe.models.note import TaskPriority, TaskStatus +from scribe.routes.utils import not_found, parse_iso_date, parse_pagination +from scribe.services.access import can_write_note +from scribe.services.embeddings import upsert_note_embedding +from scribe.services.notes import ( create_note, get_note, get_note_for_user, list_notes, update_note, ) -from fabledassistant.services.planning import start_planning as svc_start_planning -from fabledassistant.services.recurrence import calculate_next_due, validate_recurrence_rule +from scribe.services.planning import start_planning as svc_start_planning +from scribe.services.recurrence import calculate_next_due, validate_recurrence_rule tasks_bp = Blueprint("tasks", __name__, url_prefix="/api/tasks") @@ -117,7 +117,7 @@ async def create_task_route(): project_id = data.get("project_id") if project_id is None and data.get("project"): - from fabledassistant.services.projects import get_project_by_title as _gpbt + from scribe.services.projects import get_project_by_title as _gpbt proj = await _gpbt(uid, data["project"]) if proj: project_id = proj.id @@ -299,7 +299,7 @@ async def delete_task_route(task_id: int): task_note, _ = result if not await can_write_note(uid, task_id): return jsonify({"error": "Permission denied"}), 403 - from fabledassistant.services.trash import delete as trash_delete + from scribe.services.trash import delete as trash_delete batch = await trash_delete(task_note.user_id, "task", task_id) if batch is None: return not_found("Task") diff --git a/src/fabledassistant/routes/trash.py b/src/scribe/routes/trash.py similarity index 88% rename from src/fabledassistant/routes/trash.py rename to src/scribe/routes/trash.py index 47bba62..674194a 100644 --- a/src/fabledassistant/routes/trash.py +++ b/src/scribe/routes/trash.py @@ -3,8 +3,8 @@ from __future__ import annotations from quart import Blueprint, g, jsonify -from fabledassistant.auth import login_required -import fabledassistant.services.trash as trash_svc +from scribe.auth import login_required +import scribe.services.trash as trash_svc trash_bp = Blueprint("trash", __name__, url_prefix="/api/trash") diff --git a/src/fabledassistant/routes/users.py b/src/scribe/routes/users.py similarity index 82% rename from src/fabledassistant/routes/users.py rename to src/scribe/routes/users.py index a56f2f4..6e2d1ff 100644 --- a/src/fabledassistant/routes/users.py +++ b/src/scribe/routes/users.py @@ -1,9 +1,9 @@ from quart import Blueprint, jsonify, request from sqlalchemy import or_, select -from fabledassistant.auth import get_current_user_id, login_required -from fabledassistant.models import async_session -from fabledassistant.models.user import User +from scribe.auth import get_current_user_id, login_required +from scribe.models import async_session +from scribe.models.user import User users_bp = Blueprint("users", __name__, url_prefix="/api/users") diff --git a/src/fabledassistant/routes/utils.py b/src/scribe/routes/utils.py similarity index 100% rename from src/fabledassistant/routes/utils.py rename to src/scribe/routes/utils.py diff --git a/src/fabledassistant/services/__init__.py b/src/scribe/services/__init__.py similarity index 100% rename from src/fabledassistant/services/__init__.py rename to src/scribe/services/__init__.py diff --git a/src/fabledassistant/services/access.py b/src/scribe/services/access.py similarity index 95% rename from src/fabledassistant/services/access.py rename to src/scribe/services/access.py index b68fdf7..0d46ae2 100644 --- a/src/fabledassistant/services/access.py +++ b/src/scribe/services/access.py @@ -11,11 +11,11 @@ import logging from sqlalchemy import select -from fabledassistant.models import async_session -from fabledassistant.models.group import GroupMembership -from fabledassistant.models.note import Note -from fabledassistant.models.project import Project -from fabledassistant.models.share import NoteShare, ProjectShare +from scribe.models import async_session +from scribe.models.group import GroupMembership +from scribe.models.note import Note +from scribe.models.project import Project +from scribe.models.share import NoteShare, ProjectShare logger = logging.getLogger(__name__) diff --git a/src/fabledassistant/services/api_keys.py b/src/scribe/services/api_keys.py similarity index 96% rename from src/fabledassistant/services/api_keys.py rename to src/scribe/services/api_keys.py index 5560838..c0ed030 100644 --- a/src/fabledassistant/services/api_keys.py +++ b/src/scribe/services/api_keys.py @@ -4,8 +4,8 @@ from datetime import datetime, timezone from sqlalchemy import select -from fabledassistant.models import async_session -from fabledassistant.models.api_key import ApiKey +from scribe.models import async_session +from scribe.models.api_key import ApiKey def generate_key() -> str: diff --git a/src/fabledassistant/services/auth.py b/src/scribe/services/auth.py similarity index 97% rename from src/fabledassistant/services/auth.py rename to src/scribe/services/auth.py index ab1d005..cee92cd 100644 --- a/src/fabledassistant/services/auth.py +++ b/src/scribe/services/auth.py @@ -6,12 +6,12 @@ from datetime import datetime, timedelta, timezone import bcrypt from sqlalchemy import func, select, update -from fabledassistant.models import async_session -from fabledassistant.models.note import Note -from fabledassistant.models.invitation import InvitationToken -from fabledassistant.models.password_reset import PasswordResetToken -from fabledassistant.models.setting import Setting -from fabledassistant.models.user import User +from scribe.models import async_session +from scribe.models.note import Note +from scribe.models.invitation import InvitationToken +from scribe.models.password_reset import PasswordResetToken +from scribe.models.setting import Setting +from scribe.models.user import User logger = logging.getLogger(__name__) @@ -172,7 +172,7 @@ async def delete_user(user_id: int) -> bool: async def set_registration_open(admin_user_id: int, open: bool) -> None: - from fabledassistant.services.settings import set_setting + from scribe.services.settings import set_setting await set_setting(admin_user_id, "registration_open", "true" if open else "false") diff --git a/src/fabledassistant/services/backup.py b/src/scribe/services/backup.py similarity index 97% rename from src/fabledassistant/services/backup.py rename to src/scribe/services/backup.py index 47f72d6..7d1095e 100644 --- a/src/fabledassistant/services/backup.py +++ b/src/scribe/services/backup.py @@ -3,15 +3,15 @@ from datetime import date, datetime, timezone from sqlalchemy import select -from fabledassistant.models import async_session -from fabledassistant.models.milestone import Milestone -from fabledassistant.models.note import Note -from fabledassistant.models.note_draft import NoteDraft -from fabledassistant.models.note_version import NoteVersion -from fabledassistant.models.project import Project -from fabledassistant.models.setting import Setting -from fabledassistant.models.task_log import TaskLog -from fabledassistant.models.user import User +from scribe.models import async_session +from scribe.models.milestone import Milestone +from scribe.models.note import Note +from scribe.models.note_draft import NoteDraft +from scribe.models.note_version import NoteVersion +from scribe.models.project import Project +from scribe.models.setting import Setting +from scribe.models.task_log import TaskLog +from scribe.models.user import User logger = logging.getLogger(__name__) diff --git a/src/fabledassistant/services/caldav.py b/src/scribe/services/caldav.py similarity index 99% rename from src/fabledassistant/services/caldav.py rename to src/scribe/services/caldav.py index cb4c9a3..8d097e1 100644 --- a/src/fabledassistant/services/caldav.py +++ b/src/scribe/services/caldav.py @@ -8,7 +8,7 @@ from zoneinfo import ZoneInfo import caldav import icalendar -from fabledassistant.services.settings import get_all_settings +from scribe.services.settings import get_all_settings logger = logging.getLogger(__name__) @@ -194,7 +194,7 @@ async def create_event( tz = timezone or config.get("caldav_timezone") or None cal = icalendar.Calendar() - cal.add("prodid", "-//FabledAssistant//EN") + cal.add("prodid", "-//Scribe//EN") cal.add("version", "2.0") event = icalendar.Event() @@ -415,7 +415,7 @@ async def update_event( # Rebuild ical data and save cal_data = icalendar.Calendar() - cal_data.add("prodid", "-//FabledAssistant//EN") + cal_data.add("prodid", "-//Scribe//EN") cal_data.add("version", "2.0") cal_data.add_component(component) event_obj.data = cal_data.to_ical().decode("utf-8") @@ -690,7 +690,7 @@ async def update_todo( # Rebuild ical data and save cal_data = icalendar.Calendar() - cal_data.add("prodid", "-//FabledAssistant//EN") + cal_data.add("prodid", "-//Scribe//EN") cal_data.add("version", "2.0") cal_data.add_component(component) todo_obj.data = cal_data.to_ical().decode("utf-8") diff --git a/src/fabledassistant/services/caldav_sync.py b/src/scribe/services/caldav_sync.py similarity index 97% rename from src/fabledassistant/services/caldav_sync.py rename to src/scribe/services/caldav_sync.py index 0c04919..faa0072 100644 --- a/src/fabledassistant/services/caldav_sync.py +++ b/src/scribe/services/caldav_sync.py @@ -13,8 +13,8 @@ from typing import Any from sqlalchemy import select, update -from fabledassistant.models import async_session -from fabledassistant.models.event import Event +from scribe.models import async_session +from scribe.models.event import Event logger = logging.getLogger(__name__) @@ -112,7 +112,7 @@ async def sync_user_events(user_id: int) -> dict: Returns a summary dict: {created, updated, unchanged}. """ - from fabledassistant.services.caldav import get_caldav_config, is_caldav_configured # noqa: PLC0415 + from scribe.services.caldav import get_caldav_config, is_caldav_configured # noqa: PLC0415 if not await is_caldav_configured(user_id): return {"skipped": True, "reason": "CalDAV not configured"} @@ -234,7 +234,7 @@ async def sync_all_users() -> None: """Pull CalDAV events for all users with CalDAV configured.""" from sqlalchemy import select as sa_select # noqa: PLC0415 - from fabledassistant.models.user import User # noqa: PLC0415 + from scribe.models.user import User # noqa: PLC0415 async with async_session() as session: result = await session.execute(sa_select(User.id)) diff --git a/src/fabledassistant/services/dashboard.py b/src/scribe/services/dashboard.py similarity index 95% rename from src/fabledassistant/services/dashboard.py rename to src/scribe/services/dashboard.py index f853b1e..e879f56 100644 --- a/src/fabledassistant/services/dashboard.py +++ b/src/scribe/services/dashboard.py @@ -11,11 +11,11 @@ from datetime import datetime, timedelta, timezone from sqlalchemy import case, func, select -from fabledassistant.models import async_session -from fabledassistant.models.note import Note -from fabledassistant.models.project import Project -from fabledassistant.models.milestone import Milestone -from fabledassistant.services import milestones as milestones_svc +from scribe.models import async_session +from scribe.models.note import Note +from scribe.models.project import Project +from scribe.models.milestone import Milestone +from scribe.services import milestones as milestones_svc logger = logging.getLogger(__name__) @@ -145,7 +145,7 @@ async def _recently_completed(user_id: int) -> list[dict]: async def _upcoming_events(user_id: int) -> list[dict]: - from fabledassistant.services import events as events_svc + from scribe.services import events as events_svc now = datetime.now(timezone.utc) rows = await events_svc.list_events(user_id, now, now + timedelta(days=WINDOW_DAYS)) out = [] diff --git a/src/fabledassistant/services/diagnostics.py b/src/scribe/services/diagnostics.py similarity index 99% rename from src/fabledassistant/services/diagnostics.py rename to src/scribe/services/diagnostics.py index ff2f684..e355b86 100644 --- a/src/fabledassistant/services/diagnostics.py +++ b/src/scribe/services/diagnostics.py @@ -86,7 +86,7 @@ def _process_rss_mb() -> float | None: def _db_pool_stats() -> dict[str, Any]: """Pool checked-in / checked-out / overflow. Direct from SQLAlchemy.""" try: - from fabledassistant.models import engine + from scribe.models import engine pool = engine.pool # Async engines wrap a sync pool; .checkedin() / .checkedout() exist # on the underlying QueuePool. Attribute access is documented but @@ -172,7 +172,7 @@ def _setup_file_logger() -> logging.Logger: return _file_logger try: _DIAG_DIR.mkdir(parents=True, exist_ok=True) - flog = logging.getLogger("fabledassistant.diagnostics.persistent") + flog = logging.getLogger("scribe.diagnostics.persistent") flog.setLevel(logging.INFO) flog.propagate = False # don't double-log into the root stdout stream # Don't re-add handlers across module reloads / re-runs. diff --git a/src/fabledassistant/services/email.py b/src/scribe/services/email.py similarity index 97% rename from src/fabledassistant/services/email.py rename to src/scribe/services/email.py index 53d6362..e2455ad 100644 --- a/src/fabledassistant/services/email.py +++ b/src/scribe/services/email.py @@ -6,10 +6,10 @@ from email.message import EmailMessage import aiosmtplib from sqlalchemy import select -from fabledassistant.config import Config -from fabledassistant.models import async_session -from fabledassistant.models.setting import Setting -from fabledassistant.models.user import User +from scribe.config import Config +from scribe.models import async_session +from scribe.models.setting import Setting +from scribe.models.user import User logger = logging.getLogger(__name__) diff --git a/src/fabledassistant/services/embeddings.py b/src/scribe/services/embeddings.py similarity index 98% rename from src/fabledassistant/services/embeddings.py rename to src/scribe/services/embeddings.py index 423917e..be130d4 100644 --- a/src/fabledassistant/services/embeddings.py +++ b/src/scribe/services/embeddings.py @@ -16,9 +16,9 @@ import os from sqlalchemy import delete, select -from fabledassistant.models import async_session -from fabledassistant.models.embedding import NoteEmbedding -from fabledassistant.models.note import Note +from scribe.models import async_session +from scribe.models.embedding import NoteEmbedding +from scribe.models.note import Note logger = logging.getLogger(__name__) diff --git a/src/fabledassistant/services/event_scheduler.py b/src/scribe/services/event_scheduler.py similarity index 95% rename from src/fabledassistant/services/event_scheduler.py rename to src/scribe/services/event_scheduler.py index 45cc50c..33c6864 100644 --- a/src/fabledassistant/services/event_scheduler.py +++ b/src/scribe/services/event_scheduler.py @@ -19,8 +19,8 @@ from apscheduler.triggers.interval import IntervalTrigger from dateutil.rrule import rrulestr from sqlalchemy import and_, or_, select -from fabledassistant.models import async_session -from fabledassistant.models.event import Event +from scribe.models import async_session +from scribe.models.event import Event logger = logging.getLogger(__name__) @@ -84,7 +84,7 @@ async def _fire_reminders() -> None: return # Deliver via the in-app notification feed (push was removed in Phase 8). - from fabledassistant.services.notifications import create_in_app_notification + from scribe.services.notifications import create_in_app_notification async with async_session() as session: for event_id, occurrence_start in to_notify: @@ -116,7 +116,7 @@ def _run_reminders(loop: asyncio.AbstractEventLoop) -> None: # --------------------------------------------------------------------------- async def _run_caldav_sync() -> None: - from fabledassistant.services.caldav_sync import sync_all_users # noqa: PLC0415 + from scribe.services.caldav_sync import sync_all_users # noqa: PLC0415 try: await sync_all_users() except Exception: @@ -132,7 +132,7 @@ def _run_caldav_sync_threadsafe(loop: asyncio.AbstractEventLoop) -> None: # --------------------------------------------------------------------------- async def _run_recurrence_spawn() -> None: - from fabledassistant.services.recurrence import spawn_recurring_tasks # noqa: PLC0415 + from scribe.services.recurrence import spawn_recurring_tasks # noqa: PLC0415 try: await spawn_recurring_tasks() except Exception: diff --git a/src/fabledassistant/services/events.py b/src/scribe/services/events.py similarity index 98% rename from src/fabledassistant/services/events.py rename to src/scribe/services/events.py index 66a82da..ad2203d 100644 --- a/src/fabledassistant/services/events.py +++ b/src/scribe/services/events.py @@ -18,8 +18,8 @@ from datetime import datetime, timedelta, timezone from dateutil.rrule import rrulestr from sqlalchemy import or_, select -from fabledassistant.models import async_session -from fabledassistant.models.event import Event +from scribe.models import async_session +from scribe.models.event import Event logger = logging.getLogger(__name__) @@ -76,7 +76,7 @@ async def _localize_naive(user_id: int, dt: datetime | None) -> datetime | None: asyncpg store the correct UTC instant, matching the REST/UI path. """ if dt is not None and dt.tzinfo is None: - from fabledassistant.services.tz import get_user_tz # noqa: PLC0415 + from scribe.services.tz import get_user_tz # noqa: PLC0415 return dt.replace(tzinfo=await get_user_tz(user_id)) return dt @@ -398,7 +398,7 @@ async def find_events_by_query(user_id: int, query: str) -> list[Event]: async def _push_create(event: Event, user_id: int, extra: dict) -> None: try: - from fabledassistant.services.caldav import ( + from scribe.services.caldav import ( create_event as caldav_create, is_caldav_configured, ) @@ -438,7 +438,7 @@ async def _push_update(event: Event, user_id: int, old_title: str = "") -> None: if not event.caldav_uid: return try: - from fabledassistant.services.caldav import ( + from scribe.services.caldav import ( update_event as caldav_update, is_caldav_configured, ) @@ -466,7 +466,7 @@ async def _push_update(event: Event, user_id: int, old_title: str = "") -> None: async def _push_delete(caldav_uid: str, event_title: str, user_id: int) -> None: """Push a delete to CalDAV. Uses event_title to locate the event by SUMMARY.""" try: - from fabledassistant.services.caldav import ( + from scribe.services.caldav import ( delete_event as caldav_delete, is_caldav_configured, ) diff --git a/src/fabledassistant/services/groups.py b/src/scribe/services/groups.py similarity index 97% rename from src/fabledassistant/services/groups.py rename to src/scribe/services/groups.py index 8f47316..94224f4 100644 --- a/src/fabledassistant/services/groups.py +++ b/src/scribe/services/groups.py @@ -5,9 +5,9 @@ import logging from sqlalchemy import select from sqlalchemy.exc import IntegrityError -from fabledassistant.models import async_session -from fabledassistant.models.group import Group, GroupMembership -from fabledassistant.models.user import User +from scribe.models import async_session +from scribe.models.group import Group, GroupMembership +from scribe.models.user import User logger = logging.getLogger(__name__) diff --git a/src/fabledassistant/services/knowledge.py b/src/scribe/services/knowledge.py similarity index 98% rename from src/fabledassistant/services/knowledge.py rename to src/scribe/services/knowledge.py index 1da852b..8c4c9d7 100644 --- a/src/fabledassistant/services/knowledge.py +++ b/src/scribe/services/knowledge.py @@ -3,8 +3,8 @@ import logging from sqlalchemy import func, select -from fabledassistant.models import async_session -from fabledassistant.models.note import Note +from scribe.models import async_session +from scribe.models.note import Note logger = logging.getLogger(__name__) @@ -174,7 +174,7 @@ async def _semantic_knowledge_search( # 2. Semantic search — conceptual similarity semantic_notes: list[Note] = [] try: - from fabledassistant.services.embeddings import semantic_search_notes + from scribe.services.embeddings import semantic_search_notes is_task_filter = True if note_type in ("task", "plan") else (False if note_type else None) candidates = await semantic_search_notes( user_id=user_id, diff --git a/src/fabledassistant/services/logging.py b/src/scribe/services/logging.py similarity index 97% rename from src/fabledassistant/services/logging.py rename to src/scribe/services/logging.py index df8dac3..228e2ee 100644 --- a/src/fabledassistant/services/logging.py +++ b/src/scribe/services/logging.py @@ -8,9 +8,9 @@ from datetime import datetime, timedelta, timezone from sqlalchemy import delete, func, select, text -from fabledassistant.config import Config -from fabledassistant.models import async_session -from fabledassistant.models.app_log import AppLog +from scribe.config import Config +from scribe.models import async_session +from scribe.models.app_log import AppLog logger = logging.getLogger(__name__) diff --git a/src/fabledassistant/services/milestones.py b/src/scribe/services/milestones.py similarity index 97% rename from src/fabledassistant/services/milestones.py rename to src/scribe/services/milestones.py index fab1b98..d36174e 100644 --- a/src/fabledassistant/services/milestones.py +++ b/src/scribe/services/milestones.py @@ -4,9 +4,9 @@ from datetime import datetime, timezone from sqlalchemy import func, select -from fabledassistant.models import async_session -from fabledassistant.models.milestone import Milestone -from fabledassistant.models.note import Note +from scribe.models import async_session +from scribe.models.milestone import Milestone +from scribe.models.note import Note logger = logging.getLogger(__name__) diff --git a/src/fabledassistant/services/note_drafts.py b/src/scribe/services/note_drafts.py similarity index 95% rename from src/fabledassistant/services/note_drafts.py rename to src/scribe/services/note_drafts.py index 4f9513f..a80092a 100644 --- a/src/fabledassistant/services/note_drafts.py +++ b/src/scribe/services/note_drafts.py @@ -1,7 +1,7 @@ from sqlalchemy import select, text -from fabledassistant.models import async_session -from fabledassistant.models.note_draft import NoteDraft +from scribe.models import async_session +from scribe.models.note_draft import NoteDraft async def upsert_draft( diff --git a/src/fabledassistant/services/note_versions.py b/src/scribe/services/note_versions.py similarity index 96% rename from src/fabledassistant/services/note_versions.py rename to src/scribe/services/note_versions.py index bef59fb..1b9cf7a 100644 --- a/src/fabledassistant/services/note_versions.py +++ b/src/scribe/services/note_versions.py @@ -2,8 +2,8 @@ from datetime import datetime, timezone from sqlalchemy import select, text -from fabledassistant.models import async_session -from fabledassistant.models.note_version import NoteVersion +from scribe.models import async_session +from scribe.models.note_version import NoteVersion # Maximum snapshots retained per note. MAX_VERSIONS = 50 diff --git a/src/fabledassistant/services/notes.py b/src/scribe/services/notes.py similarity index 97% rename from src/fabledassistant/services/notes.py rename to src/scribe/services/notes.py index d971029..d7b94e3 100644 --- a/src/fabledassistant/services/notes.py +++ b/src/scribe/services/notes.py @@ -4,8 +4,8 @@ from datetime import date, datetime, timezone from sqlalchemy import func, or_, select, text -from fabledassistant.models import async_session -from fabledassistant.models.note import Note, TaskPriority, TaskStatus +from scribe.models import async_session +from scribe.models.note import Note, TaskPriority, TaskStatus logger = logging.getLogger(__name__) @@ -35,7 +35,7 @@ def _strip_type_nouns(q: str) -> list[str]: async def _maybe_reactivate_project(project_id: int) -> None: """If a project is paused, reactivate it — activity indicates resumed work.""" - from fabledassistant.models.project import Project + from scribe.models.project import Project try: async with async_session() as session: project = (await session.execute( @@ -82,7 +82,7 @@ async def create_note( # Auto-populate project_id from milestone when not explicitly provided if milestone_id is not None and project_id is None: - from fabledassistant.models.milestone import Milestone + from scribe.models.milestone import Milestone async with async_session() as lookup: result = await lookup.execute( select(Milestone).where(Milestone.id == milestone_id, Milestone.user_id == user_id) @@ -227,7 +227,7 @@ async def list_notes( count_query = count_query.where(Note.project_id.is_(None)) if exclude_paused_projects: - from fabledassistant.models.project import Project + from scribe.models.project import Project paused_ids = ( select(Project.id) .where(Project.user_id == user_id, Project.status == "paused") @@ -308,7 +308,7 @@ async def update_note(user_id: int, note_id: int, **fields: object) -> Note | No elif note.status in (TaskStatus.done.value, TaskStatus.cancelled.value): note.completed_at = _now if note.recurrence_rule: - from fabledassistant.services.recurrence import calculate_next_due + from scribe.services.recurrence import calculate_next_due base = note.due_date or _now.date() next_due = calculate_next_due(note.recurrence_rule, base) note.recurrence_next_spawn_at = datetime( @@ -324,7 +324,7 @@ async def update_note(user_id: int, note_id: int, **fields: object) -> Note | No # Create a version snapshot when body actually changes if "body" in fields and fields["body"] != old_body: - from fabledassistant.services.note_versions import create_version + from scribe.services.note_versions import create_version await create_version(user_id, note_id, old_body, old_title, old_tags) if note.project_id is not None: @@ -501,7 +501,7 @@ async def build_note_graph( project_ids = {n.project_id for n in notes if n.project_id is not None} project_colors: dict[int, str] = {} if project_ids: - from fabledassistant.models.project import Project + from scribe.models.project import Project async with async_session() as session: result = await session.execute( select(Project.id, Project.color).where(Project.id.in_(project_ids)) @@ -572,7 +572,7 @@ async def get_note_for_user( accessing_user_id: int, note_id: int ) -> tuple["Note", str] | None: """Returns (note, permission) if user has any access, else None.""" - from fabledassistant.services.access import get_note_permission + from scribe.services.access import get_note_permission perm = await get_note_permission(accessing_user_id, note_id) if perm is None: return None diff --git a/src/fabledassistant/services/notifications.py b/src/scribe/services/notifications.py similarity index 95% rename from src/fabledassistant/services/notifications.py rename to src/scribe/services/notifications.py index 20a79c2..ae84c9f 100644 --- a/src/fabledassistant/services/notifications.py +++ b/src/scribe/services/notifications.py @@ -7,13 +7,13 @@ from datetime import date, datetime, timezone from sqlalchemy import func, select, text -from fabledassistant.models import async_session -from fabledassistant.models.app_log import AppLog -from fabledassistant.models.note import Note -from fabledassistant.models.setting import Setting -from fabledassistant.models.user import User -from fabledassistant.services.email import _email_html, is_smtp_configured, send_email -from fabledassistant.services.logging import log_audit +from scribe.models import async_session +from scribe.models.app_log import AppLog +from scribe.models.note import Note +from scribe.models.setting import Setting +from scribe.models.user import User +from scribe.services.email import _email_html, is_smtp_configured, send_email +from scribe.services.logging import log_audit logger = logging.getLogger(__name__) @@ -245,7 +245,7 @@ async def purge_old_read_notifications(retention_days: int = _NOTIFICATION_RETEN """Delete already-read in-app notifications older than retention_days.""" from datetime import timedelta from sqlalchemy import delete - from fabledassistant.models.notification import Notification + from scribe.models.notification import Notification cutoff = datetime.now(timezone.utc) - timedelta(days=retention_days) async with async_session() as session: result = await session.execute( @@ -285,7 +285,7 @@ def start_notification_loop() -> None: async def create_in_app_notification(user_id: int, notif_type: str, payload: dict): """Create an in-app Notification record.""" - from fabledassistant.models.notification import Notification + from scribe.models.notification import Notification async with async_session() as session: n = Notification(user_id=user_id, type=notif_type, payload=payload) session.add(n) @@ -308,7 +308,7 @@ async def _fire_share_email(user_id: int, subject: str, body_text: str) -> None: async def _group_member_ids(group_id: int) -> list[int]: - from fabledassistant.models.group import GroupMembership + from scribe.models.group import GroupMembership async with async_session() as session: rows = (await session.execute( select(GroupMembership.user_id).where(GroupMembership.group_id == group_id) @@ -323,7 +323,7 @@ async def notify_project_shared( target_user_id: int | None, target_group_id: int | None, ) -> None: - from fabledassistant.models.project import Project + from scribe.models.project import Project async with async_session() as session: project = await session.get(Project, project_id) inviter = await session.get(User, invited_by_user_id) @@ -357,7 +357,7 @@ async def notify_note_shared( target_user_id: int | None, target_group_id: int | None, ) -> None: - from fabledassistant.models.note import Note + from scribe.models.note import Note async with async_session() as session: note = await session.get(Note, note_id) inviter = await session.get(User, invited_by_user_id) @@ -388,7 +388,7 @@ async def notify_note_shared( async def notify_group_added( group_id: int, role: str, invited_by_user_id: int, target_user_id: int ) -> None: - from fabledassistant.models.group import Group + from scribe.models.group import Group async with async_session() as session: group = await session.get(Group, group_id) inviter = await session.get(User, invited_by_user_id) @@ -409,7 +409,7 @@ async def notify_group_added( async def list_in_app_notifications(user_id: int, unread_only: bool = True) -> list[dict]: - from fabledassistant.models.notification import Notification + from scribe.models.notification import Notification async with async_session() as session: q = select(Notification).where(Notification.user_id == user_id) if unread_only: @@ -420,7 +420,7 @@ async def list_in_app_notifications(user_id: int, unread_only: bool = True) -> l async def unread_notification_count(user_id: int) -> int: - from fabledassistant.models.notification import Notification + from scribe.models.notification import Notification async with async_session() as session: result = await session.execute( select(func.count()).where( @@ -432,7 +432,7 @@ async def unread_notification_count(user_id: int) -> int: async def mark_notification_read(user_id: int, notification_id: int) -> bool: - from fabledassistant.models.notification import Notification + from scribe.models.notification import Notification from datetime import timezone as tz async with async_session() as session: n = (await session.execute( @@ -450,7 +450,7 @@ async def mark_notification_read(user_id: int, notification_id: int) -> bool: async def mark_all_notifications_read(user_id: int) -> int: - from fabledassistant.models.notification import Notification + from scribe.models.notification import Notification from datetime import datetime, timezone as tz from sqlalchemy import update as sa_update async with async_session() as session: diff --git a/src/fabledassistant/services/oauth.py b/src/scribe/services/oauth.py similarity index 96% rename from src/fabledassistant/services/oauth.py rename to src/scribe/services/oauth.py index 81fd8fe..d22d20d 100644 --- a/src/fabledassistant/services/oauth.py +++ b/src/scribe/services/oauth.py @@ -3,9 +3,9 @@ import urllib.parse import httpx -from fabledassistant.config import Config -from fabledassistant.models import async_session -from fabledassistant.models.user import User +from scribe.config import Config +from scribe.models import async_session +from scribe.models.user import User from sqlalchemy import select logger = logging.getLogger(__name__) diff --git a/src/fabledassistant/services/planning.py b/src/scribe/services/planning.py similarity index 91% rename from src/fabledassistant/services/planning.py rename to src/scribe/services/planning.py index 3fa2f08..413247b 100644 --- a/src/fabledassistant/services/planning.py +++ b/src/scribe/services/planning.py @@ -4,9 +4,9 @@ in Scribe and rules surface at the planning moment. """ from __future__ import annotations -from fabledassistant.services import notes as notes_svc -from fabledassistant.services import projects as projects_svc -from fabledassistant.services import rulebooks as rulebooks_svc +from scribe.services import notes as notes_svc +from scribe.services import projects as projects_svc +from scribe.services import rulebooks as rulebooks_svc PLAN_TEMPLATE = """## Goal diff --git a/src/fabledassistant/services/projects.py b/src/scribe/services/projects.py similarity index 94% rename from src/fabledassistant/services/projects.py rename to src/scribe/services/projects.py index 3b7ed74..0a51c99 100644 --- a/src/fabledassistant/services/projects.py +++ b/src/scribe/services/projects.py @@ -4,9 +4,9 @@ from datetime import datetime, timezone from sqlalchemy import func, select -from fabledassistant.models import async_session -from fabledassistant.models.note import Note -from fabledassistant.models.project import Project, ProjectStatus +from scribe.models import async_session +from scribe.models.note import Note +from scribe.models.project import Project, ProjectStatus logger = logging.getLogger(__name__) @@ -167,7 +167,7 @@ async def get_project_summary(user_id: int, project_id: int) -> dict: ) ) - from fabledassistant.services.milestones import get_project_milestone_summary + from scribe.services.milestones import get_project_milestone_summary milestone_summary = await get_project_milestone_summary(user_id, project_id) return { @@ -186,7 +186,7 @@ async def get_project_for_user( accessing_user_id: int, project_id: int ) -> tuple[Project, str] | None: """Returns (project, permission) if user has any access, else None.""" - from fabledassistant.services.access import get_project_permission + from scribe.services.access import get_project_permission perm = await get_project_permission(accessing_user_id, project_id) if perm is None: return None @@ -197,9 +197,9 @@ async def get_project_for_user( async def list_projects_for_user(user_id: int, status: str | None = None) -> list[dict]: """Owned projects + shared projects, each dict has 'permission' field.""" - from fabledassistant.models.group import GroupMembership - from fabledassistant.models.share import ProjectShare - from fabledassistant.services.access import PERMISSION_RANK + from scribe.models.group import GroupMembership + from scribe.models.share import ProjectShare + from scribe.services.access import PERMISSION_RANK owned = await list_projects(user_id, status) owned_ids = {p.id for p in owned} diff --git a/src/fabledassistant/services/recurrence.py b/src/scribe/services/recurrence.py similarity index 96% rename from src/fabledassistant/services/recurrence.py rename to src/scribe/services/recurrence.py index 77f57b4..c6f2f6b 100644 --- a/src/fabledassistant/services/recurrence.py +++ b/src/scribe/services/recurrence.py @@ -6,8 +6,8 @@ from datetime import date, datetime, timedelta, timezone from sqlalchemy import and_, select -from fabledassistant.models import async_session -from fabledassistant.models.note import Note, TaskStatus +from scribe.models import async_session +from scribe.models.note import Note, TaskStatus logger = logging.getLogger(__name__) @@ -103,8 +103,8 @@ async def spawn_recurring_tasks() -> int: Returns the number of tasks spawned. """ - from fabledassistant.services.embeddings import upsert_note_embedding - from fabledassistant.services.notes import create_note + from scribe.services.embeddings import upsert_note_embedding + from scribe.services.notes import create_note now = datetime.now(timezone.utc) spawned = 0 diff --git a/src/fabledassistant/services/rulebooks.py b/src/scribe/services/rulebooks.py similarity index 96% rename from src/fabledassistant/services/rulebooks.py rename to src/scribe/services/rulebooks.py index d8f1070..18238c6 100644 --- a/src/fabledassistant/services/rulebooks.py +++ b/src/scribe/services/rulebooks.py @@ -12,8 +12,8 @@ from typing import Optional from sqlalchemy import select -from fabledassistant.models import async_session -from fabledassistant.models.rulebook import Rulebook +from scribe.models import async_session +from scribe.models.rulebook import Rulebook logger = logging.getLogger(__name__) @@ -114,7 +114,7 @@ async def find_rulebook_by_title( # ── Topic CRUD ────────────────────────────────────────────────────────── -from fabledassistant.models.rulebook import RulebookTopic +from scribe.models.rulebook import RulebookTopic async def _assert_rulebook_owned(session, rulebook_id: int, user_id: int) -> None: @@ -223,7 +223,7 @@ async def delete_topic(topic_id: int, user_id: int) -> None: # ── Rule CRUD ────────────────────────────────────────────────────────── -from fabledassistant.models.rulebook import Rule +from scribe.models.rulebook import Rule async def _assert_topic_owned(session, topic_id: int, user_id: int) -> None: @@ -244,7 +244,7 @@ async def _assert_topic_owned(session, topic_id: int, user_id: int) -> None: async def _assert_project_owned(session, project_id: int, user_id: int) -> None: """Raise ValueError if project doesn't exist or isn't owned by user.""" - from fabledassistant.models.project import Project + from scribe.models.project import Project result = await session.execute( select(Project).where( Project.id == project_id, @@ -263,7 +263,7 @@ async def _assert_rulebook_rule_owned(session, rule_id: int, user_id: int) -> No suppressible — they belong to the project; delete them instead. This helper deliberately excludes them. """ - from fabledassistant.models.rulebook import Rule + from scribe.models.rulebook import Rule result = await session.execute( select(Rule) .join(RulebookTopic, Rule.topic_id == RulebookTopic.id) @@ -341,7 +341,7 @@ async def list_rules( returned — adding all of a user's project-scoped rules unprompted would surprise existing callers. """ - from fabledassistant.models.rulebook import project_rulebook_subscriptions + from scribe.models.rulebook import project_rulebook_subscriptions async with async_session() as session: stmt = ( @@ -378,7 +378,7 @@ async def list_rules( # Project-scoped rules (topic_id IS NULL, project_id matches). # Verifies ownership by joining Project on user_id. - from fabledassistant.models.project import Project + from scribe.models.project import Project proj_stmt = ( select(Rule) .join(Project, Rule.project_id == Project.id) @@ -426,7 +426,7 @@ async def _fetch_owned_rule(session, rule_id: int, user_id: int) -> Optional[Rul (via topic) or its project (via project_id). Honors soft-delete. Returns None when not found or not owned. """ - from fabledassistant.models.project import Project + from scribe.models.project import Project # Path A — rulebook rule. rulebook_rule = (await session.execute( @@ -495,7 +495,7 @@ async def subscribe_project( project_id: int, rulebook_id: int, user_id: int, ) -> None: """Add a subscription. Idempotent — duplicates raise; we swallow.""" - from fabledassistant.models.rulebook import project_rulebook_subscriptions + from scribe.models.rulebook import project_rulebook_subscriptions async with async_session() as session: await _assert_project_owned(session, project_id, user_id) @@ -515,7 +515,7 @@ async def subscribe_project( async def unsubscribe_project( project_id: int, rulebook_id: int, user_id: int, ) -> None: - from fabledassistant.models.rulebook import project_rulebook_subscriptions + from scribe.models.rulebook import project_rulebook_subscriptions async with async_session() as session: await _assert_project_owned(session, project_id, user_id) @@ -535,7 +535,7 @@ async def suppress_rule_for_project( project_id: int, rule_id: int, user_id: int, ) -> None: """Mute one rulebook rule for one project. Idempotent.""" - from fabledassistant.models.rulebook import project_rule_suppressions + from scribe.models.rulebook import project_rule_suppressions async with async_session() as session: await _assert_project_owned(session, project_id, user_id) @@ -555,7 +555,7 @@ async def unsuppress_rule_for_project( project_id: int, rule_id: int, user_id: int, ) -> None: """Unmute one rulebook rule for one project. Idempotent.""" - from fabledassistant.models.rulebook import project_rule_suppressions + from scribe.models.rulebook import project_rule_suppressions async with async_session() as session: await _assert_project_owned(session, project_id, user_id) @@ -572,7 +572,7 @@ async def suppress_topic_for_project( project_id: int, topic_id: int, user_id: int, ) -> None: """Mute every rule under one topic for one project. Idempotent.""" - from fabledassistant.models.rulebook import project_topic_suppressions + from scribe.models.rulebook import project_topic_suppressions async with async_session() as session: await _assert_project_owned(session, project_id, user_id) @@ -592,7 +592,7 @@ async def unsuppress_topic_for_project( project_id: int, topic_id: int, user_id: int, ) -> None: """Unmute a topic for one project. Idempotent.""" - from fabledassistant.models.rulebook import project_topic_suppressions + from scribe.models.rulebook import project_topic_suppressions async with async_session() as session: await _assert_project_owned(session, project_id, user_id) @@ -633,7 +633,7 @@ async def get_applicable_rules( titles + rulebook context callers need to display what was filtered without round-tripping for names. """ - from fabledassistant.models.rulebook import ( + from scribe.models.rulebook import ( project_rulebook_subscriptions, project_rule_suppressions, project_topic_suppressions, @@ -753,7 +753,7 @@ async def get_applicable_rules( ] # Project-scoped rules — verifies ownership via Project.user_id. - from fabledassistant.models.project import Project + from scribe.models.project import Project proj_rules_q = ( select(Rule.id, Rule.title, Rule.statement) .join(Project, Rule.project_id == Project.id) diff --git a/src/fabledassistant/services/settings.py b/src/scribe/services/settings.py similarity index 95% rename from src/fabledassistant/services/settings.py rename to src/scribe/services/settings.py index aefcc7e..9502944 100644 --- a/src/fabledassistant/services/settings.py +++ b/src/scribe/services/settings.py @@ -2,8 +2,8 @@ import logging from sqlalchemy import delete as sa_delete, select -from fabledassistant.models import async_session -from fabledassistant.models.setting import Setting +from scribe.models import async_session +from scribe.models.setting import Setting logger = logging.getLogger(__name__) diff --git a/src/fabledassistant/services/sharing.py b/src/scribe/services/sharing.py similarity index 91% rename from src/fabledassistant/services/sharing.py rename to src/scribe/services/sharing.py index 5d7a8bf..036c43c 100644 --- a/src/fabledassistant/services/sharing.py +++ b/src/scribe/services/sharing.py @@ -4,12 +4,12 @@ import logging from sqlalchemy import select -from fabledassistant.models import async_session -from fabledassistant.models.group import Group, GroupMembership -from fabledassistant.models.note import Note -from fabledassistant.models.project import Project -from fabledassistant.models.share import NoteShare, ProjectShare -from fabledassistant.models.user import User +from scribe.models import async_session +from scribe.models.group import Group, GroupMembership +from scribe.models.note import Note +from scribe.models.project import Project +from scribe.models.share import NoteShare, ProjectShare +from scribe.models.user import User logger = logging.getLogger(__name__) @@ -33,7 +33,7 @@ async def _enrich_shares(session, shares) -> list[dict]: def _deduplicate_by_permission(shares, id_attr: str) -> dict[int, str]: """Return {resource_id: best_permission} keeping the highest-ranked permission per resource.""" - from fabledassistant.services.access import PERMISSION_RANK + from scribe.services.access import PERMISSION_RANK seen: dict[int, str] = {} for share in shares: rid = getattr(share, id_attr) @@ -57,7 +57,7 @@ async def share_project( ) -> ProjectShare | None: if permission not in VALID_PERMISSIONS: return None - from fabledassistant.services.access import can_admin_project + from scribe.services.access import can_admin_project if not await can_admin_project(acting_user_id, project_id): return None async with async_session() as session: @@ -83,7 +83,7 @@ async def update_project_share( share = await session.get(ProjectShare, share_id) if not share: return None - from fabledassistant.services.access import can_admin_project + from scribe.services.access import can_admin_project if not await can_admin_project(acting_user_id, share.project_id): return None share.permission = permission @@ -97,7 +97,7 @@ async def remove_project_share(acting_user_id: int, share_id: int) -> bool: share = await session.get(ProjectShare, share_id) if not share: return False - from fabledassistant.services.access import can_admin_project + from scribe.services.access import can_admin_project if not await can_admin_project(acting_user_id, share.project_id): return False await session.delete(share) @@ -127,7 +127,7 @@ async def share_note( ) -> NoteShare | None: if permission not in VALID_PERMISSIONS: return None - from fabledassistant.services.access import get_note_permission + from scribe.services.access import get_note_permission perm = await get_note_permission(acting_user_id, note_id) if perm not in ("admin", "owner"): return None @@ -154,7 +154,7 @@ async def update_note_share( share = await session.get(NoteShare, share_id) if not share: return None - from fabledassistant.services.access import get_note_permission + from scribe.services.access import get_note_permission perm = await get_note_permission(acting_user_id, share.note_id) if perm not in ("admin", "owner"): return None @@ -169,7 +169,7 @@ async def remove_note_share(acting_user_id: int, share_id: int) -> bool: share = await session.get(NoteShare, share_id) if not share: return False - from fabledassistant.services.access import get_note_permission + from scribe.services.access import get_note_permission perm = await get_note_permission(acting_user_id, share.note_id) if perm not in ("admin", "owner"): return False diff --git a/src/fabledassistant/services/task_logs.py b/src/scribe/services/task_logs.py similarity index 94% rename from src/fabledassistant/services/task_logs.py rename to src/scribe/services/task_logs.py index d894f87..7a96165 100644 --- a/src/fabledassistant/services/task_logs.py +++ b/src/scribe/services/task_logs.py @@ -4,9 +4,9 @@ from datetime import datetime, timezone from sqlalchemy import select -from fabledassistant.models import async_session -from fabledassistant.models.task_log import TaskLog -from fabledassistant.models.note import Note +from scribe.models import async_session +from scribe.models.task_log import TaskLog +from scribe.models.note import Note logger = logging.getLogger(__name__) diff --git a/src/fabledassistant/services/trash.py b/src/scribe/services/trash.py similarity index 96% rename from src/fabledassistant/services/trash.py rename to src/scribe/services/trash.py index 07da97c..0ad61fd 100644 --- a/src/fabledassistant/services/trash.py +++ b/src/scribe/services/trash.py @@ -12,12 +12,12 @@ from datetime import datetime, timezone from sqlalchemy import or_, select, update -from fabledassistant.models import async_session -from fabledassistant.models.note import Note -from fabledassistant.models.event import Event -from fabledassistant.models.project import Project -from fabledassistant.models.milestone import Milestone -from fabledassistant.models.rulebook import Rulebook, RulebookTopic, Rule +from scribe.models import async_session +from scribe.models.note import Note +from scribe.models.event import Event +from scribe.models.project import Project +from scribe.models.milestone import Milestone +from scribe.models.rulebook import Rulebook, RulebookTopic, Rule # entity_type -> Model. Used to resolve which table a trash op targets. _MODEL_FOR = { @@ -90,7 +90,7 @@ async def _cascade(session, user_id: int, etype: str, eid: int, batch: str, now) # soft-delete path keeps the project row alive; this guarantees the # rows are gone whether or not the project ever gets purged. from sqlalchemy import delete as _sql_delete - from fabledassistant.models.rulebook import ( + from scribe.models.rulebook import ( project_rule_suppressions, project_topic_suppressions, ) await session.execute( @@ -169,7 +169,7 @@ async def delete(user_id: int, entity_type: str, entity_id: int) -> str | None: # copy lingers forever (and re-appears on any client syncing that server). if caldav_event: import asyncio - from fabledassistant.services.events import _push_delete + from scribe.services.events import _push_delete asyncio.create_task(_push_delete(caldav_event[0], caldav_event[1], user_id)) return batch diff --git a/src/fabledassistant/services/trash_scheduler.py b/src/scribe/services/trash_scheduler.py similarity index 91% rename from src/fabledassistant/services/trash_scheduler.py rename to src/scribe/services/trash_scheduler.py index 12a5ad8..3d5a5fa 100644 --- a/src/fabledassistant/services/trash_scheduler.py +++ b/src/scribe/services/trash_scheduler.py @@ -13,8 +13,8 @@ import logging from apscheduler.schedulers.background import BackgroundScheduler from apscheduler.triggers.cron import CronTrigger -from fabledassistant.services import trash as trash_svc -from fabledassistant.services.settings import get_setting +from scribe.services import trash as trash_svc +from scribe.services.settings import get_setting logger = logging.getLogger(__name__) @@ -32,8 +32,8 @@ def _run_purge_threadsafe() -> None: try: from sqlalchemy import select - from fabledassistant.models import async_session - from fabledassistant.models.user import User + from scribe.models import async_session + from scribe.models.user import User async with async_session() as session: user_ids = (await session.execute(select(User.id))).scalars().all() diff --git a/src/fabledassistant/services/tz.py b/src/scribe/services/tz.py similarity index 97% rename from src/fabledassistant/services/tz.py rename to src/scribe/services/tz.py index fb73bf3..e6f50c8 100644 --- a/src/fabledassistant/services/tz.py +++ b/src/scribe/services/tz.py @@ -11,7 +11,7 @@ from __future__ import annotations from datetime import date, datetime, timedelta from zoneinfo import ZoneInfo, ZoneInfoNotFoundError -from fabledassistant.services.settings import get_setting +from scribe.services.settings import get_setting # Day-rollover boundary for journal/day-anchored views. The "day" flips at # this local hour (not midnight) so the 00:00–04:00 local window still diff --git a/src/fabledassistant/services/user_profile.py b/src/scribe/services/user_profile.py similarity index 94% rename from src/fabledassistant/services/user_profile.py rename to src/scribe/services/user_profile.py index 0813012..62abaae 100644 --- a/src/fabledassistant/services/user_profile.py +++ b/src/scribe/services/user_profile.py @@ -9,8 +9,8 @@ import logging from sqlalchemy import select -from fabledassistant.models import async_session -from fabledassistant.models.user_profile import UserProfile +from scribe.models import async_session +from scribe.models.user_profile import UserProfile logger = logging.getLogger(__name__) diff --git a/src/fabledassistant/services/version_pinning.py b/src/scribe/services/version_pinning.py similarity index 98% rename from src/fabledassistant/services/version_pinning.py rename to src/scribe/services/version_pinning.py index 159c3c7..ce1fdbd 100644 --- a/src/fabledassistant/services/version_pinning.py +++ b/src/scribe/services/version_pinning.py @@ -19,8 +19,8 @@ from datetime import timezone from sqlalchemy import select, text -from fabledassistant.models import async_session -from fabledassistant.models.note_version import NoteVersion +from scribe.models import async_session +from scribe.models.note_version import NoteVersion logger = logging.getLogger(__name__) @@ -199,7 +199,7 @@ async def scan_all_users_for_auto_pins() -> dict[int, int]: """Top-level scan entrypoint. Iterates over all users and runs the per-user scan. Returns {user_id: newly_pinned_count}. Per-user errors are caught and logged so one user's failure doesn't stop the scan.""" - from fabledassistant.models import User + from scribe.models import User async with async_session() as session: users = (await session.execute(select(User.id))).scalars().all() diff --git a/src/fabledassistant/services/version_pinning_scheduler.py b/src/scribe/services/version_pinning_scheduler.py similarity index 96% rename from src/fabledassistant/services/version_pinning_scheduler.py rename to src/scribe/services/version_pinning_scheduler.py index 1b04fe1..7189a86 100644 --- a/src/fabledassistant/services/version_pinning_scheduler.py +++ b/src/scribe/services/version_pinning_scheduler.py @@ -16,7 +16,7 @@ import logging from apscheduler.schedulers.background import BackgroundScheduler from apscheduler.triggers.cron import CronTrigger -from fabledassistant.services.version_pinning import scan_all_users_for_auto_pins +from scribe.services.version_pinning import scan_all_users_for_auto_pins logger = logging.getLogger(__name__) diff --git a/src/fabledassistant/static/.gitkeep b/src/scribe/static/.gitkeep similarity index 100% rename from src/fabledassistant/static/.gitkeep rename to src/scribe/static/.gitkeep diff --git a/src/fabledassistant/utils/__init__.py b/src/scribe/utils/__init__.py similarity index 100% rename from src/fabledassistant/utils/__init__.py rename to src/scribe/utils/__init__.py diff --git a/src/fabledassistant/utils/tags.py b/src/scribe/utils/tags.py similarity index 100% rename from src/fabledassistant/utils/tags.py rename to src/scribe/utils/tags.py diff --git a/tests/test_api_keys.py b/tests/test_api_keys.py index c9f4842..dbe165b 100644 --- a/tests/test_api_keys.py +++ b/tests/test_api_keys.py @@ -4,7 +4,7 @@ from unittest.mock import AsyncMock, MagicMock, patch import pytest -from fabledassistant.services.api_keys import ( +from scribe.services.api_keys import ( _hash_key, _key_prefix, generate_key, @@ -44,7 +44,7 @@ async def test_create_api_key_returns_full_key(): mock_key_obj.id = 1 mock_key_obj.to_dict.return_value = {"id": 1, "name": "test", "scope": "read", "key_prefix": "fmcp_xxx"} - with patch("fabledassistant.services.api_keys.async_session") as mock_session_ctx: + with patch("scribe.services.api_keys.async_session") as mock_session_ctx: mock_session = AsyncMock() mock_session.__aenter__ = AsyncMock(return_value=mock_session) mock_session.__aexit__ = AsyncMock(return_value=False) @@ -57,7 +57,7 @@ async def test_create_api_key_returns_full_key(): mock_session_ctx.return_value = mock_session # Patch the select result - with patch("fabledassistant.services.api_keys.ApiKey") as mock_model: + with patch("scribe.services.api_keys.ApiKey") as mock_model: mock_model.return_value = mock_key_obj full_key, key_dict = await create_api_key(user_id=1, name="test", scope="read") @@ -66,7 +66,7 @@ async def test_create_api_key_returns_full_key(): @pytest.mark.asyncio async def test_lookup_key_returns_none_for_unknown(): - with patch("fabledassistant.services.api_keys.async_session") as mock_session_ctx: + with patch("scribe.services.api_keys.async_session") as mock_session_ctx: mock_session = AsyncMock() mock_session.__aenter__ = AsyncMock(return_value=mock_session) mock_session.__aexit__ = AsyncMock(return_value=False) @@ -112,15 +112,15 @@ async def test_read_only_key_blocked_on_post(): from quart import jsonify return jsonify({"ok": True}) - from fabledassistant.auth import _check_auth + from scribe.auth import _check_auth wrapped = _check_auth(dummy) async with app.test_request_context( "/test", method="POST", headers={"Authorization": "Bearer fmcp_readonly"}, ): - with patch("fabledassistant.auth.lookup_key", AsyncMock(return_value=fake_key)), \ - patch("fabledassistant.auth.get_user_by_id", AsyncMock(return_value=fake_user)): + with patch("scribe.auth.lookup_key", AsyncMock(return_value=fake_key)), \ + patch("scribe.auth.get_user_by_id", AsyncMock(return_value=fake_user)): resp = await wrapped() assert resp[1] == 403 @@ -149,15 +149,15 @@ async def test_bearer_token_valid_write_key_passes(): from quart import jsonify return jsonify({"ok": True}) - from fabledassistant.auth import _check_auth + from scribe.auth import _check_auth wrapped = _check_auth(dummy) async with app.test_request_context( "/test", method="POST", headers={"Authorization": "Bearer fmcp_writekey"}, ): - with patch("fabledassistant.auth.lookup_key", AsyncMock(return_value=fake_key)), \ - patch("fabledassistant.auth.get_user_by_id", AsyncMock(return_value=fake_user)): + with patch("scribe.auth.lookup_key", AsyncMock(return_value=fake_key)), \ + patch("scribe.auth.get_user_by_id", AsyncMock(return_value=fake_user)): resp = await wrapped() # Should not be a tuple (no error response) diff --git a/tests/test_embeddings.py b/tests/test_embeddings.py index 3706904..e887170 100644 --- a/tests/test_embeddings.py +++ b/tests/test_embeddings.py @@ -7,7 +7,7 @@ from unittest.mock import AsyncMock, MagicMock, patch import pytest -from fabledassistant.services.embeddings import ( +from scribe.services.embeddings import ( _cosine_similarity, get_embedding, ) @@ -54,7 +54,7 @@ async def test_get_embedding_returns_list_of_floats(): fake_embedder = MagicMock() fake_embedder.embed = MagicMock(return_value=iter([fake_vec])) with patch( - "fabledassistant.services.embeddings._get_model", + "scribe.services.embeddings._get_model", AsyncMock(return_value=fake_embedder), ): out = await get_embedding("hello world") @@ -71,7 +71,7 @@ async def test_get_embedding_ignores_legacy_model_param(): fake_embedder = MagicMock() fake_embedder.embed = MagicMock(return_value=iter([fake_vec])) with patch( - "fabledassistant.services.embeddings._get_model", + "scribe.services.embeddings._get_model", AsyncMock(return_value=fake_embedder), ): out = await get_embedding("x", model="ignored-model-name") @@ -83,7 +83,7 @@ async def test_get_embedding_propagates_model_load_failures(): """If fastembed can't initialize, the error propagates — callers catch and degrade to keyword search.""" with patch( - "fabledassistant.services.embeddings._get_model", + "scribe.services.embeddings._get_model", AsyncMock(side_effect=RuntimeError("model load failed")), ): with pytest.raises(RuntimeError, match="model load failed"): diff --git a/tests/test_events_model.py b/tests/test_events_model.py index 6b981a8..a01da39 100644 --- a/tests/test_events_model.py +++ b/tests/test_events_model.py @@ -1,12 +1,12 @@ def test_event_model_has_new_columns(): - from fabledassistant.models.event import Event + from scribe.models.event import Event cols = {c.key for c in Event.__table__.columns} assert "caldav_uid" in cols assert "color" in cols def test_event_to_dict_includes_new_fields(): - from fabledassistant.models.event import Event + from scribe.models.event import Event from datetime import datetime, timezone e = Event( user_id=1, uid="test-uid", title="Test", @@ -21,6 +21,6 @@ def test_event_to_dict_includes_new_fields(): def test_caldav_create_event_accepts_uid_param(): """caldav.create_event signature must accept an optional uid param.""" import inspect - from fabledassistant.services.caldav import create_event + from scribe.services.caldav import create_event sig = inspect.signature(create_event) assert "uid" in sig.parameters diff --git a/tests/test_events_routes.py b/tests/test_events_routes.py index d58ee97..cdaccf1 100644 --- a/tests/test_events_routes.py +++ b/tests/test_events_routes.py @@ -9,19 +9,19 @@ tests in test_events_service.py. def test_events_blueprint_registered(): """events_bp must be importable and have the correct name.""" - from fabledassistant.routes.events import events_bp + from scribe.routes.events import events_bp assert events_bp.name == "events" assert events_bp.url_prefix == "/api/events" def test_events_blueprint_has_five_routes(): """Blueprint must declare routes for GET/POST '' and GET/PATCH/DELETE '/'.""" - from fabledassistant.routes.events import events_bp + from scribe.routes.events import events_bp methods_by_rule: dict[str, set[str]] = {} for rule in events_bp.deferred_functions: pass # deferred; inspect via url_map after binding # Import routes module to confirm all 5 view functions exist - from fabledassistant.routes import events as events_module + from scribe.routes import events as events_module assert callable(events_module.list_events) assert callable(events_module.create_event) assert callable(events_module.get_event) @@ -31,7 +31,7 @@ def test_events_blueprint_has_five_routes(): def test_events_blueprint_registered_in_app(): """events_bp must be registered in the app factory.""" - from fabledassistant.app import create_app + from scribe.app import create_app app = create_app() # Check the blueprint is present in the app's blueprints dict assert "events" in app.blueprints @@ -43,7 +43,7 @@ def test_events_service_ownership_enforced_on_get(): # The service returns None when the event belongs to a different user, # and the route converts that to a 404 response. import inspect - from fabledassistant.services import events as events_svc + from scribe.services import events as events_svc sig = inspect.signature(events_svc.get_event) assert "user_id" in sig.parameters assert "event_id" in sig.parameters @@ -52,7 +52,7 @@ def test_events_service_ownership_enforced_on_get(): def test_events_service_ownership_enforced_on_update(): """update_event takes user_id — route passes current user's id.""" import inspect - from fabledassistant.services import events as events_svc + from scribe.services import events as events_svc sig = inspect.signature(events_svc.update_event) assert "user_id" in sig.parameters assert "event_id" in sig.parameters @@ -61,7 +61,7 @@ def test_events_service_ownership_enforced_on_update(): def test_events_service_ownership_enforced_on_delete(): """delete_event takes user_id — route verifies ownership before deleting.""" import inspect - from fabledassistant.services import events as events_svc + from scribe.services import events as events_svc sig = inspect.signature(events_svc.delete_event) assert "user_id" in sig.parameters assert "event_id" in sig.parameters diff --git a/tests/test_events_service.py b/tests/test_events_service.py index 8649d91..93e08b8 100644 --- a/tests/test_events_service.py +++ b/tests/test_events_service.py @@ -49,10 +49,10 @@ def _make_mock_event(id=1, user_id=1, uid="uid-abc", title="Meeting", @pytest.mark.asyncio async def test_create_event_stores_to_db(): mock_session = _make_mock_session() - with patch("fabledassistant.services.events.async_session") as mock_cls, \ - patch("fabledassistant.services.events.asyncio.create_task") as mock_task: + with patch("scribe.services.events.async_session") as mock_cls, \ + patch("scribe.services.events.asyncio.create_task") as mock_task: mock_cls.return_value = mock_session - from fabledassistant.services.events import create_event + from scribe.services.events import create_event result = await create_event( user_id=1, title="Dentist", @@ -72,9 +72,9 @@ async def test_find_events_by_query_returns_ilike_results(): mock_result.scalars.return_value.all.return_value = [mock_event] mock_session.execute = AsyncMock(return_value=mock_result) - with patch("fabledassistant.services.events.async_session") as mock_cls: + with patch("scribe.services.events.async_session") as mock_cls: mock_cls.return_value = mock_session - from fabledassistant.services.events import find_events_by_query + from scribe.services.events import find_events_by_query results = await find_events_by_query(user_id=1, query="meeting") assert len(results) == 1 assert results[0].title == "Team Meeting" @@ -88,9 +88,9 @@ async def test_list_events_returns_events_in_range(): mock_result.scalars.return_value.all.return_value = [mock_event] mock_session.execute = AsyncMock(return_value=mock_result) - with patch("fabledassistant.services.events.async_session") as mock_cls: + with patch("scribe.services.events.async_session") as mock_cls: mock_cls.return_value = mock_session - from fabledassistant.services.events import list_events + from scribe.services.events import list_events results = await list_events( user_id=1, date_from=datetime(2026, 3, 1, tzinfo=timezone.utc), @@ -107,10 +107,10 @@ async def test_delete_event_fires_caldav_push_when_uid_set(): mock_result.scalar_one_or_none.return_value = mock_event mock_session.execute = AsyncMock(return_value=mock_result) - with patch("fabledassistant.services.events.async_session") as mock_cls, \ - patch("fabledassistant.services.events.asyncio.create_task") as mock_task: + with patch("scribe.services.events.async_session") as mock_cls, \ + patch("scribe.services.events.asyncio.create_task") as mock_task: mock_cls.return_value = mock_session - from fabledassistant.services.events import delete_event + from scribe.services.events import delete_event await delete_event(user_id=1, event_id=1) # Push task fired because caldav_uid is set assert mock_task.called @@ -124,10 +124,10 @@ async def test_update_event_fires_caldav_push(): mock_result.scalar_one_or_none.return_value = mock_event mock_session.execute = AsyncMock(return_value=mock_result) - with patch("fabledassistant.services.events.async_session") as mock_cls, \ - patch("fabledassistant.services.events.asyncio.create_task") as mock_task: + with patch("scribe.services.events.async_session") as mock_cls, \ + patch("scribe.services.events.asyncio.create_task") as mock_task: mock_cls.return_value = mock_session - from fabledassistant.services.events import update_event + from scribe.services.events import update_event await update_event(user_id=1, event_id=1, title="Updated Title") assert mock_task.called @@ -137,7 +137,7 @@ async def test_update_event_fires_caldav_push(): def test_normalize_duration_from_end_dt(): """end_dt sugar converts to a positive minute count anchored on start.""" - from fabledassistant.services.events import _normalize_duration + from scribe.services.events import _normalize_duration start = datetime(2026, 5, 1, 8, 0, tzinfo=timezone.utc) end = datetime(2026, 5, 1, 9, 30, tzinfo=timezone.utc) assert _normalize_duration(start_dt=start, end_dt=end, duration_minutes=None) == 90 @@ -147,7 +147,7 @@ def test_normalize_duration_zero_is_valid_point_event(): """end_dt == start_dt → duration 0. The point-with-zero-duration case is rare but legal (e.g. an instant marker); the duration model treats it the same as duration None for display purposes.""" - from fabledassistant.services.events import _normalize_duration + from scribe.services.events import _normalize_duration same = datetime(2026, 5, 1, 8, 0, tzinfo=timezone.utc) assert _normalize_duration(start_dt=same, end_dt=same, duration_minutes=None) == 0 @@ -158,7 +158,7 @@ def test_normalize_duration_rejects_end_before_start(): via a CHECK constraint, but write-path callers still get a helpful ValueError if they construct an inconsistent (start, end) pair via the end_dt sugar.""" - from fabledassistant.services.events import _normalize_duration + from scribe.services.events import _normalize_duration start = datetime(2026, 5, 1, 12, 0, tzinfo=timezone.utc) end_before = datetime(2026, 3, 30, 12, 0, tzinfo=timezone.utc) with pytest.raises(ValueError, match="at or after start_dt"): @@ -171,7 +171,7 @@ def test_normalize_duration_rejects_negative_duration(): """Direct duration_minutes < 0 is rejected. Mirrors the DB CHECK constraint at the service boundary so callers get a clean error rather than a constraint violation from psycopg.""" - from fabledassistant.services.events import _normalize_duration + from scribe.services.events import _normalize_duration start = datetime(2026, 5, 1, 12, 0, tzinfo=timezone.utc) with pytest.raises(ValueError, match="must be >= 0"): _normalize_duration(start_dt=start, end_dt=None, duration_minutes=-15) @@ -180,7 +180,7 @@ def test_normalize_duration_rejects_negative_duration(): def test_normalize_duration_rejects_inconsistent_end_and_duration(): """If a caller passes both end_dt AND duration_minutes that disagree, the inconsistency is surfaced rather than silently picking one.""" - from fabledassistant.services.events import _normalize_duration + from scribe.services.events import _normalize_duration start = datetime(2026, 5, 1, 12, 0, tzinfo=timezone.utc) end = datetime(2026, 5, 1, 13, 0, tzinfo=timezone.utc) # implies 60 min with pytest.raises(ValueError, match="implies 60 minutes"): @@ -191,7 +191,7 @@ def test_normalize_duration_rejects_inconsistent_end_and_duration(): def test_normalize_duration_none_for_open_ended(): """Both inputs None → None duration (open-ended event).""" - from fabledassistant.services.events import _normalize_duration + from scribe.services.events import _normalize_duration start = datetime(2026, 5, 1, 12, 0, tzinfo=timezone.utc) assert _normalize_duration( start_dt=start, end_dt=None, duration_minutes=None, @@ -202,7 +202,7 @@ def test_normalize_duration_none_for_open_ended(): async def test_create_event_rejects_end_before_start(): """Service-level rejection — same scenario as the prod bug, surfaced cleanly for tool / route callers via ValueError.""" - from fabledassistant.services.events import create_event + from scribe.services.events import create_event start = datetime(2026, 5, 1, 12, 0, tzinfo=timezone.utc) end_before = datetime(2026, 3, 30, 12, 0, tzinfo=timezone.utc) with pytest.raises(ValueError, match="at or after start_dt"): @@ -225,10 +225,10 @@ async def test_update_event_preserves_duration_when_only_start_changes(): mock_result.scalar_one_or_none.return_value = mock_event mock_session.execute = AsyncMock(return_value=mock_result) - with patch("fabledassistant.services.events.async_session") as mock_cls, \ - patch("fabledassistant.services.events.asyncio.create_task"): + with patch("scribe.services.events.async_session") as mock_cls, \ + patch("scribe.services.events.asyncio.create_task"): mock_cls.return_value = mock_session - from fabledassistant.services.events import update_event + from scribe.services.events import update_event # Move start to 12:00; effective end becomes 13:00 automatically. result = await update_event( user_id=1, event_id=1, @@ -251,10 +251,10 @@ async def test_update_event_clearing_end_dt_clears_duration(): mock_result.scalar_one_or_none.return_value = mock_event mock_session.execute = AsyncMock(return_value=mock_result) - with patch("fabledassistant.services.events.async_session") as mock_cls, \ - patch("fabledassistant.services.events.asyncio.create_task"): + with patch("scribe.services.events.async_session") as mock_cls, \ + patch("scribe.services.events.asyncio.create_task"): mock_cls.return_value = mock_session - from fabledassistant.services.events import update_event + from scribe.services.events import update_event await update_event(user_id=1, event_id=1, end_dt=None) assert mock_event.duration_minutes is None @@ -280,9 +280,9 @@ async def test_list_events_includes_point_event_in_window(): mock_result.scalars.return_value.all.return_value = [mock_event] mock_session.execute = AsyncMock(return_value=mock_result) - with patch("fabledassistant.services.events.async_session") as mock_cls: + with patch("scribe.services.events.async_session") as mock_cls: mock_cls.return_value = mock_session - from fabledassistant.services.events import list_events + from scribe.services.events import list_events results = await list_events( user_id=1, date_from=datetime(2026, 4, 29, tzinfo=timezone.utc), @@ -310,9 +310,9 @@ async def test_list_events_excludes_timed_event_that_already_ended(): mock_result.scalars.return_value.all.return_value = [mock_event] mock_session.execute = AsyncMock(return_value=mock_result) - with patch("fabledassistant.services.events.async_session") as mock_cls: + with patch("scribe.services.events.async_session") as mock_cls: mock_cls.return_value = mock_session - from fabledassistant.services.events import list_events + from scribe.services.events import list_events results = await list_events( user_id=1, date_from=datetime(2026, 4, 29, tzinfo=timezone.utc), diff --git a/tests/test_mcp_auth.py b/tests/test_mcp_auth.py index 2f75b7c..3a8203d 100644 --- a/tests/test_mcp_auth.py +++ b/tests/test_mcp_auth.py @@ -3,7 +3,7 @@ from unittest.mock import AsyncMock, MagicMock, patch import pytest -from fabledassistant.mcp.auth import resolve_bearer, resolve_bearer_to_user_id +from scribe.mcp.auth import resolve_bearer, resolve_bearer_to_user_id @pytest.mark.asyncio @@ -22,7 +22,7 @@ async def test_resolve_bearer_malformed_header_returns_none(): @pytest.mark.asyncio async def test_resolve_bearer_unknown_token_returns_none(): with patch( - "fabledassistant.mcp.auth.lookup_key", + "scribe.mcp.auth.lookup_key", AsyncMock(return_value=None), ): assert await resolve_bearer_to_user_id("Bearer fmcp_doesnotexist") is None @@ -33,7 +33,7 @@ async def test_resolve_bearer_valid_token_returns_user_id(): fake_key = MagicMock() fake_key.user_id = 42 with patch( - "fabledassistant.mcp.auth.lookup_key", + "scribe.mcp.auth.lookup_key", AsyncMock(return_value=fake_key), ): uid = await resolve_bearer_to_user_id("Bearer fmcp_validkey") @@ -46,7 +46,7 @@ async def test_resolve_bearer_calls_lookup_with_stripped_token(): fake_key = MagicMock() fake_key.user_id = 1 mock_lookup = AsyncMock(return_value=fake_key) - with patch("fabledassistant.mcp.auth.lookup_key", mock_lookup): + with patch("scribe.mcp.auth.lookup_key", mock_lookup): await resolve_bearer_to_user_id("Bearer fmcp_abc123 ") mock_lookup.assert_awaited_once_with("fmcp_abc123") @@ -58,13 +58,13 @@ async def test_resolve_bearer_returns_user_id_and_scope(): fake_key = MagicMock() fake_key.user_id = 9 fake_key.scope = "read" - with patch("fabledassistant.mcp.auth.lookup_key", AsyncMock(return_value=fake_key)): + with patch("scribe.mcp.auth.lookup_key", AsyncMock(return_value=fake_key)): assert await resolve_bearer("Bearer fmcp_x") == (9, "read") @pytest.mark.asyncio async def test_resolve_bearer_none_for_invalid(): - with patch("fabledassistant.mcp.auth.lookup_key", AsyncMock(return_value=None)): + with patch("scribe.mcp.auth.lookup_key", AsyncMock(return_value=None)): assert await resolve_bearer("Bearer nope") is None assert await resolve_bearer(None) is None @@ -73,7 +73,7 @@ async def test_resolve_bearer_none_for_invalid(): def test_body_calls_write_tool_classifies_correctly(): import json - from fabledassistant.mcp.server import _body_calls_write_tool + from scribe.mcp.server import _body_calls_write_tool def call(name): return json.dumps({"jsonrpc": "2.0", "id": 1, "method": "tools/call", diff --git a/tests/test_mcp_context.py b/tests/test_mcp_context.py index 2a73903..e614b84 100644 --- a/tests/test_mcp_context.py +++ b/tests/test_mcp_context.py @@ -1,7 +1,7 @@ """Tests for MCP per-request user_id context.""" import pytest -from fabledassistant.mcp._context import current_user_id, _user_id_ctx +from scribe.mcp._context import current_user_id, _user_id_ctx def test_current_user_id_raises_when_unset(): diff --git a/tests/test_mcp_endpoint.py b/tests/test_mcp_endpoint.py index db8afeb..42b9293 100644 --- a/tests/test_mcp_endpoint.py +++ b/tests/test_mcp_endpoint.py @@ -15,7 +15,7 @@ from unittest.mock import AsyncMock, MagicMock, patch import pytest -from fabledassistant.app import create_app +from scribe.app import create_app async def _send_request( @@ -73,7 +73,7 @@ async def test_mcp_endpoint_unauthenticated_returns_401(): async def test_mcp_endpoint_invalid_token_returns_401(): app = create_app() with patch( - "fabledassistant.mcp.auth.lookup_key", + "scribe.mcp.auth.lookup_key", AsyncMock(return_value=None), ): status, _ = await _send_request( @@ -107,7 +107,7 @@ async def test_mcp_endpoint_valid_token_passes_auth(): }).encode() async with app.mcp_instance.session_manager.run(): with patch( - "fabledassistant.mcp.auth.lookup_key", + "scribe.mcp.auth.lookup_key", AsyncMock(return_value=fake_key), ): status, _ = await _send_request( diff --git a/tests/test_mcp_tool_entities.py b/tests/test_mcp_tool_entities.py index 85fdf08..bd75ad1 100644 --- a/tests/test_mcp_tool_entities.py +++ b/tests/test_mcp_tool_entities.py @@ -6,8 +6,8 @@ from unittest.mock import AsyncMock, MagicMock, patch import pytest -from fabledassistant.mcp._context import _user_id_ctx -from fabledassistant.mcp.tools.entities import ( +from scribe.mcp._context import _user_id_ctx +from scribe.mcp.tools.entities import ( list_persons, create_person, update_person, create_place, update_place, create_list, update_list, @@ -38,7 +38,7 @@ def _fake_note(*, note_type="note", entity_meta=None, **overrides) -> MagicMock: @pytest.mark.asyncio async def test_list_persons_calls_knowledge_with_person_type(): mock = AsyncMock(return_value=([{"id": 1, "title": "alice"}], 1)) - with patch("fabledassistant.mcp.tools.entities.knowledge_svc.query_knowledge", mock): + with patch("scribe.mcp.tools.entities.knowledge_svc.query_knowledge", mock): out = await list_persons(tag="work") kwargs = mock.call_args.kwargs assert kwargs["note_type"] == "person" @@ -55,7 +55,7 @@ async def test_create_person_only_includes_provided_fields_in_meta(): """Empty-string fields must NOT pollute entity_meta.""" fake = _fake_note(note_type="person") mock = AsyncMock(return_value=fake) - with patch("fabledassistant.mcp.tools.entities.notes_svc.create_note", mock): + with patch("scribe.mcp.tools.entities.notes_svc.create_note", mock): await create_person(name="Alice", email="a@x.com") kwargs = mock.call_args.kwargs assert kwargs["title"] == "Alice" @@ -68,7 +68,7 @@ async def test_create_person_all_empty_meta_is_none(): """Service is called with entity_meta=None when no typed fields were given.""" fake = _fake_note(note_type="person") mock = AsyncMock(return_value=fake) - with patch("fabledassistant.mcp.tools.entities.notes_svc.create_note", mock): + with patch("scribe.mcp.tools.entities.notes_svc.create_note", mock): await create_person(name="Bob") assert mock.call_args.kwargs["entity_meta"] is None @@ -77,7 +77,7 @@ async def test_create_person_all_empty_meta_is_none(): async def test_create_place_categories_into_meta(): fake = _fake_note(note_type="place") mock = AsyncMock(return_value=fake) - with patch("fabledassistant.mcp.tools.entities.notes_svc.create_note", mock): + with patch("scribe.mcp.tools.entities.notes_svc.create_note", mock): await create_place(name="Cafe X", address="123 Main", category="coffee") meta = mock.call_args.kwargs["entity_meta"] assert meta == {"address": "123 Main", "category": "coffee"} @@ -87,7 +87,7 @@ async def test_create_place_categories_into_meta(): async def test_create_list_translates_strings_to_unchecked_items(): fake = _fake_note(note_type="list") mock = AsyncMock(return_value=fake) - with patch("fabledassistant.mcp.tools.entities.notes_svc.create_note", mock): + with patch("scribe.mcp.tools.entities.notes_svc.create_note", mock): await create_list(name="shopping", items=["milk", "bread"]) meta = mock.call_args.kwargs["entity_meta"] assert meta["list_items"] == [ @@ -110,9 +110,9 @@ async def test_update_person_merges_meta_preserving_other_fields(): updated = _fake_note(note_type="person") update_mock = AsyncMock(return_value=updated) with patch( - "fabledassistant.mcp.tools.entities.notes_svc.get_note", get_mock, + "scribe.mcp.tools.entities.notes_svc.get_note", get_mock, ), patch( - "fabledassistant.mcp.tools.entities.notes_svc.update_note", update_mock, + "scribe.mcp.tools.entities.notes_svc.update_note", update_mock, ): await update_person(person_id=5, email="new@x.com") new_meta = update_mock.call_args.kwargs["entity_meta"] @@ -127,10 +127,10 @@ async def test_update_person_no_typed_fields_keeps_meta_unchanged(): ) updated = _fake_note(note_type="person") with patch( - "fabledassistant.mcp.tools.entities.notes_svc.get_note", + "scribe.mcp.tools.entities.notes_svc.get_note", AsyncMock(return_value=existing), ), patch( - "fabledassistant.mcp.tools.entities.notes_svc.update_note", + "scribe.mcp.tools.entities.notes_svc.update_note", AsyncMock(return_value=updated), ) as update_mock: await update_person(person_id=5, name="New Name") @@ -144,7 +144,7 @@ async def test_update_person_rejects_wrong_type(): """Trying to update a 'place' as a 'person' must fail.""" wrong_type = _fake_note(id=5, note_type="place") with patch( - "fabledassistant.mcp.tools.entities.notes_svc.get_note", + "scribe.mcp.tools.entities.notes_svc.get_note", AsyncMock(return_value=wrong_type), ): with pytest.raises(ValueError, match="person 5 not found"): @@ -160,10 +160,10 @@ async def test_update_list_items_empty_list_clears_items(): ) updated = _fake_note(note_type="list") with patch( - "fabledassistant.mcp.tools.entities.notes_svc.get_note", + "scribe.mcp.tools.entities.notes_svc.get_note", AsyncMock(return_value=existing), ), patch( - "fabledassistant.mcp.tools.entities.notes_svc.update_note", + "scribe.mcp.tools.entities.notes_svc.update_note", AsyncMock(return_value=updated), ) as update_mock: await update_list(list_id=5, items=[]) @@ -178,10 +178,10 @@ async def test_update_list_items_none_leaves_items_unchanged(): ) updated = _fake_note(note_type="list") with patch( - "fabledassistant.mcp.tools.entities.notes_svc.get_note", + "scribe.mcp.tools.entities.notes_svc.get_note", AsyncMock(return_value=existing), ), patch( - "fabledassistant.mcp.tools.entities.notes_svc.update_note", + "scribe.mcp.tools.entities.notes_svc.update_note", AsyncMock(return_value=updated), ) as update_mock: await update_list(list_id=5, name="renamed") diff --git a/tests/test_mcp_tool_events.py b/tests/test_mcp_tool_events.py index c980d8b..6a2c9d0 100644 --- a/tests/test_mcp_tool_events.py +++ b/tests/test_mcp_tool_events.py @@ -4,8 +4,8 @@ from unittest.mock import AsyncMock, MagicMock, patch import pytest -from fabledassistant.mcp._context import _user_id_ctx -from fabledassistant.mcp.tools.events import ( +from scribe.mcp._context import _user_id_ctx +from scribe.mcp.tools.events import ( list_events, create_event, get_event, update_event, delete_event, ) @@ -38,7 +38,7 @@ async def test_list_events_passes_timezone_aware_range(): mock = AsyncMock(return_value=[ {"id": 1, "title": "morning standup"}, ]) - with patch("fabledassistant.mcp.tools.events.events_svc.list_events", mock): + with patch("scribe.mcp.tools.events.events_svc.list_events", mock): out = await list_events(date_from="2026-06-01", date_to="2026-06-08") args, _ = mock.call_args assert args[0] == 7 # user_id @@ -52,7 +52,7 @@ async def test_list_events_passes_timezone_aware_range(): async def test_create_event_combines_date_and_time(): e = _fake_event() mock = AsyncMock(return_value=e) - with patch("fabledassistant.mcp.tools.events.events_svc.create_event", mock): + with patch("scribe.mcp.tools.events.events_svc.create_event", mock): await create_event( title="standup", start_date="2026-06-01", start_time="09:30", duration_minutes=15, @@ -67,7 +67,7 @@ async def test_create_event_zero_duration_means_point_event(): """duration_minutes=0 must map to None at the service layer (NULL = point).""" e = _fake_event() mock = AsyncMock(return_value=e) - with patch("fabledassistant.mcp.tools.events.events_svc.create_event", mock): + with patch("scribe.mcp.tools.events.events_svc.create_event", mock): await create_event(title="x", start_date="2026-06-01") assert mock.call_args.kwargs["duration_minutes"] is None @@ -75,7 +75,7 @@ async def test_create_event_zero_duration_means_point_event(): @pytest.mark.asyncio async def test_get_event_raises_when_not_found(): with patch( - "fabledassistant.mcp.tools.events.events_svc.get_event", + "scribe.mcp.tools.events.events_svc.get_event", AsyncMock(return_value=None), ): with pytest.raises(ValueError, match="event 999 not found"): @@ -86,7 +86,7 @@ async def test_get_event_raises_when_not_found(): async def test_update_event_only_sends_non_default_fields(): e = _fake_event() mock = AsyncMock(return_value=e) - with patch("fabledassistant.mcp.tools.events.events_svc.update_event", mock): + with patch("scribe.mcp.tools.events.events_svc.update_event", mock): await update_event(event_id=1, title="new title") args, kwargs = mock.call_args assert args == (7, 1) @@ -97,7 +97,7 @@ async def test_update_event_only_sends_non_default_fields(): async def test_update_event_duration_minus_one_means_unchanged(): e = _fake_event() mock = AsyncMock(return_value=e) - with patch("fabledassistant.mcp.tools.events.events_svc.update_event", mock): + with patch("scribe.mcp.tools.events.events_svc.update_event", mock): await update_event(event_id=1, duration_minutes=-1) assert "duration_minutes" not in mock.call_args.kwargs @@ -107,7 +107,7 @@ async def test_update_event_duration_zero_clears_to_point(): """duration_minutes=0 means "set to point event" (NULL).""" e = _fake_event() mock = AsyncMock(return_value=e) - with patch("fabledassistant.mcp.tools.events.events_svc.update_event", mock): + with patch("scribe.mcp.tools.events.events_svc.update_event", mock): await update_event(event_id=1, duration_minutes=0) assert mock.call_args.kwargs["duration_minutes"] is None @@ -116,7 +116,7 @@ async def test_update_event_duration_zero_clears_to_point(): async def test_update_event_requires_both_date_and_time_to_move(): e = _fake_event() mock = AsyncMock(return_value=e) - with patch("fabledassistant.mcp.tools.events.events_svc.update_event", mock): + with patch("scribe.mcp.tools.events.events_svc.update_event", mock): await update_event(event_id=1, start_date="2026-06-02") # Only start_date, no start_time → start_dt NOT in fields assert "start_dt" not in mock.call_args.kwargs @@ -131,7 +131,7 @@ async def test_update_event_requires_both_date_and_time_to_move(): @pytest.mark.asyncio async def test_update_event_raises_when_not_found(): with patch( - "fabledassistant.mcp.tools.events.events_svc.update_event", + "scribe.mcp.tools.events.events_svc.update_event", AsyncMock(return_value=None), ): with pytest.raises(ValueError, match="event 999 not found"): @@ -141,7 +141,7 @@ async def test_update_event_raises_when_not_found(): @pytest.mark.asyncio async def test_delete_event_soft_deletes_and_returns_batch(): with patch( - "fabledassistant.mcp.tools.events.trash_svc.delete", + "scribe.mcp.tools.events.trash_svc.delete", AsyncMock(return_value="batch-1"), ): result = await delete_event(event_id=7) @@ -151,7 +151,7 @@ async def test_delete_event_soft_deletes_and_returns_batch(): @pytest.mark.asyncio async def test_delete_event_raises_when_not_found(): with patch( - "fabledassistant.mcp.tools.events.trash_svc.delete", + "scribe.mcp.tools.events.trash_svc.delete", AsyncMock(return_value=None), ): with pytest.raises(ValueError, match="event 999 not found"): diff --git a/tests/test_mcp_tool_milestones.py b/tests/test_mcp_tool_milestones.py index 581a81d..b63bb3f 100644 --- a/tests/test_mcp_tool_milestones.py +++ b/tests/test_mcp_tool_milestones.py @@ -3,8 +3,8 @@ from unittest.mock import AsyncMock, MagicMock, patch import pytest -from fabledassistant.mcp._context import _user_id_ctx -from fabledassistant.mcp.tools.milestones import ( +from scribe.mcp._context import _user_id_ctx +from scribe.mcp.tools.milestones import ( list_milestones, create_milestone, update_milestone, ) @@ -29,7 +29,7 @@ def _fake_ms(**overrides) -> MagicMock: async def test_list_milestones_returns_dict_with_progress(): rows = [{"id": 1, "title": "MS1", "status": "active", "task_count": 2}] with patch( - "fabledassistant.mcp.tools.milestones.milestones_svc.get_project_milestone_summary", + "scribe.mcp.tools.milestones.milestones_svc.get_project_milestone_summary", AsyncMock(return_value=rows), ): out = await list_milestones(project_id=1) @@ -40,7 +40,7 @@ async def test_list_milestones_returns_dict_with_progress(): async def test_create_milestone_passes_through(): m = _fake_ms(id=5) mock = AsyncMock(return_value=m) - with patch("fabledassistant.mcp.tools.milestones.milestones_svc.create_milestone", mock): + with patch("scribe.mcp.tools.milestones.milestones_svc.create_milestone", mock): out = await create_milestone(project_id=1, title="new", description="d") assert out["id"] == 5 assert mock.call_args.kwargs["project_id"] == 1 @@ -52,7 +52,7 @@ async def test_create_milestone_passes_through(): async def test_create_milestone_empty_description_becomes_none(): m = _fake_ms() mock = AsyncMock(return_value=m) - with patch("fabledassistant.mcp.tools.milestones.milestones_svc.create_milestone", mock): + with patch("scribe.mcp.tools.milestones.milestones_svc.create_milestone", mock): await create_milestone(project_id=1, title="t", description="") assert mock.call_args.kwargs["description"] is None @@ -61,7 +61,7 @@ async def test_create_milestone_empty_description_becomes_none(): async def test_update_milestone_only_sends_non_default_fields(): m = _fake_ms() mock = AsyncMock(return_value=m) - with patch("fabledassistant.mcp.tools.milestones.milestones_svc.update_milestone", mock): + with patch("scribe.mcp.tools.milestones.milestones_svc.update_milestone", mock): await update_milestone(project_id=1, milestone_id=5, status="done") args, kwargs = mock.call_args assert args == (7, 5) @@ -73,7 +73,7 @@ async def test_update_milestone_order_index_negative_is_omitted(): """order_index=-1 sentinel means leave unchanged.""" m = _fake_ms() mock = AsyncMock(return_value=m) - with patch("fabledassistant.mcp.tools.milestones.milestones_svc.update_milestone", mock): + with patch("scribe.mcp.tools.milestones.milestones_svc.update_milestone", mock): await update_milestone(project_id=1, milestone_id=5, order_index=-1) assert "order_index" not in mock.call_args.kwargs @@ -83,7 +83,7 @@ async def test_update_milestone_order_index_zero_is_explicit(): """order_index=0 is a real value (top of list), not a sentinel.""" m = _fake_ms() mock = AsyncMock(return_value=m) - with patch("fabledassistant.mcp.tools.milestones.milestones_svc.update_milestone", mock): + with patch("scribe.mcp.tools.milestones.milestones_svc.update_milestone", mock): await update_milestone(project_id=1, milestone_id=5, order_index=0) assert mock.call_args.kwargs["order_index"] == 0 @@ -91,7 +91,7 @@ async def test_update_milestone_order_index_zero_is_explicit(): @pytest.mark.asyncio async def test_update_milestone_raises_when_not_found(): with patch( - "fabledassistant.mcp.tools.milestones.milestones_svc.update_milestone", + "scribe.mcp.tools.milestones.milestones_svc.update_milestone", AsyncMock(return_value=None), ): with pytest.raises(ValueError, match="milestone 999 not found"): diff --git a/tests/test_mcp_tool_notes.py b/tests/test_mcp_tool_notes.py index 0b80543..078cd2d 100644 --- a/tests/test_mcp_tool_notes.py +++ b/tests/test_mcp_tool_notes.py @@ -3,8 +3,8 @@ from unittest.mock import AsyncMock, MagicMock, patch import pytest -from fabledassistant.mcp._context import _user_id_ctx -from fabledassistant.mcp.tools.notes import ( +from scribe.mcp._context import _user_id_ctx +from scribe.mcp.tools.notes import ( list_notes, get_note, create_note, update_note, delete_note, ) @@ -29,7 +29,7 @@ def _fake_note(**overrides) -> MagicMock: async def test_list_notes_repackages_tuple_into_dict(): rows = [_fake_note(id=1), _fake_note(id=2)] with patch( - "fabledassistant.mcp.tools.notes.notes_svc.list_notes", + "scribe.mcp.tools.notes.notes_svc.list_notes", AsyncMock(return_value=(rows, 2)), ): out = await list_notes() @@ -40,7 +40,7 @@ async def test_list_notes_repackages_tuple_into_dict(): @pytest.mark.asyncio async def test_list_notes_passes_is_task_false(): mock = AsyncMock(return_value=([], 0)) - with patch("fabledassistant.mcp.tools.notes.notes_svc.list_notes", mock): + with patch("scribe.mcp.tools.notes.notes_svc.list_notes", mock): await list_notes() assert mock.call_args.kwargs["is_task"] is False @@ -49,7 +49,7 @@ async def test_list_notes_passes_is_task_false(): async def test_list_notes_tag_filter_maps_to_list(): """The single-tag string param maps to a one-element list at the service layer.""" mock = AsyncMock(return_value=([], 0)) - with patch("fabledassistant.mcp.tools.notes.notes_svc.list_notes", mock): + with patch("scribe.mcp.tools.notes.notes_svc.list_notes", mock): await list_notes(tag="ops") assert mock.call_args.kwargs["tags"] == ["ops"] @@ -57,7 +57,7 @@ async def test_list_notes_tag_filter_maps_to_list(): @pytest.mark.asyncio async def test_list_notes_empty_tag_means_no_filter(): mock = AsyncMock(return_value=([], 0)) - with patch("fabledassistant.mcp.tools.notes.notes_svc.list_notes", mock): + with patch("scribe.mcp.tools.notes.notes_svc.list_notes", mock): await list_notes(tag="") assert mock.call_args.kwargs["tags"] is None @@ -65,7 +65,7 @@ async def test_list_notes_empty_tag_means_no_filter(): @pytest.mark.asyncio async def test_list_notes_search_text_maps_to_q(): mock = AsyncMock(return_value=([], 0)) - with patch("fabledassistant.mcp.tools.notes.notes_svc.list_notes", mock): + with patch("scribe.mcp.tools.notes.notes_svc.list_notes", mock): await list_notes(search_text="kafka") assert mock.call_args.kwargs["q"] == "kafka" @@ -73,7 +73,7 @@ async def test_list_notes_search_text_maps_to_q(): @pytest.mark.asyncio async def test_list_notes_limit_clamped(): mock = AsyncMock(return_value=([], 0)) - with patch("fabledassistant.mcp.tools.notes.notes_svc.list_notes", mock): + with patch("scribe.mcp.tools.notes.notes_svc.list_notes", mock): await list_notes(limit=9999) assert mock.call_args.kwargs["limit"] == 100 @@ -82,7 +82,7 @@ async def test_list_notes_limit_clamped(): async def test_get_note_returns_dict(): fake = _fake_note(id=5, title="found") with patch( - "fabledassistant.mcp.tools.notes.notes_svc.get_note", + "scribe.mcp.tools.notes.notes_svc.get_note", AsyncMock(return_value=fake), ): out = await get_note(note_id=5) @@ -93,7 +93,7 @@ async def test_get_note_returns_dict(): @pytest.mark.asyncio async def test_get_note_raises_when_not_found(): with patch( - "fabledassistant.mcp.tools.notes.notes_svc.get_note", + "scribe.mcp.tools.notes.notes_svc.get_note", AsyncMock(return_value=None), ): with pytest.raises(ValueError, match="note 999 not found"): @@ -104,7 +104,7 @@ async def test_get_note_raises_when_not_found(): async def test_create_note_passes_through(): fake = _fake_note(id=10, title="new") mock = AsyncMock(return_value=fake) - with patch("fabledassistant.mcp.tools.notes.notes_svc.create_note", mock): + with patch("scribe.mcp.tools.notes.notes_svc.create_note", mock): out = await create_note(title="new", body="x", tags=["a"], project_id=5) assert out["id"] == 10 assert mock.call_args.kwargs["title"] == "new" @@ -116,7 +116,7 @@ async def test_create_note_project_zero_becomes_none(): """project_id=0 sentinel must become None at the service layer (orphan note).""" fake = _fake_note() mock = AsyncMock(return_value=fake) - with patch("fabledassistant.mcp.tools.notes.notes_svc.create_note", mock): + with patch("scribe.mcp.tools.notes.notes_svc.create_note", mock): await create_note(title="t", project_id=0) assert mock.call_args.kwargs["project_id"] is None @@ -127,7 +127,7 @@ async def test_update_note_only_sends_non_default_fields(): overwrite real data with empty strings.""" fake = _fake_note() mock = AsyncMock(return_value=fake) - with patch("fabledassistant.mcp.tools.notes.notes_svc.update_note", mock): + with patch("scribe.mcp.tools.notes.notes_svc.update_note", mock): await update_note(note_id=1, title="new title") # Service got user_id, note_id positional + only the title kwarg args, kwargs = mock.call_args @@ -140,7 +140,7 @@ async def test_update_note_empty_tags_clears_explicitly(): """tags=[] is an explicit clear, distinct from tags=None (omit).""" fake = _fake_note() mock = AsyncMock(return_value=fake) - with patch("fabledassistant.mcp.tools.notes.notes_svc.update_note", mock): + with patch("scribe.mcp.tools.notes.notes_svc.update_note", mock): await update_note(note_id=1, tags=[]) assert mock.call_args.kwargs == {"tags": []} @@ -149,7 +149,7 @@ async def test_update_note_empty_tags_clears_explicitly(): async def test_update_note_tags_none_means_omit(): fake = _fake_note() mock = AsyncMock(return_value=fake) - with patch("fabledassistant.mcp.tools.notes.notes_svc.update_note", mock): + with patch("scribe.mcp.tools.notes.notes_svc.update_note", mock): await update_note(note_id=1, tags=None) assert "tags" not in mock.call_args.kwargs @@ -157,7 +157,7 @@ async def test_update_note_tags_none_means_omit(): @pytest.mark.asyncio async def test_update_note_raises_when_not_found(): with patch( - "fabledassistant.mcp.tools.notes.notes_svc.update_note", + "scribe.mcp.tools.notes.notes_svc.update_note", AsyncMock(return_value=None), ): with pytest.raises(ValueError, match="note 999 not found"): @@ -167,7 +167,7 @@ async def test_update_note_raises_when_not_found(): @pytest.mark.asyncio async def test_delete_note_soft_deletes_and_returns_batch(): with patch( - "fabledassistant.mcp.tools.notes.trash_svc.delete", + "scribe.mcp.tools.notes.trash_svc.delete", AsyncMock(return_value="batch-1"), ): result = await delete_note(note_id=7) @@ -177,7 +177,7 @@ async def test_delete_note_soft_deletes_and_returns_batch(): @pytest.mark.asyncio async def test_delete_note_raises_when_not_found(): with patch( - "fabledassistant.mcp.tools.notes.trash_svc.delete", + "scribe.mcp.tools.notes.trash_svc.delete", AsyncMock(return_value=None), ): with pytest.raises(ValueError, match="note 999 not found"): diff --git a/tests/test_mcp_tool_planning.py b/tests/test_mcp_tool_planning.py index ef003df..7d6134f 100644 --- a/tests/test_mcp_tool_planning.py +++ b/tests/test_mcp_tool_planning.py @@ -2,7 +2,7 @@ from unittest.mock import AsyncMock, MagicMock, patch import pytest -from fabledassistant.mcp._context import _user_id_ctx +from scribe.mcp._context import _user_id_ctx @pytest.fixture(autouse=True) @@ -16,9 +16,9 @@ def _bind_user(): async def test_start_planning_tool_delegates_to_service(): payload = {"task": {"id": 5}, "applicable_rules": [], "subscribed_rulebooks": [], "applicable_rules_truncated": False, "project_goal": "", "open_task_count": 0} - with patch("fabledassistant.mcp.tools.tasks.planning_svc.start_planning", + with patch("scribe.mcp.tools.tasks.planning_svc.start_planning", AsyncMock(return_value=payload)) as mock: - from fabledassistant.mcp.tools.tasks import start_planning + from scribe.mcp.tools.tasks import start_planning out = await start_planning(project_id=3, title="Plan it") assert out["task"]["id"] == 5 assert mock.call_args.kwargs == {"user_id": 7, "project_id": 3, "title": "Plan it"} @@ -32,11 +32,11 @@ async def test_get_task_augments_plan_with_rules(): note.to_dict.return_value = {"id": 9, "task_kind": "plan", "project_id": 3} applicable = {"rules": [{"id": 1, "title": "r"}], "truncated": False, "subscribed_rulebooks": [{"id": 2, "title": "rb"}]} - with patch("fabledassistant.mcp.tools.tasks.notes_svc.get_note", + with patch("scribe.mcp.tools.tasks.notes_svc.get_note", AsyncMock(return_value=note)), \ - patch("fabledassistant.mcp.tools.tasks.rulebooks_svc.get_applicable_rules", + patch("scribe.mcp.tools.tasks.rulebooks_svc.get_applicable_rules", AsyncMock(return_value=applicable)): - from fabledassistant.mcp.tools.tasks import get_task + from scribe.mcp.tools.tasks import get_task out = await get_task(task_id=9) assert out["applicable_rules"] == [{"id": 1, "title": "r"}] assert out["subscribed_rulebooks"] == [{"id": 2, "title": "rb"}] @@ -49,11 +49,11 @@ async def test_get_task_work_kind_has_no_rules(): note.parent_id = None note.project_id = 3 note.to_dict.return_value = {"id": 9, "task_kind": "work", "project_id": 3} - with patch("fabledassistant.mcp.tools.tasks.notes_svc.get_note", + with patch("scribe.mcp.tools.tasks.notes_svc.get_note", AsyncMock(return_value=note)), \ - patch("fabledassistant.mcp.tools.tasks.rulebooks_svc.get_applicable_rules", + patch("scribe.mcp.tools.tasks.rulebooks_svc.get_applicable_rules", AsyncMock()) as mock_rules: - from fabledassistant.mcp.tools.tasks import get_task + from scribe.mcp.tools.tasks import get_task out = await get_task(task_id=9) assert "applicable_rules" not in out assert not mock_rules.called diff --git a/tests/test_mcp_tool_processes.py b/tests/test_mcp_tool_processes.py index 3b6a784..9bb5a68 100644 --- a/tests/test_mcp_tool_processes.py +++ b/tests/test_mcp_tool_processes.py @@ -3,7 +3,7 @@ from unittest.mock import AsyncMock, MagicMock, patch import pytest -from fabledassistant.mcp._context import _user_id_ctx +from scribe.mcp._context import _user_id_ctx @pytest.fixture(autouse=True) @@ -24,7 +24,7 @@ def _fake_note(id=1, title="Drift Audit", note_type="process"): @pytest.mark.asyncio async def test_create_process_requires_title_and_body(): - from fabledassistant.mcp.tools.processes import create_process + from scribe.mcp.tools.processes import create_process with pytest.raises(ValueError): await create_process(title="", body="something") with pytest.raises(ValueError): @@ -34,9 +34,9 @@ async def test_create_process_requires_title_and_body(): @pytest.mark.asyncio async def test_create_process_sets_note_type(): created = _fake_note() - with patch("fabledassistant.services.notes.create_note", + with patch("scribe.services.notes.create_note", AsyncMock(return_value=created)) as mock_create: - from fabledassistant.mcp.tools.processes import create_process + from scribe.mcp.tools.processes import create_process out = await create_process(title="Drift Audit", body="the prompt", tags=["audit"]) assert out["note_type"] == "process" # the service was asked to create a process @@ -47,9 +47,9 @@ async def test_create_process_sets_note_type(): @pytest.mark.asyncio async def test_get_process_returns_body_and_candidates(): note = _fake_note(id=7) - with patch("fabledassistant.services.notes.resolve_process", + with patch("scribe.services.notes.resolve_process", AsyncMock(return_value=(note, [{"id": 9, "title": "Drift Audit Notes"}]))): - from fabledassistant.mcp.tools.processes import get_process + from scribe.mcp.tools.processes import get_process out = await get_process("drift") assert out["id"] == 7 assert out["other_matches"] == [{"id": 9, "title": "Drift Audit Notes"}] @@ -57,9 +57,9 @@ async def test_get_process_returns_body_and_candidates(): @pytest.mark.asyncio async def test_get_process_not_found_raises(): - with patch("fabledassistant.services.notes.resolve_process", + with patch("scribe.services.notes.resolve_process", AsyncMock(return_value=(None, []))): - from fabledassistant.mcp.tools.processes import get_process + from scribe.mcp.tools.processes import get_process with pytest.raises(ValueError): await get_process("missing") @@ -67,15 +67,15 @@ async def test_get_process_not_found_raises(): @pytest.mark.asyncio async def test_update_process_rejects_non_process_note(): plain = _fake_note(id=3, note_type="note") - with patch("fabledassistant.services.notes.get_note", + with patch("scribe.services.notes.get_note", AsyncMock(return_value=plain)): - from fabledassistant.mcp.tools.processes import update_process + from scribe.mcp.tools.processes import update_process with pytest.raises(ValueError): await update_process(process_id=3, title="x") def test_register_attaches_four_tools(): - from fabledassistant.mcp.tools import processes + from scribe.mcp.tools import processes names: list[str] = [] class FakeMcp: diff --git a/tests/test_mcp_tool_projects.py b/tests/test_mcp_tool_projects.py index cb84619..9ef87b9 100644 --- a/tests/test_mcp_tool_projects.py +++ b/tests/test_mcp_tool_projects.py @@ -3,8 +3,8 @@ from unittest.mock import AsyncMock, MagicMock, patch import pytest -from fabledassistant.mcp._context import _user_id_ctx -from fabledassistant.mcp.tools.projects import ( +from scribe.mcp._context import _user_id_ctx +from scribe.mcp.tools.projects import ( list_projects, get_project, create_project, update_project, enter_project, ) @@ -30,7 +30,7 @@ def _fake_project(**overrides) -> MagicMock: async def test_list_projects_wraps_in_dict(): rows = [_fake_project(id=1), _fake_project(id=2)] with patch( - "fabledassistant.mcp.tools.projects.projects_svc.list_projects", + "scribe.mcp.tools.projects.projects_svc.list_projects", AsyncMock(return_value=rows), ): out = await list_projects() @@ -45,13 +45,13 @@ async def test_get_project_enriches_with_milestone_summary(): "rules": [], "truncated": False, "subscribed_rulebooks": [], } with patch( - "fabledassistant.mcp.tools.projects.projects_svc.get_project", + "scribe.mcp.tools.projects.projects_svc.get_project", AsyncMock(return_value=p), ), patch( - "fabledassistant.mcp.tools.projects.milestones_svc.get_project_milestone_summary", + "scribe.mcp.tools.projects.milestones_svc.get_project_milestone_summary", AsyncMock(return_value=milestone_summary), ), patch( - "fabledassistant.mcp.tools.projects.rulebooks_svc.get_applicable_rules", + "scribe.mcp.tools.projects.rulebooks_svc.get_applicable_rules", AsyncMock(return_value=applicable_payload), ): out = await get_project(project_id=5) @@ -77,13 +77,13 @@ async def test_get_project_includes_applicable_rules_and_subscribed_rulebooks(): "subscribed_rulebooks": [{"id": 1, "title": "FabledSword family"}], } with patch( - "fabledassistant.mcp.tools.projects.projects_svc.get_project", + "scribe.mcp.tools.projects.projects_svc.get_project", AsyncMock(return_value=p), ), patch( - "fabledassistant.mcp.tools.projects.milestones_svc.get_project_milestone_summary", + "scribe.mcp.tools.projects.milestones_svc.get_project_milestone_summary", AsyncMock(return_value=milestone_summary), ), patch( - "fabledassistant.mcp.tools.projects.rulebooks_svc.get_applicable_rules", + "scribe.mcp.tools.projects.rulebooks_svc.get_applicable_rules", AsyncMock(return_value=applicable_payload), ): out = await get_project(project_id=3) @@ -95,7 +95,7 @@ async def test_get_project_includes_applicable_rules_and_subscribed_rulebooks(): @pytest.mark.asyncio async def test_get_project_raises_when_not_found(): with patch( - "fabledassistant.mcp.tools.projects.projects_svc.get_project", + "scribe.mcp.tools.projects.projects_svc.get_project", AsyncMock(return_value=None), ): with pytest.raises(ValueError, match="project 999 not found"): @@ -106,7 +106,7 @@ async def test_get_project_raises_when_not_found(): async def test_create_project_passes_color_empty_as_none(): p = _fake_project() mock = AsyncMock(return_value=p) - with patch("fabledassistant.mcp.tools.projects.projects_svc.create_project", mock): + with patch("scribe.mcp.tools.projects.projects_svc.create_project", mock): await create_project(title="P", color="") assert mock.call_args.kwargs["color"] is None @@ -115,7 +115,7 @@ async def test_create_project_passes_color_empty_as_none(): async def test_update_project_only_sends_non_default_fields(): p = _fake_project() mock = AsyncMock(return_value=p) - with patch("fabledassistant.mcp.tools.projects.projects_svc.update_project", mock): + with patch("scribe.mcp.tools.projects.projects_svc.update_project", mock): await update_project(project_id=1, status="archived") args, kwargs = mock.call_args assert args == (7, 1) @@ -125,7 +125,7 @@ async def test_update_project_only_sends_non_default_fields(): @pytest.mark.asyncio async def test_update_project_raises_when_not_found(): with patch( - "fabledassistant.mcp.tools.projects.projects_svc.update_project", + "scribe.mcp.tools.projects.projects_svc.update_project", AsyncMock(return_value=None), ): with pytest.raises(ValueError, match="project 999 not found"): @@ -154,16 +154,16 @@ async def test_enter_project_composes_full_context(): note1.updated_at = None # avoids datetime mocking with patch( - "fabledassistant.mcp.tools.projects.projects_svc.get_project", + "scribe.mcp.tools.projects.projects_svc.get_project", AsyncMock(return_value=p), ), patch( - "fabledassistant.mcp.tools.projects.rulebooks_svc.get_applicable_rules", + "scribe.mcp.tools.projects.rulebooks_svc.get_applicable_rules", AsyncMock(return_value=applicable_payload), ), patch( - "fabledassistant.mcp.tools.projects.milestones_svc.get_project_milestone_summary", + "scribe.mcp.tools.projects.milestones_svc.get_project_milestone_summary", AsyncMock(return_value=milestone_summary), ), patch( - "fabledassistant.mcp.tools.projects.notes_svc.list_notes", + "scribe.mcp.tools.projects.notes_svc.list_notes", AsyncMock(side_effect=[([task1], 1), ([note1], 1)]), ): out = await enter_project(project_id=5) @@ -181,7 +181,7 @@ async def test_enter_project_composes_full_context(): @pytest.mark.asyncio async def test_enter_project_raises_when_project_not_found(): with patch( - "fabledassistant.mcp.tools.projects.projects_svc.get_project", + "scribe.mcp.tools.projects.projects_svc.get_project", AsyncMock(return_value=None), ): with pytest.raises(ValueError, match="project 999 not found"): @@ -190,7 +190,7 @@ async def test_enter_project_raises_when_project_not_found(): def test_enter_project_registered_in_register(): """register(mcp) registers enter_project alongside the existing tools.""" - from fabledassistant.mcp.tools.projects import register + from scribe.mcp.tools.projects import register registered: list[str] = [] class FakeMCP: diff --git a/tests/test_mcp_tool_rulebooks.py b/tests/test_mcp_tool_rulebooks.py index 7c480e2..80088f8 100644 --- a/tests/test_mcp_tool_rulebooks.py +++ b/tests/test_mcp_tool_rulebooks.py @@ -3,7 +3,7 @@ from unittest.mock import AsyncMock, MagicMock, patch import pytest -from fabledassistant.mcp._context import _user_id_ctx +from scribe.mcp._context import _user_id_ctx @pytest.fixture(autouse=True) @@ -43,10 +43,10 @@ def _fake_rule(id=100, title="r", statement="s"): async def test_list_rulebooks_wraps_in_dict(): rows = [_fake_rulebook(id=1), _fake_rulebook(id=2)] with patch( - "fabledassistant.mcp.tools.rulebooks.rulebooks_svc.list_rulebooks", + "scribe.mcp.tools.rulebooks.rulebooks_svc.list_rulebooks", AsyncMock(return_value=rows), ): - from fabledassistant.mcp.tools.rulebooks import list_rulebooks + from scribe.mcp.tools.rulebooks import list_rulebooks out = await list_rulebooks() assert len(out["rulebooks"]) == 2 @@ -56,13 +56,13 @@ async def test_get_rulebook_includes_topics(): rb = _fake_rulebook(id=1) topics = [_fake_topic(id=10), _fake_topic(id=11)] with patch( - "fabledassistant.mcp.tools.rulebooks.rulebooks_svc.get_rulebook", + "scribe.mcp.tools.rulebooks.rulebooks_svc.get_rulebook", AsyncMock(return_value=rb), ), patch( - "fabledassistant.mcp.tools.rulebooks.rulebooks_svc.list_topics", + "scribe.mcp.tools.rulebooks.rulebooks_svc.list_topics", AsyncMock(return_value=topics), ): - from fabledassistant.mcp.tools.rulebooks import get_rulebook + from scribe.mcp.tools.rulebooks import get_rulebook out = await get_rulebook(rulebook_id=1) assert out["id"] == 1 assert len(out["topics"]) == 2 @@ -71,10 +71,10 @@ async def test_get_rulebook_includes_topics(): @pytest.mark.asyncio async def test_get_rulebook_raises_when_not_found(): with patch( - "fabledassistant.mcp.tools.rulebooks.rulebooks_svc.get_rulebook", + "scribe.mcp.tools.rulebooks.rulebooks_svc.get_rulebook", AsyncMock(return_value=None), ): - from fabledassistant.mcp.tools.rulebooks import get_rulebook + from scribe.mcp.tools.rulebooks import get_rulebook with pytest.raises(ValueError, match="rulebook 999 not found"): await get_rulebook(rulebook_id=999) @@ -83,8 +83,8 @@ async def test_get_rulebook_raises_when_not_found(): async def test_create_rule_passes_required_fields(): rule = _fake_rule() mock = AsyncMock(return_value=rule) - with patch("fabledassistant.mcp.tools.rulebooks.rulebooks_svc.create_rule", mock): - from fabledassistant.mcp.tools.rulebooks import create_rule + with patch("scribe.mcp.tools.rulebooks.rulebooks_svc.create_rule", mock): + from scribe.mcp.tools.rulebooks import create_rule await create_rule( topic_id=10, title="dev is home", statement="Work directly on dev", ) @@ -98,8 +98,8 @@ async def test_create_rule_passes_required_fields(): async def test_update_rule_only_sends_non_default_fields(): rule = _fake_rule() mock = AsyncMock(return_value=rule) - with patch("fabledassistant.mcp.tools.rulebooks.rulebooks_svc.update_rule", mock): - from fabledassistant.mcp.tools.rulebooks import update_rule + with patch("scribe.mcp.tools.rulebooks.rulebooks_svc.update_rule", mock): + from scribe.mcp.tools.rulebooks import update_rule await update_rule(rule_id=1, statement="new statement") args, kwargs = mock.call_args assert args == (1, 7) @@ -111,13 +111,13 @@ async def test_delete_rule_without_confirmed_returns_warning(): """delete_rule with confirmed=False returns a preview, not an action.""" rule = _fake_rule() with patch( - "fabledassistant.mcp.tools.rulebooks.rulebooks_svc.get_rule", + "scribe.mcp.tools.rulebooks.rulebooks_svc.get_rule", AsyncMock(return_value=rule), ), patch( - "fabledassistant.mcp.tools.rulebooks.rulebooks_svc.delete_rule", + "scribe.mcp.tools.rulebooks.rulebooks_svc.delete_rule", AsyncMock(), ) as mock_delete: - from fabledassistant.mcp.tools.rulebooks import delete_rule + from scribe.mcp.tools.rulebooks import delete_rule out = await delete_rule(rule_id=1, confirmed=False) assert out.get("confirmed_required") is True assert "confirmed=True" in out.get("warning", "") @@ -129,13 +129,13 @@ async def test_delete_rule_with_confirmed_soft_deletes(): rule = _fake_rule() mock_delete = AsyncMock(return_value="batch-1") with patch( - "fabledassistant.mcp.tools.rulebooks.rulebooks_svc.get_rule", + "scribe.mcp.tools.rulebooks.rulebooks_svc.get_rule", AsyncMock(return_value=rule), ), patch( - "fabledassistant.mcp.tools.rulebooks.trash_svc.delete", + "scribe.mcp.tools.rulebooks.trash_svc.delete", mock_delete, ): - from fabledassistant.mcp.tools.rulebooks import delete_rule + from scribe.mcp.tools.rulebooks import delete_rule out = await delete_rule(rule_id=1, confirmed=True) assert out["deleted"] == 1 assert out["deleted_batch_id"] == "batch-1" @@ -146,9 +146,9 @@ async def test_delete_rule_with_confirmed_soft_deletes(): async def test_subscribe_project_to_rulebook_calls_service(): mock = AsyncMock() with patch( - "fabledassistant.mcp.tools.rulebooks.rulebooks_svc.subscribe_project", mock, + "scribe.mcp.tools.rulebooks.rulebooks_svc.subscribe_project", mock, ): - from fabledassistant.mcp.tools.rulebooks import subscribe_project_to_rulebook + from scribe.mcp.tools.rulebooks import subscribe_project_to_rulebook out = await subscribe_project_to_rulebook(project_id=3, rulebook_id=1) assert out["subscribed"] is True assert mock.called @@ -158,9 +158,9 @@ async def test_subscribe_project_to_rulebook_calls_service(): async def test_unsubscribe_project_from_rulebook_calls_service(): mock = AsyncMock() with patch( - "fabledassistant.mcp.tools.rulebooks.rulebooks_svc.unsubscribe_project", mock, + "scribe.mcp.tools.rulebooks.rulebooks_svc.unsubscribe_project", mock, ): - from fabledassistant.mcp.tools.rulebooks import unsubscribe_project_from_rulebook + from scribe.mcp.tools.rulebooks import unsubscribe_project_from_rulebook out = await unsubscribe_project_from_rulebook(project_id=3, rulebook_id=1) assert out["subscribed"] is False assert mock.called @@ -168,7 +168,7 @@ async def test_unsubscribe_project_from_rulebook_calls_service(): def test_register_attaches_all_sixteen_tools(): """register(mcp) should call mcp.tool(name=...) for all 16 tools.""" - from fabledassistant.mcp.tools.rulebooks import register + from scribe.mcp.tools.rulebooks import register registered: list[str] = [] class FakeMCP: @@ -195,10 +195,10 @@ def test_register_attaches_all_sixteen_tools(): @pytest.mark.asyncio async def test_list_always_on_rules_returns_empty_when_no_always_on_rulebooks(): with patch( - "fabledassistant.mcp.tools.rulebooks.rulebooks_svc.list_always_on_rules", + "scribe.mcp.tools.rulebooks.rulebooks_svc.list_always_on_rules", AsyncMock(return_value=[]), ): - from fabledassistant.mcp.tools.rulebooks import list_always_on_rules + from scribe.mcp.tools.rulebooks import list_always_on_rules out = await list_always_on_rules() assert out == {"rules": [], "total": 0} @@ -207,10 +207,10 @@ async def test_list_always_on_rules_returns_empty_when_no_always_on_rulebooks(): async def test_list_always_on_rules_projects_each_rule(): rules = [_fake_rule(id=100), _fake_rule(id=101)] with patch( - "fabledassistant.mcp.tools.rulebooks.rulebooks_svc.list_always_on_rules", + "scribe.mcp.tools.rulebooks.rulebooks_svc.list_always_on_rules", AsyncMock(return_value=rules), ): - from fabledassistant.mcp.tools.rulebooks import list_always_on_rules + from scribe.mcp.tools.rulebooks import list_always_on_rules out = await list_always_on_rules() assert out["total"] == 2 assert {r["id"] for r in out["rules"]} == {100, 101} @@ -221,8 +221,8 @@ async def test_list_always_on_rules_projects_each_rule(): async def test_update_rulebook_forwards_always_on_when_set(): rb = _fake_rulebook(id=1, title="t") mock = AsyncMock(return_value=rb) - with patch("fabledassistant.mcp.tools.rulebooks.rulebooks_svc.update_rulebook", mock): - from fabledassistant.mcp.tools.rulebooks import update_rulebook + with patch("scribe.mcp.tools.rulebooks.rulebooks_svc.update_rulebook", mock): + from scribe.mcp.tools.rulebooks import update_rulebook await update_rulebook(rulebook_id=1, always_on=True) kwargs = mock.call_args.kwargs assert kwargs.get("always_on") is True @@ -234,8 +234,8 @@ async def test_update_rulebook_forwards_always_on_when_set(): async def test_update_rulebook_omits_always_on_when_none(): rb = _fake_rulebook(id=1, title="t") mock = AsyncMock(return_value=rb) - with patch("fabledassistant.mcp.tools.rulebooks.rulebooks_svc.update_rulebook", mock): - from fabledassistant.mcp.tools.rulebooks import update_rulebook + with patch("scribe.mcp.tools.rulebooks.rulebooks_svc.update_rulebook", mock): + from scribe.mcp.tools.rulebooks import update_rulebook await update_rulebook(rulebook_id=1, title="new title") kwargs = mock.call_args.kwargs assert "always_on" not in kwargs @@ -246,8 +246,8 @@ async def test_update_rulebook_omits_always_on_when_none(): async def test_create_project_rule_passes_required_fields(): rule = _fake_rule() mock = AsyncMock(return_value=rule) - with patch("fabledassistant.mcp.tools.rulebooks.rulebooks_svc.create_project_rule", mock): - from fabledassistant.mcp.tools.rulebooks import create_project_rule + with patch("scribe.mcp.tools.rulebooks.rulebooks_svc.create_project_rule", mock): + from scribe.mcp.tools.rulebooks import create_project_rule await create_project_rule( project_id=42, statement="Always run migrations through alembic, not raw SQL.", @@ -264,8 +264,8 @@ async def test_create_project_rule_passes_required_fields(): async def test_create_project_rule_derives_title_from_statement(): rule = _fake_rule() mock = AsyncMock(return_value=rule) - with patch("fabledassistant.mcp.tools.rulebooks.rulebooks_svc.create_project_rule", mock): - from fabledassistant.mcp.tools.rulebooks import create_project_rule + with patch("scribe.mcp.tools.rulebooks.rulebooks_svc.create_project_rule", mock): + from scribe.mcp.tools.rulebooks import create_project_rule await create_project_rule( project_id=42, statement="Avoid auto-generated docstrings. Reviewers find them noise.", @@ -279,8 +279,8 @@ async def test_create_project_rule_derives_title_from_statement(): async def test_create_project_rule_uses_explicit_title_when_given(): rule = _fake_rule() mock = AsyncMock(return_value=rule) - with patch("fabledassistant.mcp.tools.rulebooks.rulebooks_svc.create_project_rule", mock): - from fabledassistant.mcp.tools.rulebooks import create_project_rule + with patch("scribe.mcp.tools.rulebooks.rulebooks_svc.create_project_rule", mock): + from scribe.mcp.tools.rulebooks import create_project_rule await create_project_rule( project_id=42, statement="anything", @@ -293,8 +293,8 @@ async def test_create_project_rule_uses_explicit_title_when_given(): @pytest.mark.asyncio async def test_suppress_rule_for_project_passes_through(): mock = AsyncMock(return_value=None) - with patch("fabledassistant.mcp.tools.rulebooks.rulebooks_svc.suppress_rule_for_project", mock): - from fabledassistant.mcp.tools.rulebooks import suppress_rule_for_project + with patch("scribe.mcp.tools.rulebooks.rulebooks_svc.suppress_rule_for_project", mock): + from scribe.mcp.tools.rulebooks import suppress_rule_for_project out = await suppress_rule_for_project(project_id=3, rule_id=17) kwargs = mock.call_args.kwargs assert kwargs == {"project_id": 3, "rule_id": 17, "user_id": 7} @@ -304,8 +304,8 @@ async def test_suppress_rule_for_project_passes_through(): @pytest.mark.asyncio async def test_unsuppress_rule_for_project_passes_through(): mock = AsyncMock(return_value=None) - with patch("fabledassistant.mcp.tools.rulebooks.rulebooks_svc.unsuppress_rule_for_project", mock): - from fabledassistant.mcp.tools.rulebooks import unsuppress_rule_for_project + with patch("scribe.mcp.tools.rulebooks.rulebooks_svc.unsuppress_rule_for_project", mock): + from scribe.mcp.tools.rulebooks import unsuppress_rule_for_project out = await unsuppress_rule_for_project(project_id=3, rule_id=17) kwargs = mock.call_args.kwargs assert kwargs == {"project_id": 3, "rule_id": 17, "user_id": 7} @@ -315,8 +315,8 @@ async def test_unsuppress_rule_for_project_passes_through(): @pytest.mark.asyncio async def test_suppress_topic_for_project_passes_through(): mock = AsyncMock(return_value=None) - with patch("fabledassistant.mcp.tools.rulebooks.rulebooks_svc.suppress_topic_for_project", mock): - from fabledassistant.mcp.tools.rulebooks import suppress_topic_for_project + with patch("scribe.mcp.tools.rulebooks.rulebooks_svc.suppress_topic_for_project", mock): + from scribe.mcp.tools.rulebooks import suppress_topic_for_project out = await suppress_topic_for_project(project_id=3, topic_id=22) kwargs = mock.call_args.kwargs assert kwargs == {"project_id": 3, "topic_id": 22, "user_id": 7} @@ -326,8 +326,8 @@ async def test_suppress_topic_for_project_passes_through(): @pytest.mark.asyncio async def test_unsuppress_topic_for_project_passes_through(): mock = AsyncMock(return_value=None) - with patch("fabledassistant.mcp.tools.rulebooks.rulebooks_svc.unsuppress_topic_for_project", mock): - from fabledassistant.mcp.tools.rulebooks import unsuppress_topic_for_project + with patch("scribe.mcp.tools.rulebooks.rulebooks_svc.unsuppress_topic_for_project", mock): + from scribe.mcp.tools.rulebooks import unsuppress_topic_for_project out = await unsuppress_topic_for_project(project_id=3, topic_id=22) kwargs = mock.call_args.kwargs assert kwargs == {"project_id": 3, "topic_id": 22, "user_id": 7} diff --git a/tests/test_mcp_tool_search.py b/tests/test_mcp_tool_search.py index b826506..4b1f180 100644 --- a/tests/test_mcp_tool_search.py +++ b/tests/test_mcp_tool_search.py @@ -5,8 +5,8 @@ from unittest.mock import AsyncMock, MagicMock, patch import pytest -from fabledassistant.mcp._context import _user_id_ctx -from fabledassistant.mcp.tools.search import search +from scribe.mcp._context import _user_id_ctx +from scribe.mcp.tools.search import search @pytest.fixture(autouse=True) @@ -40,7 +40,7 @@ async def test_fable_search_returns_repackaged_results(): fake = _fake_note(id=1, title="kafka rebalance", body="HPA details", tags=["ops"], is_task=False) with patch( - "fabledassistant.mcp.tools.search.semantic_search_notes", + "scribe.mcp.tools.search.semantic_search_notes", AsyncMock(return_value=[(0.93, fake)]), ): out = await search(q="kafka") @@ -62,7 +62,7 @@ async def test_fable_search_body_is_truncated_to_240_chars(): long_body = "x" * 500 fake = _fake_note(id=1, title="t", body=long_body) with patch( - "fabledassistant.mcp.tools.search.semantic_search_notes", + "scribe.mcp.tools.search.semantic_search_notes", AsyncMock(return_value=[(0.5, fake)]), ): out = await search(q="x") @@ -74,7 +74,7 @@ async def test_fable_search_content_type_filters_at_service_layer(): """content_type maps to the is_task kwarg passed to the service.""" _user_id_ctx.set(7) mock_search = AsyncMock(return_value=[]) - with patch("fabledassistant.mcp.tools.search.semantic_search_notes", mock_search): + with patch("scribe.mcp.tools.search.semantic_search_notes", mock_search): await search(q="x", content_type="task") assert mock_search.call_args.kwargs["is_task"] is True @@ -91,7 +91,7 @@ async def test_fable_search_content_type_filters_at_service_layer(): async def test_fable_search_limit_is_clamped(): _user_id_ctx.set(7) mock_search = AsyncMock(return_value=[]) - with patch("fabledassistant.mcp.tools.search.semantic_search_notes", mock_search): + with patch("scribe.mcp.tools.search.semantic_search_notes", mock_search): await search(q="x", limit=999) assert mock_search.call_args.kwargs["limit"] == 50 diff --git a/tests/test_mcp_tool_tags.py b/tests/test_mcp_tool_tags.py index f4ce0fd..c2753cf 100644 --- a/tests/test_mcp_tool_tags.py +++ b/tests/test_mcp_tool_tags.py @@ -7,8 +7,8 @@ from unittest.mock import AsyncMock, MagicMock, patch import pytest -from fabledassistant.mcp._context import _user_id_ctx -from fabledassistant.mcp.tools.tags import list_tags, _aggregate_tag_counts +from scribe.mcp._context import _user_id_ctx +from scribe.mcp.tools.tags import list_tags, _aggregate_tag_counts @pytest.fixture(autouse=True) @@ -41,7 +41,7 @@ async def test_fable_list_tags_returns_sorted_by_count_desc(): mock_session.__aexit__ = AsyncMock(return_value=False) mock_session.execute = AsyncMock(return_value=mock_result) mock_ctx = MagicMock(return_value=mock_session) - with patch("fabledassistant.mcp.tools.tags.async_session", mock_ctx): + with patch("scribe.mcp.tools.tags.async_session", mock_ctx): out = await list_tags() assert out["tags"][0] == {"tag": "a", "count": 3} assert out["tags"][1] == {"tag": "b", "count": 1} @@ -57,7 +57,7 @@ async def test_fable_list_tags_clamps_limit(): mock_session.__aexit__ = AsyncMock(return_value=False) mock_session.execute = AsyncMock(return_value=mock_result) mock_ctx = MagicMock(return_value=mock_session) - with patch("fabledassistant.mcp.tools.tags.async_session", mock_ctx): + with patch("scribe.mcp.tools.tags.async_session", mock_ctx): # No exception — just exercises the clamping branch out = await list_tags(limit=99999) assert out["total"] == 0 diff --git a/tests/test_mcp_tool_tasks.py b/tests/test_mcp_tool_tasks.py index 44052c5..9aa8f96 100644 --- a/tests/test_mcp_tool_tasks.py +++ b/tests/test_mcp_tool_tasks.py @@ -4,8 +4,8 @@ from unittest.mock import AsyncMock, MagicMock, patch import pytest -from fabledassistant.mcp._context import _user_id_ctx -from fabledassistant.mcp.tools.tasks import ( +from scribe.mcp._context import _user_id_ctx +from scribe.mcp.tools.tasks import ( list_tasks, get_task, create_task, update_task, add_task_log, ) @@ -36,7 +36,7 @@ def _fake_task(*, parent_id: int | None = None, **overrides) -> MagicMock: async def test_list_tasks_passes_is_task_true_and_repackages(): rows = [_fake_task(id=1), _fake_task(id=2)] mock = AsyncMock(return_value=(rows, 2)) - with patch("fabledassistant.mcp.tools.tasks.notes_svc.list_notes", mock): + with patch("scribe.mcp.tools.tasks.notes_svc.list_notes", mock): out = await list_tasks() assert out["total"] == 2 assert len(out["tasks"]) == 2 @@ -46,7 +46,7 @@ async def test_list_tasks_passes_is_task_true_and_repackages(): @pytest.mark.asyncio async def test_list_tasks_status_filter(): mock = AsyncMock(return_value=([], 0)) - with patch("fabledassistant.mcp.tools.tasks.notes_svc.list_notes", mock): + with patch("scribe.mcp.tools.tasks.notes_svc.list_notes", mock): await list_tasks(status="in_progress") assert mock.call_args.kwargs["status"] == "in_progress" @@ -54,7 +54,7 @@ async def test_list_tasks_status_filter(): @pytest.mark.asyncio async def test_list_tasks_empty_status_means_no_filter(): mock = AsyncMock(return_value=([], 0)) - with patch("fabledassistant.mcp.tools.tasks.notes_svc.list_notes", mock): + with patch("scribe.mcp.tools.tasks.notes_svc.list_notes", mock): await list_tasks(status="") assert mock.call_args.kwargs["status"] is None @@ -63,7 +63,7 @@ async def test_list_tasks_empty_status_means_no_filter(): async def test_get_task_with_no_parent_returns_null_parent_title(): fake = _fake_task(id=5, title="solo", parent_id=None) with patch( - "fabledassistant.mcp.tools.tasks.notes_svc.get_note", + "scribe.mcp.tools.tasks.notes_svc.get_note", AsyncMock(return_value=fake), ): out = await get_task(task_id=5) @@ -78,7 +78,7 @@ async def test_get_task_enriches_with_parent_title(): parent = _fake_task(id=5, title="parent of 10", parent_id=None) # get_note is called twice: once for child, once for parent mock_get = AsyncMock(side_effect=[child, parent]) - with patch("fabledassistant.mcp.tools.tasks.notes_svc.get_note", mock_get): + with patch("scribe.mcp.tools.tasks.notes_svc.get_note", mock_get): out = await get_task(task_id=10) assert out["parent_title"] == "parent of 10" assert mock_get.await_count == 2 @@ -89,7 +89,7 @@ async def test_get_task_parent_missing_returns_null(): """If parent_id is set but the parent is gone (orphaned), parent_title is None.""" child = _fake_task(id=10, parent_id=5) mock_get = AsyncMock(side_effect=[child, None]) - with patch("fabledassistant.mcp.tools.tasks.notes_svc.get_note", mock_get): + with patch("scribe.mcp.tools.tasks.notes_svc.get_note", mock_get): out = await get_task(task_id=10) assert out["parent_title"] is None @@ -97,7 +97,7 @@ async def test_get_task_parent_missing_returns_null(): @pytest.mark.asyncio async def test_get_task_raises_when_not_found(): with patch( - "fabledassistant.mcp.tools.tasks.notes_svc.get_note", + "scribe.mcp.tools.tasks.notes_svc.get_note", AsyncMock(return_value=None), ): with pytest.raises(ValueError, match="task 999 not found"): @@ -108,7 +108,7 @@ async def test_get_task_raises_when_not_found(): async def test_create_task_passes_status(): fake = _fake_task() mock = AsyncMock(return_value=fake) - with patch("fabledassistant.mcp.tools.tasks.notes_svc.create_note", mock): + with patch("scribe.mcp.tools.tasks.notes_svc.create_note", mock): await create_task(title="do x", status="todo") assert mock.call_args.kwargs["status"] == "todo" @@ -117,7 +117,7 @@ async def test_create_task_passes_status(): async def test_create_task_priority_empty_becomes_none(): fake = _fake_task() mock = AsyncMock(return_value=fake) - with patch("fabledassistant.mcp.tools.tasks.notes_svc.create_note", mock): + with patch("scribe.mcp.tools.tasks.notes_svc.create_note", mock): await create_task(title="x", priority="") assert mock.call_args.kwargs["priority"] is None @@ -126,7 +126,7 @@ async def test_create_task_priority_empty_becomes_none(): async def test_create_task_zero_id_sentinels_become_none(): fake = _fake_task() mock = AsyncMock(return_value=fake) - with patch("fabledassistant.mcp.tools.tasks.notes_svc.create_note", mock): + with patch("scribe.mcp.tools.tasks.notes_svc.create_note", mock): await create_task(title="x", project_id=0, milestone_id=0, parent_id=0) assert mock.call_args.kwargs["project_id"] is None assert mock.call_args.kwargs["milestone_id"] is None @@ -137,7 +137,7 @@ async def test_create_task_zero_id_sentinels_become_none(): async def test_update_task_only_sends_non_default_fields(): fake = _fake_task() mock = AsyncMock(return_value=fake) - with patch("fabledassistant.mcp.tools.tasks.notes_svc.update_note", mock): + with patch("scribe.mcp.tools.tasks.notes_svc.update_note", mock): await update_task(task_id=1, status="done") args, kwargs = mock.call_args assert args == (7, 1) @@ -149,7 +149,7 @@ async def test_update_task_empty_priority_is_omitted(): """Priority="" is "leave unchanged" — must not reach service as empty string.""" fake = _fake_task() mock = AsyncMock(return_value=fake) - with patch("fabledassistant.mcp.tools.tasks.notes_svc.update_note", mock): + with patch("scribe.mcp.tools.tasks.notes_svc.update_note", mock): await update_task(task_id=1, status="done", priority="") assert "priority" not in mock.call_args.kwargs @@ -157,7 +157,7 @@ async def test_update_task_empty_priority_is_omitted(): @pytest.mark.asyncio async def test_update_task_raises_when_not_found(): with patch( - "fabledassistant.mcp.tools.tasks.notes_svc.update_note", + "scribe.mcp.tools.tasks.notes_svc.update_note", AsyncMock(return_value=None), ): with pytest.raises(ValueError, match="task 999 not found"): @@ -169,7 +169,7 @@ async def test_update_task_milestone_zero_is_omitted(): """milestone_id=0 is 'leave unchanged' — must not reach the service.""" fake = _fake_task() mock = AsyncMock(return_value=fake) - with patch("fabledassistant.mcp.tools.tasks.notes_svc.update_note", mock): + with patch("scribe.mcp.tools.tasks.notes_svc.update_note", mock): await update_task(task_id=1, milestone_id=0) assert "milestone_id" not in mock.call_args.kwargs @@ -178,7 +178,7 @@ async def test_update_task_milestone_zero_is_omitted(): async def test_update_task_milestone_positive_is_set(): fake = _fake_task() mock = AsyncMock(return_value=fake) - with patch("fabledassistant.mcp.tools.tasks.notes_svc.update_note", mock): + with patch("scribe.mcp.tools.tasks.notes_svc.update_note", mock): await update_task(task_id=1, milestone_id=42) assert mock.call_args.kwargs["milestone_id"] == 42 @@ -188,7 +188,7 @@ async def test_update_task_milestone_negative_one_clears(): """milestone_id=-1 clears the milestone (sets the column NULL).""" fake = _fake_task() mock = AsyncMock(return_value=fake) - with patch("fabledassistant.mcp.tools.tasks.notes_svc.update_note", mock): + with patch("scribe.mcp.tools.tasks.notes_svc.update_note", mock): await update_task(task_id=1, milestone_id=-1) assert mock.call_args.kwargs["milestone_id"] is None @@ -198,7 +198,7 @@ async def test_update_task_clearing_project_also_clears_milestone(): """project_id=-1 clears the project and, with it, the milestone.""" fake = _fake_task() mock = AsyncMock(return_value=fake) - with patch("fabledassistant.mcp.tools.tasks.notes_svc.update_note", mock): + with patch("scribe.mcp.tools.tasks.notes_svc.update_note", mock): await update_task(task_id=1, project_id=-1) assert mock.call_args.kwargs["project_id"] is None assert mock.call_args.kwargs["milestone_id"] is None @@ -214,7 +214,7 @@ async def test_add_task_log_returns_log_dict(): # No to_dict on TaskLog model (per task_logs.py inspection) — fall through to manual dict del log.to_dict with patch( - "fabledassistant.mcp.tools.tasks.task_logs_svc.create_log", + "scribe.mcp.tools.tasks.task_logs_svc.create_log", AsyncMock(return_value=log), ): out = await add_task_log(task_id=1, content="checkpoint") @@ -228,7 +228,7 @@ async def test_add_task_log_returns_log_dict(): async def test_add_task_log_propagates_value_error_when_task_missing(): """create_log raises ValueError if the task doesn't exist; we let it through.""" with patch( - "fabledassistant.mcp.tools.tasks.task_logs_svc.create_log", + "scribe.mcp.tools.tasks.task_logs_svc.create_log", AsyncMock(side_effect=ValueError("Task 999 not found")), ): with pytest.raises(ValueError): diff --git a/tests/test_mcp_tool_tasks_kind.py b/tests/test_mcp_tool_tasks_kind.py index 9d8bda0..e05bdb6 100644 --- a/tests/test_mcp_tool_tasks_kind.py +++ b/tests/test_mcp_tool_tasks_kind.py @@ -2,7 +2,7 @@ from unittest.mock import AsyncMock, MagicMock, patch import pytest -from fabledassistant.mcp._context import _user_id_ctx +from scribe.mcp._context import _user_id_ctx @pytest.fixture(autouse=True) @@ -21,8 +21,8 @@ def _fake_note(task_kind="work"): @pytest.mark.asyncio async def test_create_task_passes_kind(): mock = AsyncMock(return_value=_fake_note(task_kind="plan")) - with patch("fabledassistant.mcp.tools.tasks.notes_svc.create_note", mock): - from fabledassistant.mcp.tools.tasks import create_task + with patch("scribe.mcp.tools.tasks.notes_svc.create_note", mock): + from scribe.mcp.tools.tasks import create_task await create_task(title="P", kind="plan") assert mock.call_args.kwargs["task_kind"] == "plan" @@ -30,8 +30,8 @@ async def test_create_task_passes_kind(): @pytest.mark.asyncio async def test_list_tasks_passes_kind_filter(): mock = AsyncMock(return_value=([], 0)) - with patch("fabledassistant.mcp.tools.tasks.notes_svc.list_notes", mock): - from fabledassistant.mcp.tools.tasks import list_tasks + with patch("scribe.mcp.tools.tasks.notes_svc.list_notes", mock): + from scribe.mcp.tools.tasks import list_tasks await list_tasks(kind="plan") assert mock.call_args.kwargs["task_kind"] == "plan" @@ -39,7 +39,7 @@ async def test_list_tasks_passes_kind_filter(): @pytest.mark.asyncio async def test_list_tasks_kind_empty_means_no_filter(): mock = AsyncMock(return_value=([], 0)) - with patch("fabledassistant.mcp.tools.tasks.notes_svc.list_notes", mock): - from fabledassistant.mcp.tools.tasks import list_tasks + with patch("scribe.mcp.tools.tasks.notes_svc.list_notes", mock): + from scribe.mcp.tools.tasks import list_tasks await list_tasks() assert mock.call_args.kwargs["task_kind"] is None diff --git a/tests/test_mcp_tool_trash.py b/tests/test_mcp_tool_trash.py index a20a91f..c59bc34 100644 --- a/tests/test_mcp_tool_trash.py +++ b/tests/test_mcp_tool_trash.py @@ -3,7 +3,7 @@ from unittest.mock import AsyncMock, patch import pytest -from fabledassistant.mcp._context import _user_id_ctx +from scribe.mcp._context import _user_id_ctx @pytest.fixture(autouse=True) @@ -15,18 +15,18 @@ def _bind_user(): @pytest.mark.asyncio async def test_list_trash_wraps_batches(): - with patch("fabledassistant.mcp.tools.trash.trash_svc.list_trash", + with patch("scribe.mcp.tools.trash.trash_svc.list_trash", AsyncMock(return_value=[{"batch_id": "b1", "count": 2}])): - from fabledassistant.mcp.tools.trash import list_trash + from scribe.mcp.tools.trash import list_trash out = await list_trash() assert out["batches"][0]["batch_id"] == "b1" @pytest.mark.asyncio async def test_restore_delegates(): - with patch("fabledassistant.mcp.tools.trash.trash_svc.restore", + with patch("scribe.mcp.tools.trash.trash_svc.restore", AsyncMock(return_value=3)) as mock: - from fabledassistant.mcp.tools.trash import restore + from scribe.mcp.tools.trash import restore out = await restore("b1") assert out == {"restored": 3, "batch_id": "b1"} assert mock.call_args.args == (7, "b1") @@ -34,8 +34,8 @@ async def test_restore_delegates(): @pytest.mark.asyncio async def test_purge_trash_requires_confirmed(): - with patch("fabledassistant.mcp.tools.trash.trash_svc.purge", AsyncMock()) as mock: - from fabledassistant.mcp.tools.trash import purge_trash + with patch("scribe.mcp.tools.trash.trash_svc.purge", AsyncMock()) as mock: + from scribe.mcp.tools.trash import purge_trash out = await purge_trash("b1", confirmed=False) assert out.get("confirmed_required") is True assert not mock.called @@ -43,16 +43,16 @@ async def test_purge_trash_requires_confirmed(): @pytest.mark.asyncio async def test_purge_trash_when_confirmed(): - with patch("fabledassistant.mcp.tools.trash.trash_svc.purge", + with patch("scribe.mcp.tools.trash.trash_svc.purge", AsyncMock(return_value=5)) as mock: - from fabledassistant.mcp.tools.trash import purge_trash + from scribe.mcp.tools.trash import purge_trash out = await purge_trash("b1", confirmed=True) assert out == {"purged": 5, "batch_id": "b1"} assert mock.called def test_register_attaches_three_tools(): - from fabledassistant.mcp.tools.trash import register + from scribe.mcp.tools.trash import register names: list[str] = [] class FakeMCP: diff --git a/tests/test_notes_description_field.py b/tests/test_notes_description_field.py index 2167d83..d519ce5 100644 --- a/tests/test_notes_description_field.py +++ b/tests/test_notes_description_field.py @@ -34,10 +34,10 @@ async def test_update_note_persists_description(): mock_note.project_id = None with patch( - "fabledassistant.services.notes.async_session", + "scribe.services.notes.async_session", return_value=_mock_session_for_update(mock_note), ): - from fabledassistant.services.notes import update_note + from scribe.services.notes import update_note await update_note(1, 1, description="the goal text") assert mock_note.description == "the goal text" @@ -64,9 +64,9 @@ async def test_create_note_forwards_description_to_model(): mock_session.__aexit__ = AsyncMock(return_value=False) with patch( - "fabledassistant.services.notes.async_session", return_value=mock_session - ), patch("fabledassistant.services.notes.Note", FakeNote): - from fabledassistant.services.notes import create_note + "scribe.services.notes.async_session", return_value=mock_session + ), patch("scribe.services.notes.Note", FakeNote): + from scribe.services.notes import create_note await create_note( user_id=1, title="renew cert", description="the goal text", ) diff --git a/tests/test_recurrence.py b/tests/test_recurrence.py index 071e15e..fad807d 100644 --- a/tests/test_recurrence.py +++ b/tests/test_recurrence.py @@ -21,8 +21,8 @@ async def test_update_note_sets_started_at_on_in_progress(): mock_session.__aenter__ = AsyncMock(return_value=mock_session) mock_session.__aexit__ = AsyncMock(return_value=False) - with patch("fabledassistant.services.notes.async_session", return_value=mock_session): - from fabledassistant.services.notes import update_note + with patch("scribe.services.notes.async_session", return_value=mock_session): + from scribe.services.notes import update_note await update_note(1, 1, status="in_progress") assert mock_note.started_at is not None @@ -45,8 +45,8 @@ async def test_update_note_sets_completed_at_on_done(): mock_session.__aenter__ = AsyncMock(return_value=mock_session) mock_session.__aexit__ = AsyncMock(return_value=False) - with patch("fabledassistant.services.notes.async_session", return_value=mock_session): - from fabledassistant.services.notes import update_note + with patch("scribe.services.notes.async_session", return_value=mock_session): + from scribe.services.notes import update_note await update_note(1, 1, status="done") assert mock_note.completed_at is not None @@ -69,8 +69,8 @@ async def test_update_note_clears_timestamps_on_todo(): mock_session.__aenter__ = AsyncMock(return_value=mock_session) mock_session.__aexit__ = AsyncMock(return_value=False) - with patch("fabledassistant.services.notes.async_session", return_value=mock_session): - from fabledassistant.services.notes import update_note + with patch("scribe.services.notes.async_session", return_value=mock_session): + from scribe.services.notes import update_note await update_note(1, 1, status="todo") assert mock_note.started_at is None @@ -94,8 +94,8 @@ async def test_update_note_preserves_started_at_if_already_set(): mock_session.__aenter__ = AsyncMock(return_value=mock_session) mock_session.__aexit__ = AsyncMock(return_value=False) - with patch("fabledassistant.services.notes.async_session", return_value=mock_session): - from fabledassistant.services.notes import update_note + with patch("scribe.services.notes.async_session", return_value=mock_session): + from scribe.services.notes import update_note await update_note(1, 1, status="in_progress") assert mock_note.started_at == original_start @@ -107,42 +107,42 @@ import pytest def test_validate_interval_rule_valid(): - from fabledassistant.services.recurrence import validate_recurrence_rule + from scribe.services.recurrence import validate_recurrence_rule validate_recurrence_rule({"type": "interval", "every": 3, "unit": "month"}) # no error def test_validate_interval_rule_bad_unit(): - from fabledassistant.services.recurrence import validate_recurrence_rule + from scribe.services.recurrence import validate_recurrence_rule with pytest.raises(ValueError, match="unit"): validate_recurrence_rule({"type": "interval", "every": 3, "unit": "fortnight"}) def test_validate_interval_rule_bad_every(): - from fabledassistant.services.recurrence import validate_recurrence_rule + from scribe.services.recurrence import validate_recurrence_rule with pytest.raises(ValueError, match="every"): validate_recurrence_rule({"type": "interval", "every": 0, "unit": "week"}) def test_validate_calendar_monthly_valid(): - from fabledassistant.services.recurrence import validate_recurrence_rule + from scribe.services.recurrence import validate_recurrence_rule validate_recurrence_rule({"type": "calendar", "unit": "month", "day_of_month": 1}) def test_validate_calendar_annual_valid(): - from fabledassistant.services.recurrence import validate_recurrence_rule + from scribe.services.recurrence import validate_recurrence_rule validate_recurrence_rule( {"type": "calendar", "unit": "year", "day_of_month": 15, "month": 6} ) def test_validate_calendar_bad_day(): - from fabledassistant.services.recurrence import validate_recurrence_rule + from scribe.services.recurrence import validate_recurrence_rule with pytest.raises(ValueError, match="day_of_month"): validate_recurrence_rule({"type": "calendar", "unit": "month", "day_of_month": 32}) def test_validate_calendar_annual_missing_month(): - from fabledassistant.services.recurrence import validate_recurrence_rule + from scribe.services.recurrence import validate_recurrence_rule with pytest.raises(ValueError, match="month"): validate_recurrence_rule( {"type": "calendar", "unit": "year", "day_of_month": 15} @@ -150,7 +150,7 @@ def test_validate_calendar_annual_missing_month(): def test_validate_unknown_type(): - from fabledassistant.services.recurrence import validate_recurrence_rule + from scribe.services.recurrence import validate_recurrence_rule with pytest.raises(ValueError, match="type"): validate_recurrence_rule({"type": "cron", "every": 1, "unit": "day"}) @@ -158,60 +158,60 @@ def test_validate_unknown_type(): # ── calculate_next_due ──────────────────────────────────────────────────────── def test_calculate_next_due_interval_days(): - from fabledassistant.services.recurrence import calculate_next_due + from scribe.services.recurrence import calculate_next_due rule = {"type": "interval", "every": 7, "unit": "day"} assert calculate_next_due(rule, date(2026, 3, 27)) == date(2026, 4, 3) def test_calculate_next_due_interval_weeks(): - from fabledassistant.services.recurrence import calculate_next_due + from scribe.services.recurrence import calculate_next_due rule = {"type": "interval", "every": 2, "unit": "week"} assert calculate_next_due(rule, date(2026, 3, 27)) == date(2026, 4, 10) def test_calculate_next_due_interval_months(): - from fabledassistant.services.recurrence import calculate_next_due + from scribe.services.recurrence import calculate_next_due rule = {"type": "interval", "every": 3, "unit": "month"} assert calculate_next_due(rule, date(2026, 3, 1)) == date(2026, 6, 1) def test_calculate_next_due_interval_months_end_of_month(): """Month addition clamps to last day when target month is shorter.""" - from fabledassistant.services.recurrence import calculate_next_due + from scribe.services.recurrence import calculate_next_due rule = {"type": "interval", "every": 1, "unit": "month"} assert calculate_next_due(rule, date(2026, 1, 31)) == date(2026, 2, 28) def test_calculate_next_due_interval_years(): - from fabledassistant.services.recurrence import calculate_next_due + from scribe.services.recurrence import calculate_next_due rule = {"type": "interval", "every": 1, "unit": "year"} assert calculate_next_due(rule, date(2026, 3, 27)) == date(2027, 3, 27) def test_calculate_next_due_calendar_monthly_future_day(): """If day_of_month is later this month, return that date.""" - from fabledassistant.services.recurrence import calculate_next_due + from scribe.services.recurrence import calculate_next_due rule = {"type": "calendar", "unit": "month", "day_of_month": 28} assert calculate_next_due(rule, date(2026, 3, 1)) == date(2026, 3, 28) def test_calculate_next_due_calendar_monthly_same_or_past_day(): """If day_of_month is today or earlier, advance to next month.""" - from fabledassistant.services.recurrence import calculate_next_due + from scribe.services.recurrence import calculate_next_due rule = {"type": "calendar", "unit": "month", "day_of_month": 1} assert calculate_next_due(rule, date(2026, 3, 1)) == date(2026, 4, 1) def test_calculate_next_due_calendar_annual_future(): """Annual date later this year is returned.""" - from fabledassistant.services.recurrence import calculate_next_due + from scribe.services.recurrence import calculate_next_due rule = {"type": "calendar", "unit": "year", "day_of_month": 15, "month": 6} assert calculate_next_due(rule, date(2026, 3, 27)) == date(2026, 6, 15) def test_calculate_next_due_calendar_annual_past(): """Annual date already passed this year → next year.""" - from fabledassistant.services.recurrence import calculate_next_due + from scribe.services.recurrence import calculate_next_due rule = {"type": "calendar", "unit": "year", "day_of_month": 15, "month": 1} assert calculate_next_due(rule, date(2026, 3, 27)) == date(2027, 1, 15) @@ -248,15 +248,15 @@ async def test_spawn_recurring_tasks_creates_child(): mock_child.body = "" with ( - patch("fabledassistant.services.recurrence.async_session", return_value=mock_session), + patch("scribe.services.recurrence.async_session", return_value=mock_session), patch( - "fabledassistant.services.notes.create_note", + "scribe.services.notes.create_note", new_callable=AsyncMock, return_value=mock_child, ) as mock_create, - patch("fabledassistant.services.embeddings.upsert_note_embedding"), + patch("scribe.services.embeddings.upsert_note_embedding"), ): - from fabledassistant.services.recurrence import spawn_recurring_tasks + from scribe.services.recurrence import spawn_recurring_tasks count = await spawn_recurring_tasks() assert count == 1 @@ -282,8 +282,8 @@ async def test_list_notes_multi_status_builds_in_clause(): mock_session.__aenter__ = AsyncMock(return_value=mock_session) mock_session.__aexit__ = AsyncMock(return_value=False) - with patch("fabledassistant.services.notes.async_session", return_value=mock_session): - from fabledassistant.services.notes import list_notes + with patch("scribe.services.notes.async_session", return_value=mock_session): + from scribe.services.notes import list_notes notes, total = await list_notes(1, status=["todo", "in_progress"], is_task=True) assert total == 0 diff --git a/tests/test_routes_dashboard.py b/tests/test_routes_dashboard.py index e94d452..b2858e7 100644 --- a/tests/test_routes_dashboard.py +++ b/tests/test_routes_dashboard.py @@ -2,17 +2,17 @@ def test_dashboard_blueprint_registered(): - from fabledassistant.routes.dashboard import dashboard_bp + from scribe.routes.dashboard import dashboard_bp assert dashboard_bp.name == "dashboard" assert dashboard_bp.url_prefix == "/api/dashboard" def test_dashboard_blueprint_registered_in_app(): - from fabledassistant.app import create_app + from scribe.app import create_app app = create_app() assert "dashboard" in app.blueprints def test_dashboard_handler_callable(): - from fabledassistant.routes import dashboard as dashboard_routes + from scribe.routes import dashboard as dashboard_routes assert callable(dashboard_routes.get_dashboard) diff --git a/tests/test_routes_planning.py b/tests/test_routes_planning.py index 7c4e63b..7c9566c 100644 --- a/tests/test_routes_planning.py +++ b/tests/test_routes_planning.py @@ -2,11 +2,11 @@ import inspect def test_planning_handler_callable(): - from fabledassistant.routes import tasks as tasks_routes + from scribe.routes import tasks as tasks_routes assert callable(tasks_routes.start_planning_route) def test_planning_service_signature(): - from fabledassistant.services.planning import start_planning + from scribe.services.planning import start_planning params = inspect.signature(start_planning).parameters assert "user_id" in params and "project_id" in params and "title" in params diff --git a/tests/test_routes_rulebooks.py b/tests/test_routes_rulebooks.py index f647b00..9ee5d9e 100644 --- a/tests/test_routes_rulebooks.py +++ b/tests/test_routes_rulebooks.py @@ -9,19 +9,19 @@ import inspect def test_rulebooks_blueprint_registered(): - from fabledassistant.routes.rulebooks import rulebooks_bp + from scribe.routes.rulebooks import rulebooks_bp assert rulebooks_bp.name == "rulebooks" assert rulebooks_bp.url_prefix == "/api" def test_rulebooks_blueprint_registered_in_app(): - from fabledassistant.app import create_app + from scribe.app import create_app app = create_app() assert "rulebooks" in app.blueprints def test_rulebook_handlers_callable(): - from fabledassistant.routes import rulebooks as rb_routes + from scribe.routes import rulebooks as rb_routes for name in ( "list_rulebooks", "create_rulebook", "get_rulebook", "update_rulebook", "delete_rulebook", @@ -30,7 +30,7 @@ def test_rulebook_handlers_callable(): def test_topic_handlers_callable(): - from fabledassistant.routes import rulebooks as rb_routes + from scribe.routes import rulebooks as rb_routes for name in ( "list_topics", "create_topic", "update_topic", "delete_topic", ): @@ -39,7 +39,7 @@ def test_topic_handlers_callable(): def test_service_signatures_require_user_id(): """Routes must call services with user_id — verify the contract.""" - from fabledassistant.services import rulebooks as svc + from scribe.services import rulebooks as svc for fn_name in ( "create_rulebook", "list_rulebooks", "get_rulebook", "update_rulebook", "delete_rulebook", "find_rulebook_by_title", @@ -57,7 +57,7 @@ def test_service_signatures_require_user_id(): def test_rule_model_carries_project_id_and_topic_id_nullable(): """Migration 0059 made topic_id nullable and added project_id.""" - from fabledassistant.models.rulebook import Rule + from scribe.models.rulebook import Rule assert "project_id" in Rule.__table__.columns assert Rule.__table__.columns["topic_id"].nullable is True assert Rule.__table__.columns["project_id"].nullable is True @@ -65,13 +65,13 @@ def test_rule_model_carries_project_id_and_topic_id_nullable(): def test_create_project_rule_route_exists(): """POST /api/projects//rules — the frontend fast-path endpoint.""" - from fabledassistant.routes import rulebooks as rb_routes + from scribe.routes import rulebooks as rb_routes assert callable(getattr(rb_routes, "create_project_rule")) def test_suppression_route_handlers_exist(): """The 4 suppression endpoint handlers are registered as Python callables.""" - from fabledassistant.routes import rulebooks as rb_routes + from scribe.routes import rulebooks as rb_routes for name in ( "suppress_project_rule", "unsuppress_project_rule", "suppress_project_topic", "unsuppress_project_topic", @@ -83,7 +83,7 @@ def test_suppression_association_tables_declared(): """Migration 0060 created two new association tables; the models module must declare matching Table() objects so the rest of the service layer can reference them via .c..""" - from fabledassistant.models import rulebook as rb_models + from scribe.models import rulebook as rb_models for tbl_name in ("project_rule_suppressions", "project_topic_suppressions"): tbl = getattr(rb_models, tbl_name, None) assert tbl is not None, f"models.rulebook missing {tbl_name}" @@ -94,7 +94,7 @@ def test_suppression_association_tables_declared(): def test_rulebook_model_carries_always_on(): """Migration 0058 added rulebooks.always_on — verify the model declares it.""" - from fabledassistant.models.rulebook import Rulebook + from scribe.models.rulebook import Rulebook assert "always_on" in Rulebook.__table__.columns col = Rulebook.__table__.columns["always_on"] assert col.nullable is False @@ -107,13 +107,13 @@ def test_update_rulebook_route_accepts_always_on(): include always_on or toggling from the UI silently drops the field. """ import inspect as _inspect - from fabledassistant.routes import rulebooks as rb_routes + from scribe.routes import rulebooks as rb_routes src = _inspect.getsource(rb_routes.update_rulebook) assert "always_on" in src, "update_rulebook handler missing always_on in field whitelist" def test_rule_and_subscription_handlers_callable(): - from fabledassistant.routes import rulebooks as rb_routes + from scribe.routes import rulebooks as rb_routes for name in ( "list_rules", "create_rule", "get_rule", "update_rule", "delete_rule", "subscribe_project", "unsubscribe_project", "get_project_rules", diff --git a/tests/test_routes_tasks_kind.py b/tests/test_routes_tasks_kind.py index bd06220..9669e86 100644 --- a/tests/test_routes_tasks_kind.py +++ b/tests/test_routes_tasks_kind.py @@ -2,16 +2,16 @@ import inspect def test_create_note_accepts_task_kind(): - from fabledassistant.services.notes import create_note + from scribe.services.notes import create_note assert "task_kind" in inspect.signature(create_note).parameters def test_list_notes_accepts_task_kind(): - from fabledassistant.services.notes import list_notes + from scribe.services.notes import list_notes assert "task_kind" in inspect.signature(list_notes).parameters def test_tasks_blueprint_registered_in_app(): - from fabledassistant.app import create_app + from scribe.app import create_app app = create_app() assert "tasks" in app.blueprints diff --git a/tests/test_routes_trash.py b/tests/test_routes_trash.py index 6d17e1c..bb5ae22 100644 --- a/tests/test_routes_trash.py +++ b/tests/test_routes_trash.py @@ -3,30 +3,30 @@ import inspect def test_trash_blueprint_registered(): - from fabledassistant.routes.trash import trash_bp + from scribe.routes.trash import trash_bp assert trash_bp.name == "trash" assert trash_bp.url_prefix == "/api/trash" def test_trash_blueprint_registered_in_app(): - from fabledassistant.app import create_app + from scribe.app import create_app app = create_app() assert "trash" in app.blueprints def test_trash_handlers_callable(): - from fabledassistant.routes import trash as trash_routes + from scribe.routes import trash as trash_routes for name in ("list_trash", "restore_batch", "purge_batch"): assert callable(getattr(trash_routes, name)) def test_trash_service_surface(): - from fabledassistant.services import trash as svc + from scribe.services import trash as svc for fn_name in ("delete", "restore", "purge", "list_trash", "purge_expired", "alive"): assert hasattr(svc, fn_name), f"trash service missing {fn_name}" def test_delete_service_signature(): - from fabledassistant.services.trash import delete + from scribe.services.trash import delete params = inspect.signature(delete).parameters assert "user_id" in params and "entity_type" in params and "entity_id" in params diff --git a/tests/test_search_route.py b/tests/test_search_route.py index f82c108..61b6465 100644 --- a/tests/test_search_route.py +++ b/tests/test_search_route.py @@ -1,5 +1,5 @@ """Unit tests for the search route parameter mapping.""" -from fabledassistant.routes.search import _content_type_to_is_task +from scribe.routes.search import _content_type_to_is_task def test_content_type_note(): diff --git a/tests/test_services_dashboard.py b/tests/test_services_dashboard.py index e7fbdc8..c781f78 100644 --- a/tests/test_services_dashboard.py +++ b/tests/test_services_dashboard.py @@ -10,7 +10,7 @@ import pytest def test_task_row_maps_fields(): - from fabledassistant.services.dashboard import _task_row + from scribe.services.dashboard import _task_row n = MagicMock() n.id = 5 n.title = "Wire reminders" @@ -23,7 +23,7 @@ def test_task_row_maps_fields(): @pytest.mark.asyncio async def test_safe_returns_value_then_empty_on_error(): - from fabledassistant.services.dashboard import _safe + from scribe.services.dashboard import _safe async def ok(): return [1, 2, 3] @@ -39,7 +39,7 @@ async def test_safe_returns_value_then_empty_on_error(): @pytest.mark.asyncio async def test_build_dashboard_composes_sections(): - import fabledassistant.services.dashboard as dash + import scribe.services.dashboard as dash with patch.object(dash, "_active_projects", AsyncMock(return_value=["P"])), \ patch.object(dash, "_recently_completed", AsyncMock(return_value=["done"])), \ patch.object(dash, "_upcoming_events", AsyncMock(return_value=["evt"])), \ @@ -55,7 +55,7 @@ async def test_build_dashboard_composes_sections(): @pytest.mark.asyncio async def test_build_dashboard_isolates_failing_section(): - import fabledassistant.services.dashboard as dash + import scribe.services.dashboard as dash with patch.object(dash, "_active_projects", AsyncMock(side_effect=RuntimeError("db down"))), \ patch.object(dash, "_recently_completed", AsyncMock(return_value=["done"])), \ patch.object(dash, "_upcoming_events", AsyncMock(return_value=[])), \ diff --git a/tests/test_services_knowledge_counts.py b/tests/test_services_knowledge_counts.py index ae3a803..eb673b2 100644 --- a/tests/test_services_knowledge_counts.py +++ b/tests/test_services_knowledge_counts.py @@ -32,9 +32,9 @@ async def test_counts_include_process_in_facet_and_total(): _scalar(1), # tasks _scalar(0), # plans ]) - with patch("fabledassistant.services.knowledge.async_session") as cls: + with patch("scribe.services.knowledge.async_session") as cls: cls.return_value = session - from fabledassistant.services.knowledge import get_knowledge_counts + from scribe.services.knowledge import get_knowledge_counts counts = await get_knowledge_counts(user_id=1) assert counts["process"] == 2 diff --git a/tests/test_services_notes_process.py b/tests/test_services_notes_process.py index 037d903..1bd7fc4 100644 --- a/tests/test_services_notes_process.py +++ b/tests/test_services_notes_process.py @@ -35,9 +35,9 @@ async def test_resolve_process_by_numeric_id(): session = _make_mock_session() # numeric id → first execute (id lookup) hits session.execute = AsyncMock(side_effect=[_result(first=note)]) - with patch("fabledassistant.services.notes.async_session") as cls: + with patch("scribe.services.notes.async_session") as cls: cls.return_value = session - from fabledassistant.services.notes import resolve_process + from scribe.services.notes import resolve_process found, candidates = await resolve_process(1, "5") assert found is note assert candidates == [] @@ -50,9 +50,9 @@ async def test_resolve_process_exact_title_beats_substring(): session = _make_mock_session() # non-digit → exact-title query (first execute) hits; substring never runs session.execute = AsyncMock(side_effect=[_result(first=note)]) - with patch("fabledassistant.services.notes.async_session") as cls: + with patch("scribe.services.notes.async_session") as cls: cls.return_value = session - from fabledassistant.services.notes import resolve_process + from scribe.services.notes import resolve_process found, candidates = await resolve_process(1, "Drift Audit") assert found is note assert candidates == [] @@ -66,9 +66,9 @@ async def test_resolve_process_substring_returns_candidates(): session = _make_mock_session() # exact miss, then substring returns two (most-recent first) session.execute = AsyncMock(side_effect=[_result(first=None), _result(all_=[n1, n2])]) - with patch("fabledassistant.services.notes.async_session") as cls: + with patch("scribe.services.notes.async_session") as cls: cls.return_value = session - from fabledassistant.services.notes import resolve_process + from scribe.services.notes import resolve_process found, candidates = await resolve_process(1, "drift") assert found is n1 assert candidates == [{"id": 9, "title": "Drift Audit Notes"}] @@ -79,9 +79,9 @@ async def test_resolve_process_substring_returns_candidates(): async def test_resolve_process_no_match(): session = _make_mock_session() session.execute = AsyncMock(side_effect=[_result(first=None), _result(all_=[])]) - with patch("fabledassistant.services.notes.async_session") as cls: + with patch("scribe.services.notes.async_session") as cls: cls.return_value = session - from fabledassistant.services.notes import resolve_process + from scribe.services.notes import resolve_process found, candidates = await resolve_process(1, "nope") assert found is None assert candidates == [] diff --git a/tests/test_services_notes_task_kind.py b/tests/test_services_notes_task_kind.py index d1eeb12..7986b40 100644 --- a/tests/test_services_notes_task_kind.py +++ b/tests/test_services_notes_task_kind.py @@ -23,10 +23,10 @@ async def test_create_note_passes_task_kind_to_model(): captured["task_kind"] = getattr(obj, "task_kind", "MISSING") mock_session.add = MagicMock(side_effect=_capture_add) - with patch("fabledassistant.services.notes.async_session") as mock_cls, \ - patch("fabledassistant.services.notes._maybe_reactivate_project", AsyncMock()): + with patch("scribe.services.notes.async_session") as mock_cls, \ + patch("scribe.services.notes._maybe_reactivate_project", AsyncMock()): mock_cls.return_value = mock_session - from fabledassistant.services.notes import create_note + from scribe.services.notes import create_note await create_note(user_id=1, title="P", status="todo", task_kind="plan") assert captured["task_kind"] == "plan" @@ -38,9 +38,9 @@ async def test_list_notes_filters_by_task_kind(): exec_result = MagicMock() exec_result.scalars.return_value.all.return_value = [] mock_session.execute = AsyncMock(return_value=exec_result) - with patch("fabledassistant.services.notes.async_session") as mock_cls: + with patch("scribe.services.notes.async_session") as mock_cls: mock_cls.return_value = mock_session - from fabledassistant.services.notes import list_notes + from scribe.services.notes import list_notes # Should not raise; task_kind is a recognized kwarg. rows, total = await list_notes(user_id=1, is_task=True, task_kind="plan") assert rows == [] diff --git a/tests/test_services_planning.py b/tests/test_services_planning.py index b831f7a..a483ee5 100644 --- a/tests/test_services_planning.py +++ b/tests/test_services_planning.py @@ -13,15 +13,15 @@ async def test_start_planning_creates_plan_task_and_returns_rules(): "truncated": False, "subscribed_rulebooks": [{"id": 2, "title": "FabledSword family"}], } - with patch("fabledassistant.services.planning.notes_svc.create_note", + with patch("scribe.services.planning.notes_svc.create_note", AsyncMock(return_value=fake_note)) as mock_create, \ - patch("fabledassistant.services.planning.rulebooks_svc.get_applicable_rules", + patch("scribe.services.planning.rulebooks_svc.get_applicable_rules", AsyncMock(return_value=applicable)), \ - patch("fabledassistant.services.planning.notes_svc.list_notes", + patch("scribe.services.planning.notes_svc.list_notes", AsyncMock(return_value=([], 3))), \ - patch("fabledassistant.services.planning.projects_svc.get_project", + patch("scribe.services.planning.projects_svc.get_project", AsyncMock(return_value=MagicMock(goal="ship it"))): - from fabledassistant.services.planning import start_planning + from scribe.services.planning import start_planning out = await start_planning(user_id=7, project_id=3, title="Plan it") # Created a plan-task (status set => task, kind=plan) @@ -39,8 +39,8 @@ async def test_start_planning_creates_plan_task_and_returns_rules(): @pytest.mark.asyncio async def test_start_planning_raises_when_project_not_found(): - with patch("fabledassistant.services.planning.projects_svc.get_project", + with patch("scribe.services.planning.projects_svc.get_project", AsyncMock(return_value=None)): - from fabledassistant.services.planning import start_planning + from scribe.services.planning import start_planning with pytest.raises(ValueError, match="project 999 not found"): await start_planning(user_id=7, project_id=999, title="x") diff --git a/tests/test_services_rulebooks.py b/tests/test_services_rulebooks.py index 45e5605..4517f48 100644 --- a/tests/test_services_rulebooks.py +++ b/tests/test_services_rulebooks.py @@ -36,9 +36,9 @@ def _fake_rulebook(id=1, owner_user_id=7, title="FabledSword family", descriptio @pytest.mark.asyncio async def test_create_rulebook_stores_to_db(): mock_session = _make_mock_session() - with patch("fabledassistant.services.rulebooks.async_session") as mock_cls: + with patch("scribe.services.rulebooks.async_session") as mock_cls: mock_cls.return_value = mock_session - from fabledassistant.services.rulebooks import create_rulebook + from scribe.services.rulebooks import create_rulebook await create_rulebook( user_id=7, title="FabledSword family", description="rules for the family", ) @@ -53,9 +53,9 @@ async def test_list_rulebooks_returns_owned_only(): mock_result = MagicMock() mock_result.scalars.return_value.all.return_value = [rb] mock_session.execute = AsyncMock(return_value=mock_result) - with patch("fabledassistant.services.rulebooks.async_session") as mock_cls: + with patch("scribe.services.rulebooks.async_session") as mock_cls: mock_cls.return_value = mock_session - from fabledassistant.services.rulebooks import list_rulebooks + from scribe.services.rulebooks import list_rulebooks results = await list_rulebooks(user_id=7) assert len(results) == 1 @@ -67,9 +67,9 @@ async def test_get_rulebook_returns_none_when_not_owner(): mock_result = MagicMock() mock_result.scalar_one_or_none.return_value = None mock_session.execute = AsyncMock(return_value=mock_result) - with patch("fabledassistant.services.rulebooks.async_session") as mock_cls: + with patch("scribe.services.rulebooks.async_session") as mock_cls: mock_cls.return_value = mock_session - from fabledassistant.services.rulebooks import get_rulebook + from scribe.services.rulebooks import get_rulebook result = await get_rulebook(rulebook_id=1, user_id=99) assert result is None @@ -81,9 +81,9 @@ async def test_update_rulebook_only_sets_provided_fields(): mock_result = MagicMock() mock_result.scalar_one_or_none.return_value = rb mock_session.execute = AsyncMock(return_value=mock_result) - with patch("fabledassistant.services.rulebooks.async_session") as mock_cls: + with patch("scribe.services.rulebooks.async_session") as mock_cls: mock_cls.return_value = mock_session - from fabledassistant.services.rulebooks import update_rulebook + from scribe.services.rulebooks import update_rulebook await update_rulebook(rulebook_id=1, user_id=7, title="new") assert rb.title == "new" @@ -96,9 +96,9 @@ async def test_delete_rulebook_calls_delete(): mock_result.scalar_one_or_none.return_value = rb mock_session.execute = AsyncMock(return_value=mock_result) mock_session.delete = AsyncMock() - with patch("fabledassistant.services.rulebooks.async_session") as mock_cls: + with patch("scribe.services.rulebooks.async_session") as mock_cls: mock_cls.return_value = mock_session - from fabledassistant.services.rulebooks import delete_rulebook + from scribe.services.rulebooks import delete_rulebook await delete_rulebook(rulebook_id=1, user_id=7) assert mock_session.delete.called @@ -128,9 +128,9 @@ async def test_create_topic_requires_owned_rulebook(): mock_result = MagicMock() mock_result.scalar_one_or_none.return_value = None mock_session.execute = AsyncMock(return_value=mock_result) - with patch("fabledassistant.services.rulebooks.async_session") as mock_cls: + with patch("scribe.services.rulebooks.async_session") as mock_cls: mock_cls.return_value = mock_session - from fabledassistant.services.rulebooks import create_topic + from scribe.services.rulebooks import create_topic with pytest.raises(ValueError, match="not found"): await create_topic( rulebook_id=999, user_id=7, title="git-workflow", @@ -150,9 +150,9 @@ async def test_list_topics_returns_topics_for_owned_rulebook(): topic_result.scalars.return_value.all.return_value = [topic] mock_session.execute = AsyncMock(side_effect=[rb_result, topic_result]) - with patch("fabledassistant.services.rulebooks.async_session") as mock_cls: + with patch("scribe.services.rulebooks.async_session") as mock_cls: mock_cls.return_value = mock_session - from fabledassistant.services.rulebooks import list_topics + from scribe.services.rulebooks import list_topics results = await list_topics(rulebook_id=1, user_id=7) assert len(results) == 1 assert results[0].title == "git-workflow" @@ -186,9 +186,9 @@ async def test_create_rule_requires_owned_topic(): mock_result = MagicMock() mock_result.scalar_one_or_none.return_value = None # topic not found mock_session.execute = AsyncMock(return_value=mock_result) - with patch("fabledassistant.services.rulebooks.async_session") as mock_cls: + with patch("scribe.services.rulebooks.async_session") as mock_cls: mock_cls.return_value = mock_session - from fabledassistant.services.rulebooks import create_rule + from scribe.services.rulebooks import create_rule with pytest.raises(ValueError, match="topic .* not found"): await create_rule( topic_id=999, user_id=7, title="x", statement="y", @@ -203,9 +203,9 @@ async def test_list_rules_filters_by_topic_id(): mock_result = MagicMock() mock_result.scalars.return_value.all.return_value = [rule] mock_session.execute = AsyncMock(return_value=mock_result) - with patch("fabledassistant.services.rulebooks.async_session") as mock_cls: + with patch("scribe.services.rulebooks.async_session") as mock_cls: mock_cls.return_value = mock_session - from fabledassistant.services.rulebooks import list_rules + from scribe.services.rulebooks import list_rules results = await list_rules(user_id=7, topic_id=10) assert len(results) == 1 @@ -216,9 +216,9 @@ async def test_get_rule_returns_none_when_not_owner(): mock_result = MagicMock() mock_result.scalar_one_or_none.return_value = None mock_session.execute = AsyncMock(return_value=mock_result) - with patch("fabledassistant.services.rulebooks.async_session") as mock_cls: + with patch("scribe.services.rulebooks.async_session") as mock_cls: mock_cls.return_value = mock_session - from fabledassistant.services.rulebooks import get_rule + from scribe.services.rulebooks import get_rule result = await get_rule(rule_id=1, user_id=99) assert result is None @@ -232,9 +232,9 @@ async def test_subscribe_project_requires_owned_rulebook(): mock_result = MagicMock() mock_result.scalar_one_or_none.return_value = None mock_session.execute = AsyncMock(return_value=mock_result) - with patch("fabledassistant.services.rulebooks.async_session") as mock_cls: + with patch("scribe.services.rulebooks.async_session") as mock_cls: mock_cls.return_value = mock_session - from fabledassistant.services.rulebooks import subscribe_project + from scribe.services.rulebooks import subscribe_project with pytest.raises(ValueError, match="not found"): await subscribe_project( project_id=1, rulebook_id=999, user_id=7, @@ -267,9 +267,9 @@ async def test_get_applicable_rules_returns_shape(): sub_result, _empty(), _empty(), rules_result, _empty(), ]) - with patch("fabledassistant.services.rulebooks.async_session") as mock_cls: + with patch("scribe.services.rulebooks.async_session") as mock_cls: mock_cls.return_value = mock_session - from fabledassistant.services.rulebooks import get_applicable_rules + from scribe.services.rulebooks import get_applicable_rules result = await get_applicable_rules(project_id=3, user_id=7, limit=50) assert "rules" in result @@ -302,9 +302,9 @@ async def test_get_applicable_rules_truncates_when_over_limit(): sub_result, _empty(), _empty(), rules_result, _empty(), ]) - with patch("fabledassistant.services.rulebooks.async_session") as mock_cls: + with patch("scribe.services.rulebooks.async_session") as mock_cls: mock_cls.return_value = mock_session - from fabledassistant.services.rulebooks import get_applicable_rules + from scribe.services.rulebooks import get_applicable_rules result = await get_applicable_rules(project_id=3, user_id=7, limit=50) assert result["truncated"] is True @@ -324,9 +324,9 @@ async def test_get_applicable_rules_includes_project_scoped_rules(): _empty(), _empty(), _empty(), _empty(), proj_rules_result, ]) - with patch("fabledassistant.services.rulebooks.async_session") as mock_cls: + with patch("scribe.services.rulebooks.async_session") as mock_cls: mock_cls.return_value = mock_session - from fabledassistant.services.rulebooks import get_applicable_rules + from scribe.services.rulebooks import get_applicable_rules result = await get_applicable_rules(project_id=3, user_id=7) assert len(result["project_rules"]) == 2 @@ -353,9 +353,9 @@ async def test_get_applicable_rules_surfaces_suppressed_with_context(): _empty(), suppressed_rules_result, suppressed_topics_result, _empty(), _empty(), ]) - with patch("fabledassistant.services.rulebooks.async_session") as mock_cls: + with patch("scribe.services.rulebooks.async_session") as mock_cls: mock_cls.return_value = mock_session - from fabledassistant.services.rulebooks import get_applicable_rules + from scribe.services.rulebooks import get_applicable_rules result = await get_applicable_rules(project_id=3, user_id=7) assert len(result["suppressed_rules"]) == 1 diff --git a/tests/test_services_trash.py b/tests/test_services_trash.py index d218aa3..cb169ba 100644 --- a/tests/test_services_trash.py +++ b/tests/test_services_trash.py @@ -26,9 +26,9 @@ async def test_delete_note_returns_batch_and_commits(): no_children = MagicMock() no_children.scalars.return_value.all.return_value = [] session.execute = AsyncMock(side_effect=[_exists_result(True), no_children, MagicMock()]) - with patch("fabledassistant.services.trash.async_session") as cls: + with patch("scribe.services.trash.async_session") as cls: cls.return_value = session - from fabledassistant.services.trash import delete + from scribe.services.trash import delete batch = await delete(user_id=1, entity_type="note", entity_id=5) assert isinstance(batch, str) and len(batch) > 0 assert session.commit.called @@ -40,9 +40,9 @@ async def test_delete_note_returns_batch_and_commits(): async def test_delete_returns_none_when_not_found(): session = _make_mock_session() session.execute = AsyncMock(return_value=_exists_result(False)) - with patch("fabledassistant.services.trash.async_session") as cls: + with patch("scribe.services.trash.async_session") as cls: cls.return_value = session - from fabledassistant.services.trash import delete + from scribe.services.trash import delete batch = await delete(user_id=1, entity_type="task", entity_id=999) assert batch is None assert not session.commit.called @@ -63,9 +63,9 @@ async def test_delete_project_cascades_to_notes_milestones_project_rules_and_sup MagicMock(), MagicMock(), MagicMock(), ]) - with patch("fabledassistant.services.trash.async_session") as cls: + with patch("scribe.services.trash.async_session") as cls: cls.return_value = session - from fabledassistant.services.trash import delete + from scribe.services.trash import delete batch = await delete(user_id=1, entity_type="project", entity_id=3) assert isinstance(batch, str) assert session.execute.await_count == 7 @@ -80,9 +80,9 @@ async def test_delete_rulebook_cascades_topics_and_rules(): session.execute = AsyncMock(side_effect=[ _exists_result(True), topic_ids_result, MagicMock(), MagicMock(), MagicMock(), ]) - with patch("fabledassistant.services.trash.async_session") as cls: + with patch("scribe.services.trash.async_session") as cls: cls.return_value = session - from fabledassistant.services.trash import delete + from scribe.services.trash import delete batch = await delete(user_id=7, entity_type="rulebook", entity_id=2) assert isinstance(batch, str) assert session.execute.await_count == 5 @@ -99,9 +99,9 @@ async def test_restore_clears_batch_across_all_models(): session = _make_mock_session() # 7 models, each returns a rowcount session.execute = AsyncMock(side_effect=[_rowcount_result(i) for i in [2, 0, 1, 1, 0, 0, 0]]) - with patch("fabledassistant.services.trash.async_session") as cls: + with patch("scribe.services.trash.async_session") as cls: cls.return_value = session - from fabledassistant.services.trash import restore + from scribe.services.trash import restore n = await restore(user_id=1, batch_id="abc") assert n == 4 assert session.execute.await_count == 7 @@ -112,9 +112,9 @@ async def test_restore_clears_batch_across_all_models(): async def test_purge_expired_skips_when_retention_zero(): session = _make_mock_session() session.execute = AsyncMock() - with patch("fabledassistant.services.trash.async_session") as cls: + with patch("scribe.services.trash.async_session") as cls: cls.return_value = session - from fabledassistant.services.trash import purge_expired + from scribe.services.trash import purge_expired n = await purge_expired(1, 0) assert n == 0 assert not session.execute.called # never opens a delete @@ -124,9 +124,9 @@ async def test_purge_expired_skips_when_retention_zero(): async def test_purge_expired_deletes_across_models_when_positive(): session = _make_mock_session() session.execute = AsyncMock(side_effect=[_rowcount_result(1) for _ in range(7)]) - with patch("fabledassistant.services.trash.async_session") as cls: + with patch("scribe.services.trash.async_session") as cls: cls.return_value = session - from fabledassistant.services.trash import purge_expired + from scribe.services.trash import purge_expired n = await purge_expired(1, 90) assert n == 7 assert session.execute.await_count == 7 @@ -135,9 +135,9 @@ async def test_purge_expired_deletes_across_models_when_positive(): def test_owner_clause_scopes_every_model(): """Regression: every trash op must owner-scope, including topics/rules which previously had NO owner check (IDOR across tenants).""" - from fabledassistant.services.trash import _owner_clause, _MODEL_FOR - from fabledassistant.models.note import Note - from fabledassistant.models.rulebook import Rulebook, RulebookTopic, Rule + from scribe.services.trash import _owner_clause, _MODEL_FOR + from scribe.models.note import Note + from scribe.models.rulebook import Rulebook, RulebookTopic, Rule # Models with a direct owner column. assert "user_id" in str(_owner_clause(Note, 7)) @@ -172,9 +172,9 @@ async def test_list_trash_groups_by_batch(): empty = MagicMock() empty.scalars.return_value.all.return_value = [] session.execute = AsyncMock(side_effect=[note_rows] + [empty] * 6) - with patch("fabledassistant.services.trash.async_session") as cls: + with patch("scribe.services.trash.async_session") as cls: cls.return_value = session - from fabledassistant.services.trash import list_trash + from scribe.services.trash import list_trash out = await list_trash(user_id=1) assert len(out) == 1 assert out[0]["batch_id"] == "b1" diff --git a/tests/test_trash_filtering.py b/tests/test_trash_filtering.py index feb9061..c011645 100644 --- a/tests/test_trash_filtering.py +++ b/tests/test_trash_filtering.py @@ -39,9 +39,9 @@ def _has_filter(captured: list[str]) -> bool: @pytest.mark.asyncio async def test_get_note_excludes_trashed(): cap: list[str] = [] - with patch("fabledassistant.services.notes.async_session") as cls: + with patch("scribe.services.notes.async_session") as cls: cls.return_value = _capturing_session(cap) - from fabledassistant.services.notes import get_note + from scribe.services.notes import get_note await get_note(1, 5) assert _has_filter(cap) @@ -49,9 +49,9 @@ async def test_get_note_excludes_trashed(): @pytest.mark.asyncio async def test_list_notes_excludes_trashed(): cap: list[str] = [] - with patch("fabledassistant.services.notes.async_session") as cls: + with patch("scribe.services.notes.async_session") as cls: cls.return_value = _capturing_session(cap) - from fabledassistant.services.notes import list_notes + from scribe.services.notes import list_notes await list_notes(1) assert _has_filter(cap) @@ -59,9 +59,9 @@ async def test_list_notes_excludes_trashed(): @pytest.mark.asyncio async def test_list_projects_excludes_trashed(): cap: list[str] = [] - with patch("fabledassistant.services.projects.async_session") as cls: + with patch("scribe.services.projects.async_session") as cls: cls.return_value = _capturing_session(cap) - from fabledassistant.services.projects import list_projects + from scribe.services.projects import list_projects await list_projects(1) assert _has_filter(cap) @@ -69,9 +69,9 @@ async def test_list_projects_excludes_trashed(): @pytest.mark.asyncio async def test_list_events_excludes_trashed(): cap: list[str] = [] - with patch("fabledassistant.services.events.async_session") as cls: + with patch("scribe.services.events.async_session") as cls: cls.return_value = _capturing_session(cap) - from fabledassistant.services.events import list_events + from scribe.services.events import list_events await list_events( 1, datetime(2026, 5, 1, tzinfo=timezone.utc), @@ -83,9 +83,9 @@ async def test_list_events_excludes_trashed(): @pytest.mark.asyncio async def test_query_knowledge_excludes_trashed(): cap: list[str] = [] - with patch("fabledassistant.services.knowledge.async_session") as cls: + with patch("scribe.services.knowledge.async_session") as cls: cls.return_value = _capturing_session(cap) - from fabledassistant.services.knowledge import query_knowledge + from scribe.services.knowledge import query_knowledge await query_knowledge(user_id=1, note_type=None, tags=[], sort="modified", q=None, limit=10, offset=0) assert _has_filter(cap) @@ -93,8 +93,8 @@ async def test_query_knowledge_excludes_trashed(): @pytest.mark.asyncio async def test_get_applicable_rules_excludes_trashed(): cap: list[str] = [] - with patch("fabledassistant.services.rulebooks.async_session") as cls: + with patch("scribe.services.rulebooks.async_session") as cls: cls.return_value = _capturing_session(cap) - from fabledassistant.services.rulebooks import get_applicable_rules + from scribe.services.rulebooks import get_applicable_rules await get_applicable_rules(project_id=3, user_id=1) assert _has_filter(cap) diff --git a/tests/test_tz_helpers.py b/tests/test_tz_helpers.py index a5d37c4..d13c006 100644 --- a/tests/test_tz_helpers.py +++ b/tests/test_tz_helpers.py @@ -9,7 +9,7 @@ import pytest @pytest.mark.asyncio async def test_get_user_tz_returns_configured_zone(): - from fabledassistant.services import tz as tz_mod + from scribe.services import tz as tz_mod with patch.object(tz_mod, "get_setting", AsyncMock(return_value="America/New_York")): zone = await tz_mod.get_user_tz(1) @@ -18,7 +18,7 @@ async def test_get_user_tz_returns_configured_zone(): @pytest.mark.asyncio async def test_get_user_tz_falls_back_to_utc_on_bad_input(): - from fabledassistant.services import tz as tz_mod + from scribe.services import tz as tz_mod with patch.object(tz_mod, "get_setting", AsyncMock(return_value="Not/AZone")): zone = await tz_mod.get_user_tz(1) @@ -28,7 +28,7 @@ async def test_get_user_tz_falls_back_to_utc_on_bad_input(): @pytest.mark.asyncio async def test_user_day_date_before_4am_returns_yesterday(): """00:00–03:59 local still shows yesterday's day_date.""" - from fabledassistant.services import tz as tz_mod + from scribe.services import tz as tz_mod ny = ZoneInfo("America/New_York") # 02:00 NY on 2026-04-13 → day_date = 2026-04-12 (still pre-rollover) @@ -47,7 +47,7 @@ async def test_user_day_date_before_4am_returns_yesterday(): @pytest.mark.asyncio async def test_user_day_date_after_4am_returns_today(): - from fabledassistant.services import tz as tz_mod + from scribe.services import tz as tz_mod ny = ZoneInfo("America/New_York") fake_now = datetime(2026, 4, 13, 4, 30, tzinfo=ny) diff --git a/tests/test_utils.py b/tests/test_utils.py index 2b496ce..1055aec 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -2,7 +2,7 @@ Unit tests for pure utility functions — no database or network required. """ import pytest -from fabledassistant.routes.export import _safe_filename +from scribe.routes.export import _safe_filename class TestSafeFilename: diff --git a/tests/test_version_pinning_pin_unpin.py b/tests/test_version_pinning_pin_unpin.py index d018640..8fab843 100644 --- a/tests/test_version_pinning_pin_unpin.py +++ b/tests/test_version_pinning_pin_unpin.py @@ -20,10 +20,10 @@ async def test_pin_version_sets_manual_kind_and_label(): mock_version.pin_label = None with patch( - "fabledassistant.services.version_pinning.async_session", + "scribe.services.version_pinning.async_session", return_value=_mock_session_for_version(mock_version), ): - from fabledassistant.services.version_pinning import pin_version + from scribe.services.version_pinning import pin_version result = await pin_version( user_id=1, note_id=42, version_id=7, label="the runbook circa Q2", ) @@ -40,10 +40,10 @@ async def test_pin_version_accepts_null_label(): mock_version.pin_label = None with patch( - "fabledassistant.services.version_pinning.async_session", + "scribe.services.version_pinning.async_session", return_value=_mock_session_for_version(mock_version), ): - from fabledassistant.services.version_pinning import pin_version + from scribe.services.version_pinning import pin_version await pin_version(user_id=1, note_id=42, version_id=7, label=None) assert mock_version.pin_kind == "manual" @@ -57,10 +57,10 @@ async def test_pin_version_promotes_auto_to_manual_with_label_update(): mock_version.pin_label = "stable 2026-04-01 → 2026-04-05" with patch( - "fabledassistant.services.version_pinning.async_session", + "scribe.services.version_pinning.async_session", return_value=_mock_session_for_version(mock_version), ): - from fabledassistant.services.version_pinning import pin_version + from scribe.services.version_pinning import pin_version await pin_version( user_id=1, note_id=42, version_id=7, label="post-network-refresh", ) @@ -78,10 +78,10 @@ async def test_pin_version_rejects_overlong_label(): # even be entered. We still patch it to avoid accidental DB lookups # if the implementation order changes. with patch( - "fabledassistant.services.version_pinning.async_session", + "scribe.services.version_pinning.async_session", return_value=_mock_session_for_version(mock_version), ): - from fabledassistant.services.version_pinning import pin_version + from scribe.services.version_pinning import pin_version try: await pin_version( user_id=1, note_id=42, version_id=7, label="x" * 501, @@ -102,10 +102,10 @@ async def test_pin_version_returns_none_when_not_found(): mock_session.__aexit__ = AsyncMock(return_value=False) with patch( - "fabledassistant.services.version_pinning.async_session", + "scribe.services.version_pinning.async_session", return_value=mock_session, ): - from fabledassistant.services.version_pinning import pin_version + from scribe.services.version_pinning import pin_version out = await pin_version(user_id=1, note_id=42, version_id=99, label=None) assert out is None @@ -117,10 +117,10 @@ async def test_unpin_version_clears_kind_and_label(): mock_version.pin_label = "previous label" with patch( - "fabledassistant.services.version_pinning.async_session", + "scribe.services.version_pinning.async_session", return_value=_mock_session_for_version(mock_version), ): - from fabledassistant.services.version_pinning import unpin_version + from scribe.services.version_pinning import unpin_version result = await unpin_version(user_id=1, note_id=42, version_id=7) assert result is mock_version diff --git a/tests/test_version_pinning_prune.py b/tests/test_version_pinning_prune.py index c6e1ca5..a7fbe86 100644 --- a/tests/test_version_pinning_prune.py +++ b/tests/test_version_pinning_prune.py @@ -32,10 +32,10 @@ async def test_create_version_prune_sql_filters_to_unpinned(): mock_session.execute = AsyncMock(side_effect=execute_capture) with patch( - "fabledassistant.services.note_versions.async_session", + "scribe.services.note_versions.async_session", return_value=mock_session, ): - from fabledassistant.services.note_versions import create_version + from scribe.services.note_versions import create_version await create_version( user_id=1, note_id=42, body="content", title="t", tags=["a"], ) @@ -67,10 +67,10 @@ async def test_prune_auto_pins_filters_to_auto_kind(): mock_session.execute = AsyncMock(side_effect=execute_capture) with patch( - "fabledassistant.services.version_pinning.async_session", + "scribe.services.version_pinning.async_session", return_value=mock_session, ): - from fabledassistant.services.version_pinning import ( + from scribe.services.version_pinning import ( prune_auto_pins, MAX_AUTO_PINS, ) await prune_auto_pins(user_id=1, note_id=42) diff --git a/tests/test_version_pinning_scan.py b/tests/test_version_pinning_scan.py index 772ec87..4ceea6e 100644 --- a/tests/test_version_pinning_scan.py +++ b/tests/test_version_pinning_scan.py @@ -19,7 +19,7 @@ def _v(version_id: int, days_ago: int, pin_kind=None): def test_promote_stable_versions_pins_versions_with_2_day_gap(): """Versions followed by another version >= 2 days later get promoted.""" - from fabledassistant.services.version_pinning import ( + from scribe.services.version_pinning import ( _promote_stable_versions_for_note, ) # 3 versions: v1 (10 days ago), v2 (5 days ago), v3 (1 day ago) @@ -35,7 +35,7 @@ def test_promote_stable_versions_pins_versions_with_2_day_gap(): def test_promote_stable_versions_pins_latest_if_old_enough(): """If the latest version is >= 2 days old with no successor, pin it.""" - from fabledassistant.services.version_pinning import ( + from scribe.services.version_pinning import ( _promote_stable_versions_for_note, ) versions = [_v(1, 10), _v(2, 3)] # latest is 3 days old @@ -44,7 +44,7 @@ def test_promote_stable_versions_pins_latest_if_old_enough(): def test_promote_stable_versions_skips_already_pinned(): - from fabledassistant.services.version_pinning import ( + from scribe.services.version_pinning import ( _promote_stable_versions_for_note, ) versions = [_v(1, 10, pin_kind="manual"), _v(2, 5)] @@ -67,7 +67,7 @@ def test_promote_stable_versions_skips_active_editing(): id=2, created_at=base - timedelta(hours=2), pin_kind=None, pin_label=None, ) - from fabledassistant.services.version_pinning import ( + from scribe.services.version_pinning import ( _promote_stable_versions_for_note, ) pinned = _promote_stable_versions_for_note([v1, v2]) @@ -76,7 +76,7 @@ def test_promote_stable_versions_skips_active_editing(): def test_promote_stable_versions_writes_descriptive_label(): """The auto-generated label references stability.""" - from fabledassistant.services.version_pinning import ( + from scribe.services.version_pinning import ( _promote_stable_versions_for_note, ) versions = [_v(1, 10), _v(2, 5), _v(3, 1)] @@ -94,7 +94,7 @@ def test_promote_stable_versions_naive_datetime_is_treated_as_utc(): SimpleNamespace(id=1, created_at=naive_old, pin_kind=None, pin_label=None), SimpleNamespace(id=2, created_at=naive_recent, pin_kind=None, pin_label=None), ] - from fabledassistant.services.version_pinning import ( + from scribe.services.version_pinning import ( _promote_stable_versions_for_note, ) pinned = _promote_stable_versions_for_note(versions) @@ -105,7 +105,7 @@ def test_promote_stable_versions_naive_datetime_is_treated_as_utc(): async def test_scan_user_for_auto_pins_iterates_all_notes(monkeypatch): """scan_user_for_auto_pins iterates every note for the user and calls the per-note flow on each, summing returned counts.""" - from fabledassistant.services import version_pinning + from scribe.services import version_pinning note_ids = [10, 20, 30] seen: list[int] = [] @@ -136,7 +136,7 @@ async def test_scan_user_for_auto_pins_iterates_all_notes(monkeypatch): async def test_scan_user_for_auto_pins_swallows_per_note_errors(monkeypatch): """One bad note doesn't stop the scan; the error is logged and the others continue.""" - from fabledassistant.services import version_pinning + from scribe.services import version_pinning async def fake_list_note_ids(uid): return [10, 20, 30] diff --git a/uv.lock b/uv.lock index 76e38e6..8cd7f5c 100644 --- a/uv.lock +++ b/uv.lock @@ -2,15 +2,6 @@ version = 1 revision = 3 requires-python = ">=3.14" -[[package]] -name = "addict" -version = "2.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/85/ef/fd7649da8af11d93979831e8f1f8097e85e82d5bfeabc8c68b39175d8e75/addict-2.4.0.tar.gz", hash = "sha256:b3b2210e0e067a281f5646c8c5db92e99b7231ea8b0eb5f74dbdf9e259d4e494", size = 9186, upload-time = "2020-11-21T16:21:31.416Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/00/b08f23b7d7e1e14ce01419a467b583edbb93c6cdb8654e54a9cc579cd61f/addict-2.4.0-py3-none-any.whl", hash = "sha256:249bb56bbfd3cdc2a004ea0ff4c2b6ddc84d53bc2194761636eb314d5cfa5dfc", size = 3832, upload-time = "2020-11-21T16:21:29.588Z" }, -] - [[package]] name = "aiofiles" version = "25.1.0" @@ -20,78 +11,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/bc/8a/340a1555ae33d7354dbca4faa54948d76d89a27ceef032c8c3bc661d003e/aiofiles-25.1.0-py3-none-any.whl", hash = "sha256:abe311e527c862958650f9438e859c1fa7568a141b22abcd015e120e86a85695", size = 14668, upload-time = "2025-10-09T20:51:03.174Z" }, ] -[[package]] -name = "aiohappyeyeballs" -version = "2.6.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760, upload-time = "2025-03-12T01:42:48.764Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265, upload-time = "2025-03-12T01:42:47.083Z" }, -] - -[[package]] -name = "aiohttp" -version = "3.13.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiohappyeyeballs" }, - { name = "aiosignal" }, - { name = "attrs" }, - { name = "frozenlist" }, - { name = "multidict" }, - { name = "propcache" }, - { name = "yarl" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/77/9a/152096d4808df8e4268befa55fba462f440f14beab85e8ad9bf990516918/aiohttp-3.13.5.tar.gz", hash = "sha256:9d98cc980ecc96be6eb4c1994ce35d28d8b1f5e5208a23b421187d1209dbb7d1", size = 7858271, upload-time = "2026-03-31T22:01:03.343Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5d/ce/46572759afc859e867a5bc8ec3487315869013f59281ce61764f76d879de/aiohttp-3.13.5-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:eb4639f32fd4a9904ab8fb45bf3383ba71137f3d9d4ba25b3b3f3109977c5b8c", size = 745721, upload-time = "2026-03-31T21:58:50.229Z" }, - { url = "https://files.pythonhosted.org/packages/13/fe/8a2efd7626dbe6049b2ef8ace18ffda8a4dfcbe1bcff3ac30c0c7575c20b/aiohttp-3.13.5-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:7e5dc4311bd5ac493886c63cbf76ab579dbe4641268e7c74e48e774c74b6f2be", size = 497663, upload-time = "2026-03-31T21:58:52.232Z" }, - { url = "https://files.pythonhosted.org/packages/9b/91/cc8cc78a111826c54743d88651e1687008133c37e5ee615fee9b57990fac/aiohttp-3.13.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:756c3c304d394977519824449600adaf2be0ccee76d206ee339c5e76b70ded25", size = 499094, upload-time = "2026-03-31T21:58:54.566Z" }, - { url = "https://files.pythonhosted.org/packages/0a/33/a8362cb15cf16a3af7e86ed11962d5cd7d59b449202dc576cdc731310bde/aiohttp-3.13.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecc26751323224cf8186efcf7fbcbc30f4e1d8c7970659daf25ad995e4032a56", size = 1726701, upload-time = "2026-03-31T21:58:56.864Z" }, - { url = "https://files.pythonhosted.org/packages/45/0c/c091ac5c3a17114bd76cbf85d674650969ddf93387876cf67f754204bd77/aiohttp-3.13.5-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:10a75acfcf794edf9d8db50e5a7ec5fc818b2a8d3f591ce93bc7b1210df016d2", size = 1683360, upload-time = "2026-03-31T21:58:59.072Z" }, - { url = "https://files.pythonhosted.org/packages/23/73/bcee1c2b79bc275e964d1446c55c54441a461938e70267c86afaae6fba27/aiohttp-3.13.5-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0f7a18f258d124cd678c5fe072fe4432a4d5232b0657fca7c1847f599233c83a", size = 1773023, upload-time = "2026-03-31T21:59:01.776Z" }, - { url = "https://files.pythonhosted.org/packages/c7/ef/720e639df03004fee2d869f771799d8c23046dec47d5b81e396c7cda583a/aiohttp-3.13.5-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:df6104c009713d3a89621096f3e3e88cc323fd269dbd7c20afe18535094320be", size = 1853795, upload-time = "2026-03-31T21:59:04.568Z" }, - { url = "https://files.pythonhosted.org/packages/bd/c9/989f4034fb46841208de7aeeac2c6d8300745ab4f28c42f629ba77c2d916/aiohttp-3.13.5-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:241a94f7de7c0c3b616627aaad530fe2cb620084a8b144d3be7b6ecfe95bae3b", size = 1730405, upload-time = "2026-03-31T21:59:07.221Z" }, - { url = "https://files.pythonhosted.org/packages/ce/75/ee1fd286ca7dc599d824b5651dad7b3be7ff8d9a7e7b3fe9820d9180f7db/aiohttp-3.13.5-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c974fb66180e58709b6fc402846f13791240d180b74de81d23913abe48e96d94", size = 1558082, upload-time = "2026-03-31T21:59:09.484Z" }, - { url = "https://files.pythonhosted.org/packages/c3/20/1e9e6650dfc436340116b7aa89ff8cb2bbdf0abc11dfaceaad8f74273a10/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:6e27ea05d184afac78aabbac667450c75e54e35f62238d44463131bd3f96753d", size = 1692346, upload-time = "2026-03-31T21:59:12.068Z" }, - { url = "https://files.pythonhosted.org/packages/d8/40/8ebc6658d48ea630ac7903912fe0dd4e262f0e16825aa4c833c56c9f1f56/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a79a6d399cef33a11b6f004c67bb07741d91f2be01b8d712d52c75711b1e07c7", size = 1698891, upload-time = "2026-03-31T21:59:14.552Z" }, - { url = "https://files.pythonhosted.org/packages/d8/78/ea0ae5ec8ba7a5c10bdd6e318f1ba5e76fcde17db8275188772afc7917a4/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:c632ce9c0b534fbe25b52c974515ed674937c5b99f549a92127c85f771a78772", size = 1742113, upload-time = "2026-03-31T21:59:17.068Z" }, - { url = "https://files.pythonhosted.org/packages/8a/66/9d308ed71e3f2491be1acb8769d96c6f0c47d92099f3bc9119cada27b357/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:fceedde51fbd67ee2bcc8c0b33d0126cc8b51ef3bbde2f86662bd6d5a6f10ec5", size = 1553088, upload-time = "2026-03-31T21:59:19.541Z" }, - { url = "https://files.pythonhosted.org/packages/da/a6/6cc25ed8dfc6e00c90f5c6d126a98e2cf28957ad06fa1036bd34b6f24a2c/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f92995dfec9420bb69ae629abf422e516923ba79ba4403bc750d94fb4a6c68c1", size = 1757976, upload-time = "2026-03-31T21:59:22.311Z" }, - { url = "https://files.pythonhosted.org/packages/c1/2b/cce5b0ffe0de99c83e5e36d8f828e4161e415660a9f3e58339d07cce3006/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:20ae0ff08b1f2c8788d6fb85afcb798654ae6ba0b747575f8562de738078457b", size = 1712444, upload-time = "2026-03-31T21:59:24.635Z" }, - { url = "https://files.pythonhosted.org/packages/6c/cf/9e1795b4160c58d29421eafd1a69c6ce351e2f7c8d3c6b7e4ca44aea1a5b/aiohttp-3.13.5-cp314-cp314-win32.whl", hash = "sha256:b20df693de16f42b2472a9c485e1c948ee55524786a0a34345511afdd22246f3", size = 438128, upload-time = "2026-03-31T21:59:27.291Z" }, - { url = "https://files.pythonhosted.org/packages/22/4d/eaedff67fc805aeba4ba746aec891b4b24cebb1a7d078084b6300f79d063/aiohttp-3.13.5-cp314-cp314-win_amd64.whl", hash = "sha256:f85c6f327bf0b8c29da7d93b1cabb6363fb5e4e160a32fa241ed2dce21b73162", size = 464029, upload-time = "2026-03-31T21:59:29.429Z" }, - { url = "https://files.pythonhosted.org/packages/79/11/c27d9332ee20d68dd164dc12a6ecdef2e2e35ecc97ed6cf0d2442844624b/aiohttp-3.13.5-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:1efb06900858bb618ff5cee184ae2de5828896c448403d51fb633f09e109be0a", size = 778758, upload-time = "2026-03-31T21:59:31.547Z" }, - { url = "https://files.pythonhosted.org/packages/04/fb/377aead2e0a3ba5f09b7624f702a964bdf4f08b5b6728a9799830c80041e/aiohttp-3.13.5-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:fee86b7c4bd29bdaf0d53d14739b08a106fdda809ca5fe032a15f52fae5fe254", size = 512883, upload-time = "2026-03-31T21:59:34.098Z" }, - { url = "https://files.pythonhosted.org/packages/bb/a6/aa109a33671f7a5d3bd78b46da9d852797c5e665bfda7d6b373f56bff2ec/aiohttp-3.13.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:20058e23909b9e65f9da62b396b77dfa95965cbe840f8def6e572538b1d32e36", size = 516668, upload-time = "2026-03-31T21:59:36.497Z" }, - { url = "https://files.pythonhosted.org/packages/79/b3/ca078f9f2fa9563c36fb8ef89053ea2bb146d6f792c5104574d49d8acb63/aiohttp-3.13.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cf20a8d6868cb15a73cab329ffc07291ba8c22b1b88176026106ae39aa6df0f", size = 1883461, upload-time = "2026-03-31T21:59:38.723Z" }, - { url = "https://files.pythonhosted.org/packages/b7/e3/a7ad633ca1ca497b852233a3cce6906a56c3225fb6d9217b5e5e60b7419d/aiohttp-3.13.5-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:330f5da04c987f1d5bdb8ae189137c77139f36bd1cb23779ca1a354a4b027800", size = 1747661, upload-time = "2026-03-31T21:59:41.187Z" }, - { url = "https://files.pythonhosted.org/packages/33/b9/cd6fe579bed34a906d3d783fe60f2fa297ef55b27bb4538438ee49d4dc41/aiohttp-3.13.5-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6f1cbf0c7926d315c3c26c2da41fd2b5d2fe01ac0e157b78caefc51a782196cf", size = 1863800, upload-time = "2026-03-31T21:59:43.84Z" }, - { url = "https://files.pythonhosted.org/packages/c0/3f/2c1e2f5144cefa889c8afd5cf431994c32f3b29da9961698ff4e3811b79a/aiohttp-3.13.5-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:53fc049ed6390d05423ba33103ded7281fe897cf97878f369a527070bd95795b", size = 1958382, upload-time = "2026-03-31T21:59:46.187Z" }, - { url = "https://files.pythonhosted.org/packages/66/1d/f31ec3f1013723b3babe3609e7f119c2c2fb6ef33da90061a705ef3e1bc8/aiohttp-3.13.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:898703aa2667e3c5ca4c54ca36cd73f58b7a38ef87a5606414799ebce4d3fd3a", size = 1803724, upload-time = "2026-03-31T21:59:48.656Z" }, - { url = "https://files.pythonhosted.org/packages/0e/b4/57712dfc6f1542f067daa81eb61da282fab3e6f1966fca25db06c4fc62d5/aiohttp-3.13.5-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0494a01ca9584eea1e5fbd6d748e61ecff218c51b576ee1999c23db7066417d8", size = 1640027, upload-time = "2026-03-31T21:59:51.284Z" }, - { url = "https://files.pythonhosted.org/packages/25/3c/734c878fb43ec083d8e31bf029daae1beafeae582d1b35da234739e82ee7/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:6cf81fe010b8c17b09495cbd15c1d35afbc8fb405c0c9cf4738e5ae3af1d65be", size = 1806644, upload-time = "2026-03-31T21:59:53.753Z" }, - { url = "https://files.pythonhosted.org/packages/20/a5/f671e5cbec1c21d044ff3078223f949748f3a7f86b14e34a365d74a5d21f/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:c564dd5f09ddc9d8f2c2d0a301cd30a79a2cc1b46dd1a73bef8f0038863d016b", size = 1791630, upload-time = "2026-03-31T21:59:56.239Z" }, - { url = "https://files.pythonhosted.org/packages/0b/63/fb8d0ad63a0b8a99be97deac8c04dacf0785721c158bdf23d679a87aa99e/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:2994be9f6e51046c4f864598fd9abeb4fba6e88f0b2152422c9666dcd4aea9c6", size = 1809403, upload-time = "2026-03-31T21:59:59.103Z" }, - { url = "https://files.pythonhosted.org/packages/59/0c/bfed7f30662fcf12206481c2aac57dedee43fe1c49275e85b3a1e1742294/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:157826e2fa245d2ef46c83ea8a5faf77ca19355d278d425c29fda0beb3318037", size = 1634924, upload-time = "2026-03-31T22:00:02.116Z" }, - { url = "https://files.pythonhosted.org/packages/17/d6/fd518d668a09fd5a3319ae5e984d4d80b9a4b3df4e21c52f02251ef5a32e/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:a8aca50daa9493e9e13c0f566201a9006f080e7c50e5e90d0b06f53146a54500", size = 1836119, upload-time = "2026-03-31T22:00:04.756Z" }, - { url = "https://files.pythonhosted.org/packages/78/b7/15fb7a9d52e112a25b621c67b69c167805cb1f2ab8f1708a5c490d1b52fe/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3b13560160d07e047a93f23aaa30718606493036253d5430887514715b67c9d9", size = 1772072, upload-time = "2026-03-31T22:00:07.494Z" }, - { url = "https://files.pythonhosted.org/packages/7e/df/57ba7f0c4a553fc2bd8b6321df236870ec6fd64a2a473a8a13d4f733214e/aiohttp-3.13.5-cp314-cp314t-win32.whl", hash = "sha256:9a0f4474b6ea6818b41f82172d799e4b3d29e22c2c520ce4357856fced9af2f8", size = 471819, upload-time = "2026-03-31T22:00:10.277Z" }, - { url = "https://files.pythonhosted.org/packages/62/29/2f8418269e46454a26171bfdd6a055d74febf32234e474930f2f60a17145/aiohttp-3.13.5-cp314-cp314t-win_amd64.whl", hash = "sha256:18a2f6c1182c51baa1d28d68fea51513cb2a76612f038853c0ad3c145423d3d9", size = 505441, upload-time = "2026-03-31T22:00:12.791Z" }, -] - -[[package]] -name = "aiosignal" -version = "1.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "frozenlist" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, -] - [[package]] name = "aiosmtplib" version = "5.1.0" @@ -190,39 +109,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, ] -[[package]] -name = "av" -version = "17.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b2/eb/abca886df3a091bc406feb5ff71b4c4f426beaae6b71b9697264ce8c7211/av-17.0.0.tar.gz", hash = "sha256:c53685df73775a8763c375c7b2d62a6cb149d992a26a4b098204da42ade8c3df", size = 4410769, upload-time = "2026-03-14T14:38:45.868Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b1/fb/55e3b5b5d1fc61466292f26fbcbabafa2642f378dc48875f8f554591e1a4/av-17.0.0-cp311-abi3-macosx_11_0_x86_64.whl", hash = "sha256:ed4013fac77c309a4a68141dcf6148f1821bb1073a36d4289379762a6372f711", size = 23238424, upload-time = "2026-03-14T14:38:05.856Z" }, - { url = "https://files.pythonhosted.org/packages/52/03/9ace1acc08bc9ae38c14bf3a4b1360e995e4d999d1d33c2cbd7c9e77582a/av-17.0.0-cp311-abi3-macosx_14_0_arm64.whl", hash = "sha256:e44b6c83e9f3be9f79ee87d0b77a27cea9a9cd67bd630362c86b7e56a748dfbb", size = 18709043, upload-time = "2026-03-14T14:38:08.288Z" }, - { url = "https://files.pythonhosted.org/packages/00/c0/637721f3cd5bb8bd16105a1a08efd781fc12f449931bdb3a4d0cfd63fa55/av-17.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:b440da6ac47da0629d509316f24bcd858f33158dbdd0f1b7293d71e99beb26de", size = 34018780, upload-time = "2026-03-14T14:38:10.45Z" }, - { url = "https://files.pythonhosted.org/packages/d2/59/d19bc3257dd985d55337d7f0414c019414b97e16cd3690ebf9941a847543/av-17.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:1060cba85f97f4a337311169d92c0b5e143452cfa5ca0e65fa499d7955e8592e", size = 36358757, upload-time = "2026-03-14T14:38:13.092Z" }, - { url = "https://files.pythonhosted.org/packages/52/6c/a1f4f2677bae6f2ade7a8a18e90ebdcf70690c9b1c4e40e118aa30fa313f/av-17.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:deda202e6021cfc7ba3e816897760ec5431309d59a4da1f75df3c0e9413d71e7", size = 35195281, upload-time = "2026-03-14T14:38:15.789Z" }, - { url = "https://files.pythonhosted.org/packages/90/ea/52b0fc6f69432c7bf3f5fbe6f707113650aa40a1a05b9096ffc2bba4f77d/av-17.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ffaf266a1a9c2148072de0a4b5ae98061465178d2cfaa69ee089761149342974", size = 37444817, upload-time = "2026-03-14T14:38:18.563Z" }, - { url = "https://files.pythonhosted.org/packages/34/ad/d2172966282cb8f146c13b6be7416efefde74186460c5e1708ddfc13dba6/av-17.0.0-cp311-abi3-win_amd64.whl", hash = "sha256:45a35a40b2875bf2f98de7c952d74d960f92f319734e6d28e03b4c62a49e6f49", size = 28888553, upload-time = "2026-03-14T14:38:21.223Z" }, - { url = "https://files.pythonhosted.org/packages/b0/bb/c5a4c4172c514d631fb506e6366b503576b8c7f29809cf42aca73e28ff01/av-17.0.0-cp311-abi3-win_arm64.whl", hash = "sha256:3d32e9b5c5bbcb872a0b6917b352a1db8a42142237826c9b49a36d5dbd9e9c26", size = 21916910, upload-time = "2026-03-14T14:38:23.706Z" }, - { url = "https://files.pythonhosted.org/packages/7f/8e/c40ac08e63f79387c59f6ecc38f47d4c942b549130eee579ec1a91f6a291/av-17.0.0-cp314-cp314t-macosx_11_0_x86_64.whl", hash = "sha256:d13250fb4b4522e9a6bec32da082556d5f257110ea223758151375748d9bbe25", size = 23483029, upload-time = "2026-03-14T14:38:25.758Z" }, - { url = "https://files.pythonhosted.org/packages/a9/fb/b4419494bfc249163ec393c613966d66db7e95c76da3345711cd115a79df/av-17.0.0-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:dbb56aa3b7ae72451d1bf6e9d37c7d83d39b97af712f73583ff419fbf08fc237", size = 18920446, upload-time = "2026-03-14T14:38:27.905Z" }, - { url = "https://files.pythonhosted.org/packages/30/62/c2306d91602ddad2c56106f21dcb334fd51d5ea2e952f7fa025bb8aa39fc/av-17.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:a213ac9e83b7ab12c2e9f277a09cac8e9d85cf0883efdab7a87a60e2e4e48879", size = 37477266, upload-time = "2026-03-14T14:38:30.404Z" }, - { url = "https://files.pythonhosted.org/packages/28/cd/c8510a9607886785c0b3ca019d503e888c3757529be42a7287fe2bfa92d5/av-17.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:e15c88bb0921f9435bcc5a27a0863dba571a80ad5e1389c4fcf2073833bb4a74", size = 39572988, upload-time = "2026-03-14T14:38:32.984Z" }, - { url = "https://files.pythonhosted.org/packages/7d/2d/207d9361e25b5abec9be335bbab4df6b6b838e2214be4b374f4cfb285427/av-17.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:096cfd1e9fc896506726c7c42aaf9b370e78c2f257cde4d6ddb6c889bfcc49ec", size = 38399591, upload-time = "2026-03-14T14:38:35.465Z" }, - { url = "https://files.pythonhosted.org/packages/73/ca/307740c6aa2980966bf11383ffcb04bacc5b13f3d268ab4cfb274ad6f793/av-17.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3649ab3d2c7f58049ded1a36e100c0d8fd529cf258f41dd88678ba824034d8c9", size = 40590681, upload-time = "2026-03-14T14:38:38.269Z" }, - { url = "https://files.pythonhosted.org/packages/35/f2/6fdb26d0651adf409864cb2a0d60da107e467d3d1aabc94b234ead54324a/av-17.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:e5002271ab2135b551d980c2db8f3299d452e3b9d3633f24f6bb57fffe91cd10", size = 29216337, upload-time = "2026-03-14T14:38:40.83Z" }, - { url = "https://files.pythonhosted.org/packages/41/0a/0896b829a39b5669a2d811e1a79598de661693685cd62b31f11d0c18e65b/av-17.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:dba98603fc4665b4f750de86fbaf6c0cfaece970671a9b529e0e3d1711e8367e", size = 22071058, upload-time = "2026-03-14T14:38:43.663Z" }, -] - -[[package]] -name = "babel" -version = "2.18.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7d/b2/51899539b6ceeeb420d40ed3cd4b7a40519404f9baf3d4ac99dc413a834b/babel-2.18.0.tar.gz", hash = "sha256:b80b99a14bd085fcacfa15c9165f651fbb3406e66cc603abf11c5750937c992d", size = 9959554, upload-time = "2026-02-01T12:30:56.078Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/77/f5/21d2de20e8b8b0408f0681956ca2c69f1320a3848ac50e6e7f39c6159675/babel-2.18.0-py3-none-any.whl", hash = "sha256:e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35", size = 10196845, upload-time = "2026-02-01T12:30:53.445Z" }, -] - [[package]] name = "bcrypt" version = "5.0.0" @@ -283,24 +169,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc", size = 8458, upload-time = "2024-11-08T17:25:46.184Z" }, ] -[[package]] -name = "blis" -version = "1.3.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d0/d0/d8cc8c9a4488a787e7fa430f6055e5bd1ddb22c340a751d9e901b82e2efe/blis-1.3.3.tar.gz", hash = "sha256:034d4560ff3cc43e8aa37e188451b0440e3261d989bb8a42ceee865607715ecd", size = 2644873, upload-time = "2025-11-17T12:28:30.511Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/8a/80f7c68fbc24a76fc9c18522c46d6d69329c320abb18e26a707a5d874083/blis-1.3.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:c3e33cfbf22a418373766816343fcfcd0556012aa3ffdf562c29cddec448a415", size = 6934081, upload-time = "2025-11-17T12:28:16.436Z" }, - { url = "https://files.pythonhosted.org/packages/e5/52/d1aa3a51a7fc299b0c89dcaa971922714f50b1202769eebbdaadd1b5cff7/blis-1.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:6f165930e8d3a85c606d2003211497e28d528c7416fbfeafb6b15600963f7c9b", size = 1231486, upload-time = "2025-11-17T12:28:18.008Z" }, - { url = "https://files.pythonhosted.org/packages/99/4f/badc7bd7f74861b26c10123bba7b9d16f99cd9535ad0128780360713820f/blis-1.3.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:878d4d96d8f2c7a2459024f013f2e4e5f46d708b23437dae970d998e7bff14a0", size = 2814944, upload-time = "2025-11-17T12:28:19.654Z" }, - { url = "https://files.pythonhosted.org/packages/72/a6/f62a3bd814ca19ec7e29ac889fd354adea1217df3183e10217de51e2eb8b/blis-1.3.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f36c0ca84a05ee5d3dbaa38056c4423c1fc29948b17a7923dd2fed8967375d74", size = 11345825, upload-time = "2025-11-17T12:28:21.354Z" }, - { url = "https://files.pythonhosted.org/packages/d4/6c/671af79ee42bc4c968cae35c091ac89e8721c795bfa4639100670dc59139/blis-1.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e5a662c48cd4aad5dae1a950345df23957524f071315837a4c6feb7d3b288990", size = 3008771, upload-time = "2025-11-17T12:28:23.637Z" }, - { url = "https://files.pythonhosted.org/packages/be/92/7cd7f8490da7c98ee01557f2105885cc597217b0e7fd2eeb9e22cdd4ef23/blis-1.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9de26fbd72bac900c273b76d46f0b45b77a28eace2e01f6ac6c2239531a413bb", size = 14219213, upload-time = "2025-11-17T12:28:26.143Z" }, - { url = "https://files.pythonhosted.org/packages/0a/de/acae8e9f9a1f4bb393d41c8265898b0f29772e38eac14e9f69d191e2c006/blis-1.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:9e5fdf4211b1972400f8ff6dafe87cb689c5d84f046b4a76b207c0bd2270faaf", size = 6324695, upload-time = "2025-11-17T12:28:28.401Z" }, -] - [[package]] name = "caldav" version = "3.1.0" @@ -320,15 +188,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cc/fd/e2e9f12d7b86296894dfc211d7120a4a740288761e5f5349b54ebb490461/caldav-3.1.0-py3-none-any.whl", hash = "sha256:9d6713f75273bc82f58e2fb7684ecb8196f09ee309609d36577c1c5eec22152b", size = 232470, upload-time = "2026-03-19T18:09:24.212Z" }, ] -[[package]] -name = "catalogue" -version = "2.0.10" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/38/b4/244d58127e1cdf04cf2dc7d9566f0d24ef01d5ce21811bab088ecc62b5ea/catalogue-2.0.10.tar.gz", hash = "sha256:4f56daa940913d3f09d589c191c74e5a6d51762b3a9e37dd53b7437afd6cda15", size = 19561, upload-time = "2023-09-25T06:29:24.962Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/96/d32b941a501ab566a16358d68b6eb4e4acc373fab3c3c4d7d9e649f7b4bb/catalogue-2.0.10-py3-none-any.whl", hash = "sha256:58c2de0020aa90f4a2da7dfad161bf7b3b054c86a5f09fcedc0b2b740c109a9f", size = 17325, upload-time = "2023-09-25T06:29:23.337Z" }, -] - [[package]] name = "certifi" version = "2026.2.25" @@ -424,15 +283,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e4/20/71885d8b97d4f3dde17b1fdb92dbd4908b00541c5a3379787137285f602e/click-8.3.2-py3-none-any.whl", hash = "sha256:1924d2c27c5653561cd2cae4548d1406039cb79b858b747cfea24924bbc1616d", size = 108379, upload-time = "2026-04-03T19:14:43.505Z" }, ] -[[package]] -name = "cloudpathlib" -version = "0.23.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f4/18/2ac35d6b3015a0c74e923d94fc69baf8307f7c3233de015d69f99e17afa8/cloudpathlib-0.23.0.tar.gz", hash = "sha256:eb38a34c6b8a048ecfd2b2f60917f7cbad4a105b7c979196450c2f541f4d6b4b", size = 53126, upload-time = "2025-10-07T22:47:56.278Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ae/8a/c4bb04426d608be4a3171efa2e233d2c59a5c8937850c10d098e126df18e/cloudpathlib-0.23.0-py3-none-any.whl", hash = "sha256:8520b3b01468fee77de37ab5d50b1b524ea6b4a8731c35d1b7407ac0cd716002", size = 62755, upload-time = "2025-10-07T22:47:54.905Z" }, -] - [[package]] name = "colorama" version = "0.4.6" @@ -442,29 +292,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, ] -[[package]] -name = "confection" -version = "1.3.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ca/65/efd0fe8a936fc8ca2978cb7b82581fb20d901c6039e746a808f746b7647b/confection-1.3.3.tar.gz", hash = "sha256:f0f6810d567ff73993fe74d218ca5e1ffb6a44fb03f391257fc5d033546cbfaa", size = 54895, upload-time = "2026-03-24T18:45:24.331Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/e4/d66708bdf0d92fb4d49b22cdff4b10cec38aca5dcd7e81d909bb55c65cd7/confection-1.3.3-py3-none-any.whl", hash = "sha256:b9fef9ee84b237ef4611ec3eb5797b70e13063e6310ad9f15536373f5e313c82", size = 35902, upload-time = "2026-03-24T18:45:22.664Z" }, -] - -[[package]] -name = "courlan" -version = "1.3.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "babel" }, - { name = "tld" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/6f/54/6d6ceeff4bed42e7a10d6064d35ee43a810e7b3e8beb4abeae8cff4713ae/courlan-1.3.2.tar.gz", hash = "sha256:0b66f4db3a9c39a6e22dd247c72cfaa57d68ea660e94bb2c84ec7db8712af190", size = 206382, upload-time = "2024-10-29T16:40:20.994Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8e/ca/6a667ccbe649856dcd3458bab80b016681b274399d6211187c6ab969fc50/courlan-1.3.2-py3-none-any.whl", hash = "sha256:d0dab52cf5b5b1000ee2839fbc2837e93b2514d3cb5bb61ae158a55b7a04c6be", size = 33848, upload-time = "2024-10-29T16:40:18.325Z" }, -] - [[package]] name = "cryptography" version = "46.0.7" @@ -518,184 +345,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d2/f1/00ce3bde3ca542d1acd8f8cfa38e446840945aa6363f9b74746394b14127/cryptography-46.0.7-cp38-abi3-win_amd64.whl", hash = "sha256:506c4ff91eff4f82bdac7633318a526b1d1309fc07ca76a3ad182cb5b686d6d3", size = 3472985, upload-time = "2026-04-08T01:57:36.714Z" }, ] -[[package]] -name = "csvw" -version = "3.7.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "attrs" }, - { name = "babel" }, - { name = "isodate" }, - { name = "jsonschema" }, - { name = "language-tags" }, - { name = "python-dateutil" }, - { name = "rdflib" }, - { name = "requests" }, - { name = "rfc3986" }, - { name = "termcolor" }, - { name = "uritemplate" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d4/90/64efac40e52949079c2b4030167ee68ec52cf061f11e368ee1a82e410670/csvw-3.7.0.tar.gz", hash = "sha256:869b5c761481e52c01a99fb4749b278a4b8b0db4e0fa1965a33a3441c703465b", size = 74789, upload-time = "2025-10-07T10:46:28.729Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/80/cb/19e8e582fc164db200c18078bdbdcc60c012cb83c7f02ea8e876bc0b1adf/csvw-3.7.0-py2.py3-none-any.whl", hash = "sha256:21b88db50a35e940d4b5cdd8f3a8084493ad7f1bb1657ed7323aad977359940e", size = 60685, upload-time = "2025-10-07T10:46:26.708Z" }, -] - -[[package]] -name = "ctranslate2" -version = "4.7.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, - { name = "pyyaml" }, - { name = "setuptools" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/73/8a6b7ba18cad0c8667ee221ddab8c361cb70926440e5b8dd0e81924c28ac/ctranslate2-4.7.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:d5dfb076566551f4959dfd0706f94c923c1931def9b7bb249a2caa6ab23353a0", size = 1257560, upload-time = "2026-02-04T06:12:00.926Z" }, - { url = "https://files.pythonhosted.org/packages/70/c2/8817ca5d6c1b175b23a12f7c8b91484652f8718a76353317e5919b038733/ctranslate2-4.7.1-cp314-cp314-macosx_11_0_x86_64.whl", hash = "sha256:eecdb4ed934b384f16e8c01b185b082d6b5ffc7dcbb0b6a6eb48cd465282d957", size = 11918995, upload-time = "2026-02-04T06:12:02.875Z" }, - { url = "https://files.pythonhosted.org/packages/ac/33/b8eb3acc67bbca4d9872fc9ff94db78e6167a7ba5cd932f585d1560effc7/ctranslate2-4.7.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1aa6796edcc3c8d163c9e39c429d50076d266d68980fed9d1b2443f617c67e9e", size = 16844162, upload-time = "2026-02-04T06:12:05.099Z" }, - { url = "https://files.pythonhosted.org/packages/80/11/6474893b07121057035069a0a483fe1cd8c47878213f282afb4c0c6fc275/ctranslate2-4.7.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24c0482c51726430fb83724451921c0e539d769c8618dcfd46b1645e7f75960d", size = 38966728, upload-time = "2026-02-04T06:12:07.923Z" }, - { url = "https://files.pythonhosted.org/packages/94/88/8fc7ff435c5e783e5fad9586d839d463e023988dbbbad949d442092d01f1/ctranslate2-4.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:76db234c0446a23d20dd8eeaa7a789cc87d1d05283f48bf3152bae9fa0a69844", size = 19100788, upload-time = "2026-02-04T06:12:10.592Z" }, - { url = "https://files.pythonhosted.org/packages/d9/b3/f100013a76a98d64e67c721bd4559ea4eeb54be3e4ac45f4d801769899af/ctranslate2-4.7.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:058c9db2277dc8b19ecc86c7937628f69022f341844b9081d2ab642965d88fc6", size = 1280179, upload-time = "2026-02-04T06:12:12.596Z" }, - { url = "https://files.pythonhosted.org/packages/39/22/b77f748015667a5e2ca54a5ee080d7016fce34314f0e8cf904784549305a/ctranslate2-4.7.1-cp314-cp314t-macosx_11_0_x86_64.whl", hash = "sha256:5abcf885062c7f28a3f9a46be8d185795e8706ac6230ad086cae0bc82917df31", size = 11940166, upload-time = "2026-02-04T06:12:14.054Z" }, - { url = "https://files.pythonhosted.org/packages/7d/78/6d7fd52f646c6ba3343f71277a9bbef33734632949d1651231948b0f0359/ctranslate2-4.7.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9950acb04a002d5c60ae90a1ddceead1a803af1f00cadd9b1a1dc76e1f017481", size = 16849483, upload-time = "2026-02-04T06:12:17.082Z" }, - { url = "https://files.pythonhosted.org/packages/40/27/58769ff15ac31b44205bd7a8aeca80cf7357c657ea5df1b94ce0f5c83771/ctranslate2-4.7.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1dcc734e92e3f1ceeaa0c42bbfd009352857be179ecd4a7ed6cccc086a202f58", size = 38949393, upload-time = "2026-02-04T06:12:21.302Z" }, - { url = "https://files.pythonhosted.org/packages/0e/5c/9fa0ad6462b62efd0fb5ac1100eee47bc96ecc198ff4e237c731e5473616/ctranslate2-4.7.1-cp314-cp314t-win_amd64.whl", hash = "sha256:dfb7657bdb7b8211c8f9ecb6f3b70bc0db0e0384d01a8b1808cb66fe7199df59", size = 19123451, upload-time = "2026-02-04T06:12:24.115Z" }, -] - -[[package]] -name = "cuda-bindings" -version = "13.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cuda-pathfinder" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/c0/87/87a014f045b77c6de5c8527b0757fe644417b184e5367db977236a141602/cuda_bindings-13.2.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a6464b30f46692d6c7f65d4a0e0450d81dd29de3afc1bb515653973d01c2cd6e", size = 5685673, upload-time = "2026-03-11T00:12:56.371Z" }, - { url = "https://files.pythonhosted.org/packages/ee/5e/c0fe77a73aaefd3fff25ffaccaac69c5a63eafdf8b9a4c476626ef0ac703/cuda_bindings-13.2.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f4af9f3e1be603fa12d5ad6cfca7844c9d230befa9792b5abdf7dd79979c3626", size = 6191386, upload-time = "2026-03-11T00:12:58.965Z" }, - { url = "https://files.pythonhosted.org/packages/5f/58/ed2c3b39c8dd5f96aa7a4abef0d47a73932c7a988e30f5fa428f00ed0da1/cuda_bindings-13.2.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df850a1ff8ce1b3385257b08e47b70e959932f5f432d0a4e46a355962b4e4771", size = 5507469, upload-time = "2026-03-11T00:13:04.063Z" }, - { url = "https://files.pythonhosted.org/packages/1f/01/0c941b112ceeb21439b05895eace78ca1aa2eaaf695c8521a068fd9b4c00/cuda_bindings-13.2.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8a16384c6494e5485f39314b0b4afb04bee48d49edb16d5d8593fd35bbd231b", size = 6059693, upload-time = "2026-03-11T00:13:06.003Z" }, -] - -[[package]] -name = "cuda-pathfinder" -version = "1.5.2" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f2/f9/1b9b60a30fc463c14cdea7a77228131a0ccc89572e8df9cb86c9648271ab/cuda_pathfinder-1.5.2-py3-none-any.whl", hash = "sha256:0c5f160a7756c5b072723cbbd6d861e38917ef956c68150b02f0b6e9271c71fa", size = 49988, upload-time = "2026-04-06T23:01:05.17Z" }, -] - -[[package]] -name = "cuda-toolkit" -version = "13.0.2" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/57/b2/453099f5f3b698d7d0eab38916aac44c7f76229f451709e2eb9db6615dcd/cuda_toolkit-13.0.2-py2.py3-none-any.whl", hash = "sha256:b198824cf2f54003f50d64ada3a0f184b42ca0846c1c94192fa269ecd97a66eb", size = 2364, upload-time = "2025-12-19T23:24:07.328Z" }, -] - -[package.optional-dependencies] -cublas = [ - { name = "nvidia-cublas", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, -] -cudart = [ - { name = "nvidia-cuda-runtime", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, -] -cufft = [ - { name = "nvidia-cufft", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, -] -cufile = [ - { name = "nvidia-cufile", marker = "sys_platform == 'linux'" }, -] -cupti = [ - { name = "nvidia-cuda-cupti", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, -] -curand = [ - { name = "nvidia-curand", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, -] -cusolver = [ - { name = "nvidia-cusolver", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, -] -cusparse = [ - { name = "nvidia-cusparse", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, -] -nvjitlink = [ - { name = "nvidia-nvjitlink", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, -] -nvrtc = [ - { name = "nvidia-cuda-nvrtc", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, -] -nvtx = [ - { name = "nvidia-nvtx", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, -] - -[[package]] -name = "curated-tokenizers" -version = "0.0.9" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "regex" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/fc/fa/b2d55f0d53c7c7f5dc0b6dbb48cc4344ee84fb572f23de28040bf2cde89d/curated-tokenizers-0.0.9.tar.gz", hash = "sha256:c93d47e54ab3528a6db2796eeb4bdce5d44e8226c671e42c2f23522ab1d0ce25", size = 2237055, upload-time = "2024-01-18T13:45:52.36Z" } - -[[package]] -name = "curated-transformers" -version = "0.1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "torch" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/70/06/6c12c149a7f737dacc76b4c3949dbc7ff87d622567b86996896ae4d104aa/curated-transformers-0.1.1.tar.gz", hash = "sha256:4671f03314df30efda2ec2b59bc7692ea34fcea44cb65382342c16684e8a2119", size = 16313, upload-time = "2023-05-24T07:29:22.801Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d6/67/3b72b3fdfcadab61bc8f59c17e63770e526ffabd583ed32f174a7c01af85/curated_transformers-0.1.1-py2.py3-none-any.whl", hash = "sha256:d716063d73d803c6925d2dab56fde9b9ab8e89e663c2c0587804944ba488ff01", size = 25972, upload-time = "2023-05-24T07:29:21.119Z" }, -] - -[[package]] -name = "cymem" -version = "2.0.13" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c0/8f/2f0fbb32535c3731b7c2974c569fb9325e0a38ed5565a08e1139a3b71e82/cymem-2.0.13.tar.gz", hash = "sha256:1c91a92ae8c7104275ac26bd4d29b08ccd3e7faff5893d3858cb6fadf1bc1588", size = 12320, upload-time = "2025-11-14T14:58:36.902Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/2e/f0e1596010a9a57fa9ebd124a678c07c5b2092283781ae51e79edcf5cb98/cymem-2.0.13-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d2a4bf67db76c7b6afc33de44fb1c318207c3224a30da02c70901936b5aafdf1", size = 43812, upload-time = "2025-11-14T14:58:04.227Z" }, - { url = "https://files.pythonhosted.org/packages/bc/45/8ccc21df08fcbfa6aa3efeb7efc11a1c81c90e7476e255768bb9c29ba02a/cymem-2.0.13-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:92a2ce50afa5625fb5ce7c9302cee61e23a57ccac52cd0410b4858e572f8614b", size = 42951, upload-time = "2025-11-14T14:58:05.424Z" }, - { url = "https://files.pythonhosted.org/packages/01/8c/fe16531631f051d3d1226fa42e2d76fd2c8d5cfa893ec93baee90c7a9d90/cymem-2.0.13-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bc116a70cc3a5dc3d1684db5268eff9399a0be8603980005e5b889564f1ea42f", size = 249878, upload-time = "2025-11-14T14:58:06.95Z" }, - { url = "https://files.pythonhosted.org/packages/47/4b/39d67b80ffb260457c05fcc545de37d82e9e2dbafc93dd6b64f17e09b933/cymem-2.0.13-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:68489bf0035c4c280614067ab6a82815b01dc9fcd486742a5306fe9f68deb7ef", size = 252571, upload-time = "2025-11-14T14:58:08.232Z" }, - { url = "https://files.pythonhosted.org/packages/53/0e/76f6531f74dfdfe7107899cce93ab063bb7ee086ccd3910522b31f623c08/cymem-2.0.13-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:03cb7bdb55718d5eb6ef0340b1d2430ba1386db30d33e9134d01ba9d6d34d705", size = 248555, upload-time = "2025-11-14T14:58:09.429Z" }, - { url = "https://files.pythonhosted.org/packages/c7/7c/eee56757db81f0aefc2615267677ae145aff74228f529838425057003c0d/cymem-2.0.13-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:1710390e7fb2510a8091a1991024d8ae838fd06b02cdfdcd35f006192e3c6b0e", size = 254177, upload-time = "2025-11-14T14:58:10.594Z" }, - { url = "https://files.pythonhosted.org/packages/77/e0/a4b58ec9e53c836dce07ef39837a64a599f4a21a134fc7ca57a3a8f9a4b5/cymem-2.0.13-cp314-cp314-win_amd64.whl", hash = "sha256:ac699c8ec72a3a9de8109bd78821ab22f60b14cf2abccd970b5ff310e14158ed", size = 40853, upload-time = "2025-11-14T14:58:12.116Z" }, - { url = "https://files.pythonhosted.org/packages/61/81/9931d1f83e5aeba175440af0b28f0c2e6f71274a5a7b688bc3e907669388/cymem-2.0.13-cp314-cp314-win_arm64.whl", hash = "sha256:90c2d0c04bcda12cd5cebe9be93ce3af6742ad8da96e1b1907e3f8e00291def1", size = 36970, upload-time = "2025-11-14T14:58:13.114Z" }, - { url = "https://files.pythonhosted.org/packages/b7/ef/af447c2184dec6dec973be14614df8ccb4d16d1c74e0784ab4f02538433c/cymem-2.0.13-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:ff036bbc1464993552fd1251b0a83fe102af334b301e3896d7aa05a4999ad042", size = 46804, upload-time = "2025-11-14T14:58:14.113Z" }, - { url = "https://files.pythonhosted.org/packages/8c/95/e10f33a8d4fc17f9b933d451038218437f9326c2abb15a3e7f58ce2a06ec/cymem-2.0.13-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:fb8291691ba7ff4e6e000224cc97a744a8d9588418535c9454fd8436911df612", size = 46254, upload-time = "2025-11-14T14:58:15.156Z" }, - { url = "https://files.pythonhosted.org/packages/e7/7a/5efeb2d2ea6ebad2745301ad33a4fa9a8f9a33b66623ee4d9185683007a6/cymem-2.0.13-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d8d06ea59006b1251ad5794bcc00121e148434826090ead0073c7b7fedebe431", size = 296061, upload-time = "2025-11-14T14:58:16.254Z" }, - { url = "https://files.pythonhosted.org/packages/0b/28/2a3f65842cc8443c2c0650cf23d525be06c8761ab212e0a095a88627be1b/cymem-2.0.13-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c0046a619ecc845ccb4528b37b63426a0cbcb4f14d7940add3391f59f13701e6", size = 285784, upload-time = "2025-11-14T14:58:17.412Z" }, - { url = "https://files.pythonhosted.org/packages/98/73/dd5f9729398f0108c2e71d942253d0d484d299d08b02e474d7cfc43ed0b0/cymem-2.0.13-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:18ad5b116a82fa3674bc8838bd3792891b428971e2123ae8c0fd3ca472157c5e", size = 288062, upload-time = "2025-11-14T14:58:20.225Z" }, - { url = "https://files.pythonhosted.org/packages/5a/01/ffe51729a8f961a437920560659073e47f575d4627445216c1177ecd4a41/cymem-2.0.13-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:666ce6146bc61b9318aa70d91ce33f126b6344a25cf0b925621baed0c161e9cc", size = 290465, upload-time = "2025-11-14T14:58:21.815Z" }, - { url = "https://files.pythonhosted.org/packages/fd/ac/c9e7d68607f71ef978c81e334ab2898b426944c71950212b1467186f69f9/cymem-2.0.13-cp314-cp314t-win_amd64.whl", hash = "sha256:84c1168c563d9d1e04546cb65e3e54fde2bf814f7c7faf11fc06436598e386d1", size = 46665, upload-time = "2025-11-14T14:58:23.512Z" }, - { url = "https://files.pythonhosted.org/packages/66/66/150e406a2db5535533aa3c946de58f0371f2e412e23f050c704588023e6e/cymem-2.0.13-cp314-cp314t-win_arm64.whl", hash = "sha256:e9027764dc5f1999fb4b4cabee1d0322c59e330c0a6485b436a68275f614277f", size = 39715, upload-time = "2025-11-14T14:58:24.773Z" }, -] - -[[package]] -name = "dateparser" -version = "1.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "python-dateutil" }, - { name = "pytz" }, - { name = "regex" }, - { name = "tzlocal" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/46/2d/a0ccdb78788064fa0dc901b8524e50615c42be1d78b78d646d0b28d09180/dateparser-1.4.0.tar.gz", hash = "sha256:97a21840d5ecdf7630c584f673338a5afac5dfe84f647baf4d7e8df98f9354a4", size = 321512, upload-time = "2026-03-26T09:56:10.292Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b4/0b/3c3bb7cbe757279e693a0be6049048012f794d01f81099609ecd53b899f0/dateparser-1.4.0-py3-none-any.whl", hash = "sha256:7902b8e85d603494bf70a5a0b1decdddb2270b9c6e6b2bc8a57b93476c0df378", size = 300379, upload-time = "2026-03-26T09:56:08.409Z" }, -] - -[[package]] -name = "dlinfo" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/85/8e/8f2f94cd40af1b51e8e371a83b385d622170d42f98776441a6118f4dd682/dlinfo-2.0.0.tar.gz", hash = "sha256:88a2bc04f51d01bc604cdc9eb1c3cc0bde89057532ca6a3e71a41f6235433e17", size = 12727, upload-time = "2025-01-16T15:43:10.756Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/da/90/022c79d6e5e6f843268c10b84d4a021ee3afba0621d3c176d3ff2024bfc8/dlinfo-2.0.0-py3-none-any.whl", hash = "sha256:b32cc18e3ea67c0ca9ca409e5b41eed863bd1363dbc9dd3de90fedf11b61e7bc", size = 3654, upload-time = "2025-01-16T15:43:09.474Z" }, -] - [[package]] name = "dnspython" version = "2.8.0" @@ -706,26 +355,7 @@ wheels = [ ] [[package]] -name = "docopt" -version = "0.6.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/55/8f8cab2afd404cf578136ef2cc5dfb50baa1761b68c9da1fb1e4eed343c9/docopt-0.6.2.tar.gz", hash = "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491", size = 25901, upload-time = "2014-06-16T11:18:57.406Z" } - -[[package]] -name = "espeakng-loader" -version = "0.2.4" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f8/92/f44ed7f531143c3c6c97d56e2b0f9be8728dc05e18b96d46eb539230ed46/espeakng_loader-0.2.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b77477ae2ddf62a748e04e49714eabb2f3a24f344166200b00539083bd669904", size = 9938387, upload-time = "2025-01-17T01:22:42.064Z" }, - { url = "https://files.pythonhosted.org/packages/a8/26/258c0cd43b9bc1043301c5f61767d6a6c3b679df82790c9cb43a3277b865/espeakng_loader-0.2.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d27cdca31112226e7299d8562e889d3e38a1e48055c9ee381b45d669072ee59f", size = 9892565, upload-time = "2025-01-17T01:22:40.365Z" }, - { url = "https://files.pythonhosted.org/packages/de/1e/25ec5ab07528c0fbb215a61800a38eca05c8a99445515a02d7fa5debcb32/espeakng_loader-0.2.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08721baf27d13d461f6be6eed9a65277e70d68234ff484fd8b9897b222cdcb6d", size = 10078484, upload-time = "2025-01-17T01:22:43.373Z" }, - { url = "https://files.pythonhosted.org/packages/d9/ad/1b768d8daffc2996e07bbcb6f534d8de3202cd75fce1f1c45eced1ce6465/espeakng_loader-0.2.4-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:d1e798141b46a050cdb75fcf3c17db969bb2c40394f3f4a48910655d547508b9", size = 10037736, upload-time = "2025-01-17T01:22:42.576Z" }, - { url = "https://files.pythonhosted.org/packages/9d/ed/a3d872fbad4f3a3f3db0e8c31768ab14e77cd77306de16b8b20b1e1df7ea/espeakng_loader-0.2.4-py3-none-win_amd64.whl", hash = "sha256:41f1e08ac9deda2efd1ea9de0b81dab9f5ae3c4b24284f76533d0a7b1dd7abd7", size = 9437292, upload-time = "2025-01-17T01:23:27.463Z" }, - { url = "https://files.pythonhosted.org/packages/29/64/0b75bc50ec53b4e000bac913625511215aa96124adf5dba8c4baa17c02cd/espeakng_loader-0.2.4-py3-none-win_arm64.whl", hash = "sha256:d7a2928843eaeb2df82f99a370f44e8a630f59b02f9b0d1f168a03c4eeb76b89", size = 9426841, upload-time = "2025-01-17T01:23:21.766Z" }, -] - -[[package]] -name = "fabledassistant" +name = "scribe" version = "0.1.0" source = { editable = "." } dependencies = [ @@ -735,15 +365,13 @@ dependencies = [ { name = "asyncpg" }, { name = "bcrypt" }, { name = "caldav" }, - { name = "feedparser" }, - { name = "html2text" }, + { name = "fastembed" }, { name = "httpx" }, { name = "hypercorn" }, { name = "icalendar" }, - { name = "pywebpush" }, + { name = "mcp", extra = ["cli"] }, { name = "quart" }, { name = "sqlalchemy", extra = ["asyncio"] }, - { name = "trafilatura" }, ] [package.optional-dependencies] @@ -752,11 +380,6 @@ dev = [ { name = "pytest-asyncio" }, { name = "ruff" }, ] -voice = [ - { name = "faster-whisper" }, - { name = "kokoro" }, - { name = "soundfile" }, -] [package.metadata] requires-dist = [ @@ -766,50 +389,38 @@ requires-dist = [ { name = "asyncpg", specifier = ">=0.29" }, { name = "bcrypt", specifier = ">=4.0" }, { name = "caldav", specifier = ">=1.3" }, - { name = "faster-whisper", marker = "extra == 'voice'", specifier = ">=1.0" }, - { name = "feedparser", specifier = ">=6.0" }, - { name = "html2text", specifier = ">=2024.2" }, + { name = "fastembed", specifier = ">=0.4" }, { name = "httpx", specifier = ">=0.27" }, { name = "hypercorn", specifier = ">=0.17" }, { name = "icalendar", specifier = ">=5.0" }, - { name = "kokoro", marker = "extra == 'voice'", specifier = ">=0.9" }, + { name = "mcp", extras = ["cli"], specifier = ">=1.0" }, { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0" }, { name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.23" }, - { name = "pywebpush", specifier = ">=2.0" }, { name = "quart", specifier = ">=0.19" }, { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.6" }, - { name = "soundfile", marker = "extra == 'voice'", specifier = ">=0.12" }, { name = "sqlalchemy", extras = ["asyncio"], specifier = ">=2.0" }, - { name = "trafilatura", specifier = ">=1.12" }, ] -provides-extras = ["dev", "voice"] +provides-extras = ["dev"] [[package]] -name = "faster-whisper" -version = "1.2.1" +name = "fastembed" +version = "0.8.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "av" }, - { name = "ctranslate2" }, { name = "huggingface-hub" }, + { name = "loguru" }, + { name = "mmh3" }, + { name = "numpy" }, { name = "onnxruntime" }, + { name = "pillow" }, + { name = "py-rust-stemmers" }, + { name = "requests" }, { name = "tokenizers" }, { name = "tqdm" }, ] +sdist = { url = "https://files.pythonhosted.org/packages/26/25/58865e36b6e8a9a0d0ff905b5601aa30db97956327c0df42ec4ed6accc21/fastembed-0.8.0.tar.gz", hash = "sha256:75966edfa8b006ee78514c726bd7f6a50721dadc89305279052be9db72fd53e8", size = 75115, upload-time = "2026-03-23T16:34:41.699Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/05/99/49ee85903dee060d9f08297b4a342e5e0bcfca2f027a07b4ee0a38ab13f9/faster_whisper-1.2.1-py3-none-any.whl", hash = "sha256:79a66ad50688c0b794dd501dc340a736992a6342f7f95e5811be60b5224a26a7", size = 1118909, upload-time = "2025-10-31T11:35:47.794Z" }, -] - -[[package]] -name = "feedparser" -version = "6.0.12" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "sgmllib3k" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/dc/79/db7edb5e77d6dfbc54d7d9df72828be4318275b2e580549ff45a962f6461/feedparser-6.0.12.tar.gz", hash = "sha256:64f76ce90ae3e8ef5d1ede0f8d3b50ce26bcce71dd8ae5e82b1cd2d4a5f94228", size = 286579, upload-time = "2025-09-10T13:33:59.486Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4e/eb/c96d64137e29ae17d83ad2552470bafe3a7a915e85434d9942077d7fd011/feedparser-6.0.12-py3-none-any.whl", hash = "sha256:6bbff10f5a52662c00a2e3f86a38928c37c48f77b3c511aedcd51de933549324", size = 81480, upload-time = "2025-09-10T13:33:58.022Z" }, + { url = "https://files.pythonhosted.org/packages/2a/e8/26b7d78bb8972498c467ca34cb12ee2e60d26ba5eae6d8443189a1af37a5/fastembed-0.8.0-py3-none-any.whl", hash = "sha256:40bee672657574a1009e35ec50030a55f2b426842cb011845379817641bbbbd0", size = 116572, upload-time = "2026-03-23T16:34:40.69Z" }, ] [[package]] @@ -846,47 +457,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e8/2d/d2a548598be01649e2d46231d151a6c56d10b964d94043a335ae56ea2d92/flatbuffers-25.12.19-py2.py3-none-any.whl", hash = "sha256:7634f50c427838bb021c2d66a3d1168e9d199b0607e6329399f04846d42e20b4", size = 26661, upload-time = "2025-12-19T23:16:13.622Z" }, ] -[[package]] -name = "frozenlist" -version = "1.8.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875, upload-time = "2025-10-06T05:38:17.865Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f1/c8/85da824b7e7b9b6e7f7705b2ecaf9591ba6f79c1177f324c2735e41d36a2/frozenlist-1.8.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cee686f1f4cadeb2136007ddedd0aaf928ab95216e7691c63e50a8ec066336d0", size = 86127, upload-time = "2025-10-06T05:37:08.438Z" }, - { url = "https://files.pythonhosted.org/packages/8e/e8/a1185e236ec66c20afd72399522f142c3724c785789255202d27ae992818/frozenlist-1.8.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:119fb2a1bd47307e899c2fac7f28e85b9a543864df47aa7ec9d3c1b4545f096f", size = 49698, upload-time = "2025-10-06T05:37:09.48Z" }, - { url = "https://files.pythonhosted.org/packages/a1/93/72b1736d68f03fda5fdf0f2180fb6caaae3894f1b854d006ac61ecc727ee/frozenlist-1.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4970ece02dbc8c3a92fcc5228e36a3e933a01a999f7094ff7c23fbd2beeaa67c", size = 49749, upload-time = "2025-10-06T05:37:10.569Z" }, - { url = "https://files.pythonhosted.org/packages/a7/b2/fabede9fafd976b991e9f1b9c8c873ed86f202889b864756f240ce6dd855/frozenlist-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cba69cb73723c3f329622e34bdbf5ce1f80c21c290ff04256cff1cd3c2036ed2", size = 231298, upload-time = "2025-10-06T05:37:11.993Z" }, - { url = "https://files.pythonhosted.org/packages/3a/3b/d9b1e0b0eed36e70477ffb8360c49c85c8ca8ef9700a4e6711f39a6e8b45/frozenlist-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:778a11b15673f6f1df23d9586f83c4846c471a8af693a22e066508b77d201ec8", size = 232015, upload-time = "2025-10-06T05:37:13.194Z" }, - { url = "https://files.pythonhosted.org/packages/dc/94/be719d2766c1138148564a3960fc2c06eb688da592bdc25adcf856101be7/frozenlist-1.8.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686", size = 225038, upload-time = "2025-10-06T05:37:14.577Z" }, - { url = "https://files.pythonhosted.org/packages/e4/09/6712b6c5465f083f52f50cf74167b92d4ea2f50e46a9eea0523d658454ae/frozenlist-1.8.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:97260ff46b207a82a7567b581ab4190bd4dfa09f4db8a8b49d1a958f6aa4940e", size = 240130, upload-time = "2025-10-06T05:37:15.781Z" }, - { url = "https://files.pythonhosted.org/packages/f8/d4/cd065cdcf21550b54f3ce6a22e143ac9e4836ca42a0de1022da8498eac89/frozenlist-1.8.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54b2077180eb7f83dd52c40b2750d0a9f175e06a42e3213ce047219de902717a", size = 242845, upload-time = "2025-10-06T05:37:17.037Z" }, - { url = "https://files.pythonhosted.org/packages/62/c3/f57a5c8c70cd1ead3d5d5f776f89d33110b1addae0ab010ad774d9a44fb9/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2f05983daecab868a31e1da44462873306d3cbfd76d1f0b5b69c473d21dbb128", size = 229131, upload-time = "2025-10-06T05:37:18.221Z" }, - { url = "https://files.pythonhosted.org/packages/6c/52/232476fe9cb64f0742f3fde2b7d26c1dac18b6d62071c74d4ded55e0ef94/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:33f48f51a446114bc5d251fb2954ab0164d5be02ad3382abcbfe07e2531d650f", size = 240542, upload-time = "2025-10-06T05:37:19.771Z" }, - { url = "https://files.pythonhosted.org/packages/5f/85/07bf3f5d0fb5414aee5f47d33c6f5c77bfe49aac680bfece33d4fdf6a246/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:154e55ec0655291b5dd1b8731c637ecdb50975a2ae70c606d100750a540082f7", size = 237308, upload-time = "2025-10-06T05:37:20.969Z" }, - { url = "https://files.pythonhosted.org/packages/11/99/ae3a33d5befd41ac0ca2cc7fd3aa707c9c324de2e89db0e0f45db9a64c26/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:4314debad13beb564b708b4a496020e5306c7333fa9a3ab90374169a20ffab30", size = 238210, upload-time = "2025-10-06T05:37:22.252Z" }, - { url = "https://files.pythonhosted.org/packages/b2/60/b1d2da22f4970e7a155f0adde9b1435712ece01b3cd45ba63702aea33938/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:073f8bf8becba60aa931eb3bc420b217bb7d5b8f4750e6f8b3be7f3da85d38b7", size = 231972, upload-time = "2025-10-06T05:37:23.5Z" }, - { url = "https://files.pythonhosted.org/packages/3f/ab/945b2f32de889993b9c9133216c068b7fcf257d8595a0ac420ac8677cab0/frozenlist-1.8.0-cp314-cp314-win32.whl", hash = "sha256:bac9c42ba2ac65ddc115d930c78d24ab8d4f465fd3fc473cdedfccadb9429806", size = 40536, upload-time = "2025-10-06T05:37:25.581Z" }, - { url = "https://files.pythonhosted.org/packages/59/ad/9caa9b9c836d9ad6f067157a531ac48b7d36499f5036d4141ce78c230b1b/frozenlist-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:3e0761f4d1a44f1d1a47996511752cf3dcec5bbdd9cc2b4fe595caf97754b7a0", size = 44330, upload-time = "2025-10-06T05:37:26.928Z" }, - { url = "https://files.pythonhosted.org/packages/82/13/e6950121764f2676f43534c555249f57030150260aee9dcf7d64efda11dd/frozenlist-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:d1eaff1d00c7751b7c6662e9c5ba6eb2c17a2306ba5e2a37f24ddf3cc953402b", size = 40627, upload-time = "2025-10-06T05:37:28.075Z" }, - { url = "https://files.pythonhosted.org/packages/c0/c7/43200656ecc4e02d3f8bc248df68256cd9572b3f0017f0a0c4e93440ae23/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d3bb933317c52d7ea5004a1c442eef86f426886fba134ef8cf4226ea6ee1821d", size = 89238, upload-time = "2025-10-06T05:37:29.373Z" }, - { url = "https://files.pythonhosted.org/packages/d1/29/55c5f0689b9c0fb765055629f472c0de484dcaf0acee2f7707266ae3583c/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8009897cdef112072f93a0efdce29cd819e717fd2f649ee3016efd3cd885a7ed", size = 50738, upload-time = "2025-10-06T05:37:30.792Z" }, - { url = "https://files.pythonhosted.org/packages/ba/7d/b7282a445956506fa11da8c2db7d276adcbf2b17d8bb8407a47685263f90/frozenlist-1.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2c5dcbbc55383e5883246d11fd179782a9d07a986c40f49abe89ddf865913930", size = 51739, upload-time = "2025-10-06T05:37:32.127Z" }, - { url = "https://files.pythonhosted.org/packages/62/1c/3d8622e60d0b767a5510d1d3cf21065b9db874696a51ea6d7a43180a259c/frozenlist-1.8.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:39ecbc32f1390387d2aa4f5a995e465e9e2f79ba3adcac92d68e3e0afae6657c", size = 284186, upload-time = "2025-10-06T05:37:33.21Z" }, - { url = "https://files.pythonhosted.org/packages/2d/14/aa36d5f85a89679a85a1d44cd7a6657e0b1c75f61e7cad987b203d2daca8/frozenlist-1.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92db2bf818d5cc8d9c1f1fc56b897662e24ea5adb36ad1f1d82875bd64e03c24", size = 292196, upload-time = "2025-10-06T05:37:36.107Z" }, - { url = "https://files.pythonhosted.org/packages/05/23/6bde59eb55abd407d34f77d39a5126fb7b4f109a3f611d3929f14b700c66/frozenlist-1.8.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2dc43a022e555de94c3b68a4ef0b11c4f747d12c024a520c7101709a2144fb37", size = 273830, upload-time = "2025-10-06T05:37:37.663Z" }, - { url = "https://files.pythonhosted.org/packages/d2/3f/22cff331bfad7a8afa616289000ba793347fcd7bc275f3b28ecea2a27909/frozenlist-1.8.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb89a7f2de3602cfed448095bab3f178399646ab7c61454315089787df07733a", size = 294289, upload-time = "2025-10-06T05:37:39.261Z" }, - { url = "https://files.pythonhosted.org/packages/a4/89/5b057c799de4838b6c69aa82b79705f2027615e01be996d2486a69ca99c4/frozenlist-1.8.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:33139dc858c580ea50e7e60a1b0ea003efa1fd42e6ec7fdbad78fff65fad2fd2", size = 300318, upload-time = "2025-10-06T05:37:43.213Z" }, - { url = "https://files.pythonhosted.org/packages/30/de/2c22ab3eb2a8af6d69dc799e48455813bab3690c760de58e1bf43b36da3e/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:168c0969a329b416119507ba30b9ea13688fafffac1b7822802537569a1cb0ef", size = 282814, upload-time = "2025-10-06T05:37:45.337Z" }, - { url = "https://files.pythonhosted.org/packages/59/f7/970141a6a8dbd7f556d94977858cfb36fa9b66e0892c6dd780d2219d8cd8/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:28bd570e8e189d7f7b001966435f9dac6718324b5be2990ac496cf1ea9ddb7fe", size = 291762, upload-time = "2025-10-06T05:37:46.657Z" }, - { url = "https://files.pythonhosted.org/packages/c1/15/ca1adae83a719f82df9116d66f5bb28bb95557b3951903d39135620ef157/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b2a095d45c5d46e5e79ba1e5b9cb787f541a8dee0433836cea4b96a2c439dcd8", size = 289470, upload-time = "2025-10-06T05:37:47.946Z" }, - { url = "https://files.pythonhosted.org/packages/ac/83/dca6dc53bf657d371fbc88ddeb21b79891e747189c5de990b9dfff2ccba1/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:eab8145831a0d56ec9c4139b6c3e594c7a83c2c8be25d5bcf2d86136a532287a", size = 289042, upload-time = "2025-10-06T05:37:49.499Z" }, - { url = "https://files.pythonhosted.org/packages/96/52/abddd34ca99be142f354398700536c5bd315880ed0a213812bc491cff5e4/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:974b28cf63cc99dfb2188d8d222bc6843656188164848c4f679e63dae4b0708e", size = 283148, upload-time = "2025-10-06T05:37:50.745Z" }, - { url = "https://files.pythonhosted.org/packages/af/d3/76bd4ed4317e7119c2b7f57c3f6934aba26d277acc6309f873341640e21f/frozenlist-1.8.0-cp314-cp314t-win32.whl", hash = "sha256:342c97bf697ac5480c0a7ec73cd700ecfa5a8a40ac923bd035484616efecc2df", size = 44676, upload-time = "2025-10-06T05:37:52.222Z" }, - { url = "https://files.pythonhosted.org/packages/89/76/c615883b7b521ead2944bb3480398cbb07e12b7b4e4d073d3752eb721558/frozenlist-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:06be8f67f39c8b1dc671f5d83aaefd3358ae5cdcf8314552c57e7ed3e6475bdd", size = 49451, upload-time = "2025-10-06T05:37:53.425Z" }, - { url = "https://files.pythonhosted.org/packages/e0/a3/5982da14e113d07b325230f95060e2169f5311b1017ea8af2a29b374c289/frozenlist-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:102e6314ca4da683dca92e3b1355490fed5f313b768500084fbe6371fddfdb79", size = 42507, upload-time = "2025-10-06T05:37:54.513Z" }, - { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409, upload-time = "2025-10-06T05:38:16.721Z" }, -] - [[package]] name = "fsspec" version = "2026.3.0" @@ -978,40 +548,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/07/c6/80c95b1b2b94682a72cbdbfb85b81ae2daffa4291fbfa1b1464502ede10d/hpack-4.1.0-py3-none-any.whl", hash = "sha256:157ac792668d995c657d93111f46b4535ed114f0c9c8d672271bbec7eae1b496", size = 34357, upload-time = "2025-01-22T21:44:56.92Z" }, ] -[[package]] -name = "html2text" -version = "2025.4.15" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f8/27/e158d86ba1e82967cc2f790b0cb02030d4a8bef58e0c79a8590e9678107f/html2text-2025.4.15.tar.gz", hash = "sha256:948a645f8f0bc3abe7fd587019a2197a12436cd73d0d4908af95bfc8da337588", size = 64316, upload-time = "2025-04-15T04:02:30.045Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1d/84/1a0f9555fd5f2b1c924ff932d99b40a0f8a6b12f6dd625e2a47f415b00ea/html2text-2025.4.15-py3-none-any.whl", hash = "sha256:00569167ffdab3d7767a4cdf589b7f57e777a5ed28d12907d8c58769ec734acc", size = 34656, upload-time = "2025-04-15T04:02:28.44Z" }, -] - -[[package]] -name = "htmldate" -version = "1.9.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "charset-normalizer" }, - { name = "dateparser" }, - { name = "lxml" }, - { name = "python-dateutil" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/9d/10/ead9dabc999f353c3aa5d0dc0835b1e355215a5ecb489a7f4ef2ddad5e33/htmldate-1.9.4.tar.gz", hash = "sha256:1129063e02dd0354b74264de71e950c0c3fcee191178321418ccad2074cc8ed0", size = 44690, upload-time = "2025-11-04T17:46:44.983Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a1/bd/adfcdaaad5805c0c5156aeefd64c1e868c05e9c1cd6fd21751f168cd88c7/htmldate-1.9.4-py3-none-any.whl", hash = "sha256:1b94bcc4e08232a5b692159903acf95548b6a7492dddca5bb123d89d6325921c", size = 31558, upload-time = "2025-11-04T17:46:43.258Z" }, -] - -[[package]] -name = "http-ece" -version = "1.2.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cryptography" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7c/af/249d1576653b69c20b9ac30e284b63bd94af6a175d72d87813235caf2482/http_ece-1.2.1.tar.gz", hash = "sha256:8c6ab23116bbf6affda894acfd5f2ca0fb8facbcbb72121c11c75c33e7ce8cff", size = 8830, upload-time = "2024-08-08T00:10:47.301Z" } - [[package]] name = "httpcore" version = "1.0.9" @@ -1040,6 +576,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, ] +[[package]] +name = "httpx-sse" +version = "0.4.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0f/4c/751061ffa58615a32c31b2d82e8482be8dd4a89154f003147acee90f2be9/httpx_sse-0.4.3.tar.gz", hash = "sha256:9b1ed0127459a66014aec3c56bebd93da3c1bc8bb6618c8082039a44889a755d", size = 15943, upload-time = "2025-10-10T21:48:22.271Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/fd/6668e5aec43ab844de6fc74927e155a3b37bf40d7c3790e49fc0406b6578/httpx_sse-0.4.3-py3-none-any.whl", hash = "sha256:0ac1c9fe3c0afad2e0ebb25a934a59f4c7823b60792691f779fad2c5568830fc", size = 8960, upload-time = "2025-10-10T21:48:21.158Z" }, +] + [[package]] name = "huggingface-hub" version = "1.10.1" @@ -1128,15 +673,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, ] -[[package]] -name = "isodate" -version = "0.7.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/54/4d/e940025e2ce31a8ce1202635910747e5a87cc3a6a6bb2d00973375014749/isodate-0.7.2.tar.gz", hash = "sha256:4cd1aa0f43ca76f4a6c6c0292a85f40b35ec2e43e315b59f06e6d32171a953e6", size = 29705, upload-time = "2024-10-08T23:04:11.5Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/15/aa/0aca39a37d3c7eb941ba736ede56d689e7be91cab5d9ca846bde3999eba6/isodate-0.7.2-py3-none-any.whl", hash = "sha256:28009937d8031054830160fce6d409ed342816b543597cece116d966c6d99e15", size = 22320, upload-time = "2024-10-08T23:04:09.501Z" }, -] - [[package]] name = "itsdangerous" version = "2.2.0" @@ -1197,15 +733,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, ] -[[package]] -name = "joblib" -version = "1.5.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/41/f2/d34e8b3a08a9cc79a50b2208a93dce981fe615b64d5a4d4abee421d898df/joblib-1.5.3.tar.gz", hash = "sha256:8561a3269e6801106863fd0d6d84bb737be9e7631e33aaed3fb9ce5953688da3", size = 331603, upload-time = "2025-12-15T08:41:46.427Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/91/984aca2ec129e2757d1e4e3c81c3fcda9d0f85b74670a094cc443d9ee949/joblib-1.5.3-py3-none-any.whl", hash = "sha256:5fc3c5039fc5ca8c0276333a188bbd59d6b7ab37fe6632daa76bc7f9ec18e713", size = 309071, upload-time = "2025-12-15T08:41:44.973Z" }, -] - [[package]] name = "jsonschema" version = "4.26.0" @@ -1233,44 +760,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" }, ] -[[package]] -name = "justext" -version = "3.0.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "lxml", extra = ["html-clean"] }, -] -sdist = { url = "https://files.pythonhosted.org/packages/49/f3/45890c1b314f0d04e19c1c83d534e611513150939a7cf039664d9ab1e649/justext-3.0.2.tar.gz", hash = "sha256:13496a450c44c4cd5b5a75a5efcd9996066d2a189794ea99a49949685a0beb05", size = 828521, upload-time = "2025-02-25T20:21:49.934Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f2/ac/52f4e86d1924a7fc05af3aeb34488570eccc39b4af90530dd6acecdf16b5/justext-3.0.2-py2.py3-none-any.whl", hash = "sha256:62b1c562b15c3c6265e121cc070874243a443bfd53060e869393f09d6b6cc9a7", size = 837940, upload-time = "2025-02-25T20:21:44.179Z" }, -] - -[[package]] -name = "kokoro" -version = "0.9.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "huggingface-hub" }, - { name = "loguru" }, - { name = "misaki", extra = ["en"] }, - { name = "numpy" }, - { name = "torch" }, - { name = "transformers" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e8/48/88b8cdf28b068d070195c2817175549dee48e7682e3ab8994bee5f69217e/kokoro-0.9.4.tar.gz", hash = "sha256:fbf633262797f8cf46fdac3315cf9cade67dc8b762c0feccf334892772fb9ac4", size = 26215928, upload-time = "2025-04-05T22:01:35.294Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ea/cc/75f41633c75224ba820a4533163bc8b070b6bf25416014074c63284c2d4e/kokoro-0.9.4-py3-none-any.whl", hash = "sha256:a129dc6364a286bd6a92c396e9862459d3d3e45f2c15596ed5a94dcee5789efd", size = 32592, upload-time = "2025-04-05T22:01:23.018Z" }, -] - -[[package]] -name = "language-tags" -version = "1.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e0/7e/b6a0efe4fee11e9742c1baaedf7c574084238a70b03c1d8eb2761383848f/language_tags-1.2.0.tar.gz", hash = "sha256:e934acba3e3dc85f867703eca421847a9ab7b7679b11b5d5cfd096febbf8bde6", size = 207901, upload-time = "2023-01-11T18:38:07.893Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b0/42/327554649ed2dd5ce59d3f5da176c7be20f9352c7c6c51597293660b7b08/language_tags-1.2.0-py3-none-any.whl", hash = "sha256:d815604622242fdfbbfd747b40c31213617fd03734a267f2e39ee4bd73c88722", size = 213449, upload-time = "2023-01-11T18:38:05.692Z" }, -] - [[package]] name = "loguru" version = "0.7.3" @@ -1328,23 +817,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/99/39/a0f486360a6f1b36fd2f5eb62d037652bef503d82b6f853aee6664cdfcac/lxml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:cbc7ce67f85b92db97c92219985432be84dc1ba9a028e68c6933e89551234df2", size = 3816374, upload-time = "2026-04-09T14:37:45.532Z" }, ] -[package.optional-dependencies] -html-clean = [ - { name = "lxml-html-clean" }, -] - -[[package]] -name = "lxml-html-clean" -version = "0.4.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "lxml" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/9a/a4/5c62acfacd69ff4f5db395100f5cfb9b54e7ac8c69a235e4e939fd13f021/lxml_html_clean-0.4.4.tar.gz", hash = "sha256:58f39a9d632711202ed1d6d0b9b47a904e306c85de5761543b90e3e3f736acfb", size = 23899, upload-time = "2026-02-27T09:35:52.911Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/76/7ffc1d3005cf7749123bc47cb3ea343cd97b0ac2211bab40f57283577d0e/lxml_html_clean-0.4.4-py3-none-any.whl", hash = "sha256:ce2ef506614ecb85ee1c5fe0a2aa45b06a19514ec7949e9c8f34f06925cfabcb", size = 14565, upload-time = "2026-02-27T09:35:51.86Z" }, -] - [[package]] name = "mako" version = "1.3.10" @@ -1399,6 +871,37 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" }, ] +[[package]] +name = "mcp" +version = "1.27.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "httpx" }, + { name = "httpx-sse" }, + { name = "jsonschema" }, + { name = "pydantic" }, + { name = "pydantic-settings" }, + { name = "pyjwt", extra = ["crypto"] }, + { name = "python-multipart" }, + { name = "pywin32", marker = "sys_platform == 'win32'" }, + { name = "sse-starlette" }, + { name = "starlette" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, + { name = "uvicorn", marker = "sys_platform != 'emscripten'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/27/3c/347cf965d313f5d41764e7d46bea6ffe7d9ef13b983cc429b0340962a082/mcp-1.27.2.tar.gz", hash = "sha256:8e02db104096d1c25b28e64bde29a5c32b31bc241710213e12fd4d84985bdfef", size = 621116, upload-time = "2026-05-29T17:16:04.039Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c9/11/252c6f971dc4f16af1d98a1c469d8ba523aab00d1bb76b4d3bc1ff32eacc/mcp-1.27.2-py3-none-any.whl", hash = "sha256:d6ff5160c6ca65d93013626efb3fc249de683c30b2d8570755ceddd490344de5", size = 220498, upload-time = "2026-05-29T17:16:02.442Z" }, +] + +[package.optional-dependencies] +cli = [ + { name = "python-dotenv" }, + { name = "typer" }, +] + [[package]] name = "mdurl" version = "0.1.2" @@ -1409,25 +912,48 @@ wheels = [ ] [[package]] -name = "misaki" -version = "0.9.4" +name = "mmh3" +version = "5.2.1" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "addict" }, - { name = "regex" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1a/c7/fb01370a76585b46595a01b52f18e65c8ba6d7a313a05e5d9fff0a8e1c69/misaki-0.9.4.tar.gz", hash = "sha256:3960fa3e6de179a90ee8e628446a4a4f6b8c730b6e3410999cf396189f4d9c40", size = 3756765, upload-time = "2025-04-05T21:57:14.186Z" } +sdist = { url = "https://files.pythonhosted.org/packages/91/1a/edb23803a168f070ded7a3014c6d706f63b90c84ccc024f89d794a3b7a6d/mmh3-5.2.1.tar.gz", hash = "sha256:bbea5b775f0ac84945191fb83f845a6fd9a21a03ea7f2e187defac7e401616ad", size = 33775, upload-time = "2026-03-05T15:55:57.716Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/82/ec/0ee4110ddb54278b8f21c40a140370ae8f687036c4edf578316602697c56/misaki-0.9.4-py3-none-any.whl", hash = "sha256:90e2eeb169786c014c429e5058d2ea6bcd02d651f2a24450ba6c9ffc0f8da15a", size = 3617774, upload-time = "2025-04-05T21:57:10.678Z" }, -] - -[package.optional-dependencies] -en = [ - { name = "espeakng-loader" }, - { name = "num2words" }, - { name = "phonemizer-fork" }, - { name = "spacy" }, - { name = "spacy-curated-transformers" }, + { url = "https://files.pythonhosted.org/packages/63/b4/65bc1fb2bb7f83e91c30865023b1847cf89a5f237165575e8c83aa536584/mmh3-5.2.1-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:d771f085fcdf4035786adfb1d8db026df1eb4b41dac1c3d070d1e49512843227", size = 40794, upload-time = "2026-03-05T15:55:09.773Z" }, + { url = "https://files.pythonhosted.org/packages/c4/86/7168b3d83be8eb553897b1fac9da8bbb06568e5cfe555ffc329ebb46f59d/mmh3-5.2.1-cp314-cp314-android_24_x86_64.whl", hash = "sha256:7f196cd7910d71e9d9860da0ff7a77f64d22c1ad931f1dd18559a06e03109fc0", size = 41923, upload-time = "2026-03-05T15:55:10.924Z" }, + { url = "https://files.pythonhosted.org/packages/bf/9b/b653ab611c9060ce8ff0ba25c0226757755725e789292f3ca138a58082cd/mmh3-5.2.1-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:b1f12bd684887a0a5d55e6363ca87056f361e45451105012d329b86ec19dbe0b", size = 39131, upload-time = "2026-03-05T15:55:11.961Z" }, + { url = "https://files.pythonhosted.org/packages/9b/b4/5a2e0d34ab4d33543f01121e832395ea510132ea8e52cdf63926d9d81754/mmh3-5.2.1-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:d106493a60dcb4aef35a0fac85105e150a11cf8bc2b0d388f5a33272d756c966", size = 39825, upload-time = "2026-03-05T15:55:13.013Z" }, + { url = "https://files.pythonhosted.org/packages/bd/69/81699a8f39a3f8d368bec6443435c0c392df0d200ad915bf0d222b588e03/mmh3-5.2.1-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:44983e45310ee5b9f73397350251cdf6e63a466406a105f1d16cb5baa659270b", size = 40344, upload-time = "2026-03-05T15:55:14.026Z" }, + { url = "https://files.pythonhosted.org/packages/0c/b3/71c8c775807606e8fd8acc5c69016e1caf3200d50b50b6dd4b40ce10b76c/mmh3-5.2.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:368625fb01666655985391dbad3860dc0ba7c0d6b9125819f3121ee7292b4ac8", size = 56291, upload-time = "2026-03-05T15:55:15.137Z" }, + { url = "https://files.pythonhosted.org/packages/6f/75/2c24517d4b2ce9e4917362d24f274d3d541346af764430249ddcc4cb3a08/mmh3-5.2.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:72d1cc63bcc91e14933f77d51b3df899d6a07d184ec515ea7f56bff659e124d7", size = 40575, upload-time = "2026-03-05T15:55:16.518Z" }, + { url = "https://files.pythonhosted.org/packages/bf/b9/e4a360164365ac9f07a25f0f7928e3a66eb9ecc989384060747aa170e6aa/mmh3-5.2.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e8b4b5580280b9265af3e0409974fb79c64cf7523632d03fbf11df18f8b0181e", size = 40052, upload-time = "2026-03-05T15:55:17.735Z" }, + { url = "https://files.pythonhosted.org/packages/97/ca/120d92223a7546131bbbc31c9174168ee7a73b1366f5463ffe69d9e691fe/mmh3-5.2.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:4cbbde66f1183db040daede83dd86c06d663c5bb2af6de1142b7c8c37923dd74", size = 97311, upload-time = "2026-03-05T15:55:18.959Z" }, + { url = "https://files.pythonhosted.org/packages/b6/71/c1a60c1652b8813ef9de6d289784847355417ee0f2980bca002fe87f4ae5/mmh3-5.2.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8ff038d52ef6aa0f309feeba00c5095c9118d0abf787e8e8454d6048db2037fc", size = 103279, upload-time = "2026-03-05T15:55:20.448Z" }, + { url = "https://files.pythonhosted.org/packages/48/29/ad97f4be1509cdcb28ae32c15593ce7c415db47ace37f8fad35b493faa9a/mmh3-5.2.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a4130d0b9ce5fad6af07421b1aecc7e079519f70d6c05729ab871794eded8617", size = 106290, upload-time = "2026-03-05T15:55:21.6Z" }, + { url = "https://files.pythonhosted.org/packages/77/29/1f86d22e281bd8827ba373600a4a8b0c0eae5ca6aa55b9a8c26d2a34decc/mmh3-5.2.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6e0bfe77d238308839699944164b96a2eeccaf55f2af400f54dc20669d8d5f2", size = 113116, upload-time = "2026-03-05T15:55:22.826Z" }, + { url = "https://files.pythonhosted.org/packages/a7/7c/339971ea7ed4c12d98f421f13db3ea576a9114082ccb59d2d1a0f00ccac1/mmh3-5.2.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f963eafc0a77a6c0562397da004f5876a9bcf7265a7bcc3205e29636bc4a1312", size = 120740, upload-time = "2026-03-05T15:55:24.3Z" }, + { url = "https://files.pythonhosted.org/packages/e4/92/3c7c4bdb8e926bb3c972d1e2907d77960c1c4b250b41e8366cf20c6e4373/mmh3-5.2.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:92883836caf50d5255be03d988d75bc93e3f86ba247b7ca137347c323f731deb", size = 99143, upload-time = "2026-03-05T15:55:25.456Z" }, + { url = "https://files.pythonhosted.org/packages/df/0a/33dd8706e732458c8375eae63c981292de07a406bad4ec03e5269654aa2c/mmh3-5.2.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:57b52603e89355ff318025dd55158f6e71396c0f1f609d548e9ea9c94cc6ce0a", size = 98703, upload-time = "2026-03-05T15:55:26.723Z" }, + { url = "https://files.pythonhosted.org/packages/51/04/76bbce05df76cbc3d396f13b2ea5b1578ef02b6a5187e132c6c33f99d596/mmh3-5.2.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:f40a95186a72fa0b67d15fef0f157bfcda00b4f59c8a07cbe5530d41ac35d105", size = 106484, upload-time = "2026-03-05T15:55:28.214Z" }, + { url = "https://files.pythonhosted.org/packages/d3/8f/c6e204a2c70b719c1f62ffd9da27aef2dddcba875ea9c31ca0e87b975a46/mmh3-5.2.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:58370d05d033ee97224c81263af123dea3d931025030fd34b61227a768a8858a", size = 110012, upload-time = "2026-03-05T15:55:29.532Z" }, + { url = "https://files.pythonhosted.org/packages/e3/37/7181efd8e39db386c1ebc3e6b7d1f702a09d7c1197a6f2742ed6b5c16597/mmh3-5.2.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7be6dfb49e48fd0a7d91ff758a2b51336f1cd21f9d44b20f6801f072bd080cdd", size = 97508, upload-time = "2026-03-05T15:55:31.01Z" }, + { url = "https://files.pythonhosted.org/packages/42/0f/afa7ca2615fd85e1469474bb860e381443d0b868c083b62b41cb1d7ca32f/mmh3-5.2.1-cp314-cp314-win32.whl", hash = "sha256:54fe8518abe06a4c3852754bfd498b30cc58e667f376c513eac89a244ce781a4", size = 41387, upload-time = "2026-03-05T15:55:32.403Z" }, + { url = "https://files.pythonhosted.org/packages/71/0d/46d42a260ee1357db3d486e6c7a692e303c017968e14865e00efa10d09fc/mmh3-5.2.1-cp314-cp314-win_amd64.whl", hash = "sha256:3f796b535008708846044c43302719c6956f39ca2d93f2edda5319e79a29efbb", size = 42101, upload-time = "2026-03-05T15:55:33.646Z" }, + { url = "https://files.pythonhosted.org/packages/a4/7b/848a8378059d96501a41159fca90d6a99e89736b0afbe8e8edffeac8c74b/mmh3-5.2.1-cp314-cp314-win_arm64.whl", hash = "sha256:cd471ede0d802dd936b6fab28188302b2d497f68436025857ca72cd3810423fe", size = 39836, upload-time = "2026-03-05T15:55:35.026Z" }, + { url = "https://files.pythonhosted.org/packages/27/61/1dabea76c011ba8547c25d30c91c0ec22544487a8750997a27a0c9e1180b/mmh3-5.2.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:5174a697ce042fa77c407e05efe41e03aa56dae9ec67388055820fb48cf4c3ba", size = 57727, upload-time = "2026-03-05T15:55:36.162Z" }, + { url = "https://files.pythonhosted.org/packages/b7/32/731185950d1cf2d5e28979cc8593016ba1619a295faba10dda664a4931b5/mmh3-5.2.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:0a3984146e414684a6be2862d84fcb1035f4984851cb81b26d933bab6119bf00", size = 41308, upload-time = "2026-03-05T15:55:37.254Z" }, + { url = "https://files.pythonhosted.org/packages/76/aa/66c76801c24b8c9418b4edde9b5e57c75e72c94e29c48f707e3962534f18/mmh3-5.2.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:bd6e7d363aa93bd3421b30b6af97064daf47bc96005bddba67c5ffbc6df426b8", size = 40758, upload-time = "2026-03-05T15:55:38.61Z" }, + { url = "https://files.pythonhosted.org/packages/9e/bb/79a1f638a02f0ae389f706d13891e2fbf7d8c0a22ecde67ba828951bb60a/mmh3-5.2.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:113f78e7463a36dbbcea05bfe688efd7fa759d0f0c56e73c974d60dcfec3dfcc", size = 109670, upload-time = "2026-03-05T15:55:40.13Z" }, + { url = "https://files.pythonhosted.org/packages/26/94/8cd0e187a288985bcfc79bf5144d1d712df9dee74365f59d26e3a1865be6/mmh3-5.2.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7e8ec5f606e0809426d2440e0683509fb605a8820a21ebd120dcdba61b74ef7f", size = 117399, upload-time = "2026-03-05T15:55:42.076Z" }, + { url = "https://files.pythonhosted.org/packages/42/94/dfea6059bd5c5beda565f58a4096e43f4858fb6d2862806b8bbd12cbb284/mmh3-5.2.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:22b0f9971ec4e07e8223f2beebe96a6cfc779d940b6f27d26604040dd74d3a44", size = 120386, upload-time = "2026-03-05T15:55:43.481Z" }, + { url = "https://files.pythonhosted.org/packages/47/cb/f9c45e62aaa67220179f487772461d891bb582bb2f9783c944832c60efd9/mmh3-5.2.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:85ffc9920ffc39c5eee1e3ac9100c913a0973996fbad5111f939bbda49204bb7", size = 125924, upload-time = "2026-03-05T15:55:44.638Z" }, + { url = "https://files.pythonhosted.org/packages/a5/83/fe54a4a7c11bc9f623dfc1707decd034245602b076dfc1dcc771a4163170/mmh3-5.2.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7aec798c2b01aaa65a55f1124f3405804184373abb318a3091325aece235f67c", size = 135280, upload-time = "2026-03-05T15:55:45.866Z" }, + { url = "https://files.pythonhosted.org/packages/97/67/fe7e9e9c143daddd210cd22aef89cbc425d58ecf238d2b7d9eb0da974105/mmh3-5.2.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:55dbbd8ffbc40d1697d5e2d0375b08599dae8746b0b08dea05eee4ce81648fac", size = 110050, upload-time = "2026-03-05T15:55:47.074Z" }, + { url = "https://files.pythonhosted.org/packages/43/c4/6d4b09fcbef80794de447c9378e39eefc047156b290fa3dd2d5257ca8227/mmh3-5.2.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:6c85c38a279ca9295a69b9b088a2e48aa49737bb1b34e6a9dc6297c110e8d912", size = 111158, upload-time = "2026-03-05T15:55:48.239Z" }, + { url = "https://files.pythonhosted.org/packages/81/a6/ca51c864bdb30524beb055a6d8826db3906af0834ec8c41d097a6e8573d5/mmh3-5.2.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:6290289fa5fb4c70fd7f72016e03633d60388185483ff3b162912c81205ae2cf", size = 116890, upload-time = "2026-03-05T15:55:49.405Z" }, + { url = "https://files.pythonhosted.org/packages/cc/04/5a1fe2e2ad843d03e89af25238cbc4f6840a8bb6c4329a98ab694c71deda/mmh3-5.2.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:4fc6cd65dc4d2fdb2625e288939a3566e36127a84811a4913f02f3d5931da52d", size = 123121, upload-time = "2026-03-05T15:55:50.61Z" }, + { url = "https://files.pythonhosted.org/packages/af/4d/3c820c6f4897afd25905270a9f2330a23f77a207ea7356f7aadace7273c0/mmh3-5.2.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:623f938f6a039536cc02b7582a07a080f13fdfd48f87e63201d92d7e34d09a18", size = 110187, upload-time = "2026-03-05T15:55:52.143Z" }, + { url = "https://files.pythonhosted.org/packages/21/54/1d71cd143752361c0aebef16ad3f55926a6faf7b112d355745c1f8a25f7f/mmh3-5.2.1-cp314-cp314t-win32.whl", hash = "sha256:29bc3973676ae334412efdd367fcd11d036b7be3efc1ce2407ef8676dabfeb82", size = 41934, upload-time = "2026-03-05T15:55:53.564Z" }, + { url = "https://files.pythonhosted.org/packages/9d/e4/63a2a88f31d93dea03947cccc2a076946857e799ea4f7acdecbf43b324aa/mmh3-5.2.1-cp314-cp314t-win_amd64.whl", hash = "sha256:28cfab66577000b9505a0d068c731aee7ca85cd26d4d63881fab17857e0fe1fb", size = 43036, upload-time = "2026-03-05T15:55:55.252Z" }, + { url = "https://files.pythonhosted.org/packages/a0/0f/59204bf136d1201f8d7884cfbaf7498c5b4674e87a4c693f9bde63741ce1/mmh3-5.2.1-cp314-cp314t-win_arm64.whl", hash = "sha256:dfd51b4c56b673dfbc43d7d27ef857dd91124801e2806c69bb45585ce0fa019b", size = 40391, upload-time = "2026-03-05T15:55:56.697Z" }, ] [[package]] @@ -1439,84 +965,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" }, ] -[[package]] -name = "multidict" -version = "6.7.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1a/c2/c2d94cbe6ac1753f3fc980da97b3d930efe1da3af3c9f5125354436c073d/multidict-6.7.1.tar.gz", hash = "sha256:ec6652a1bee61c53a3e5776b6049172c53b6aaba34f18c9ad04f82712bac623d", size = 102010, upload-time = "2026-01-26T02:46:45.979Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/91/cc/db74228a8be41884a567e88a62fd589a913708fcf180d029898c17a9a371/multidict-6.7.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8f333ec9c5eb1b7105e3b84b53141e66ca05a19a605368c55450b6ba208cb9ee", size = 75190, upload-time = "2026-01-26T02:45:10.651Z" }, - { url = "https://files.pythonhosted.org/packages/d5/22/492f2246bb5b534abd44804292e81eeaf835388901f0c574bac4eeec73c5/multidict-6.7.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a407f13c188f804c759fc6a9f88286a565c242a76b27626594c133b82883b5c2", size = 44486, upload-time = "2026-01-26T02:45:11.938Z" }, - { url = "https://files.pythonhosted.org/packages/f1/4f/733c48f270565d78b4544f2baddc2fb2a245e5a8640254b12c36ac7ac68e/multidict-6.7.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0e161ddf326db5577c3a4cc2d8648f81456e8a20d40415541587a71620d7a7d1", size = 43219, upload-time = "2026-01-26T02:45:14.346Z" }, - { url = "https://files.pythonhosted.org/packages/24/bb/2c0c2287963f4259c85e8bcbba9182ced8d7fca65c780c38e99e61629d11/multidict-6.7.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1e3a8bb24342a8201d178c3b4984c26ba81a577c80d4d525727427460a50c22d", size = 245132, upload-time = "2026-01-26T02:45:15.712Z" }, - { url = "https://files.pythonhosted.org/packages/a7/f9/44d4b3064c65079d2467888794dea218d1601898ac50222ab8a9a8094460/multidict-6.7.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97231140a50f5d447d3164f994b86a0bed7cd016e2682f8650d6a9158e14fd31", size = 252420, upload-time = "2026-01-26T02:45:17.293Z" }, - { url = "https://files.pythonhosted.org/packages/8b/13/78f7275e73fa17b24c9a51b0bd9d73ba64bb32d0ed51b02a746eb876abe7/multidict-6.7.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6b10359683bd8806a200fd2909e7c8ca3a7b24ec1d8132e483d58e791d881048", size = 233510, upload-time = "2026-01-26T02:45:19.356Z" }, - { url = "https://files.pythonhosted.org/packages/4b/25/8167187f62ae3cbd52da7893f58cb036b47ea3fb67138787c76800158982/multidict-6.7.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:283ddac99f7ac25a4acadbf004cb5ae34480bbeb063520f70ce397b281859362", size = 264094, upload-time = "2026-01-26T02:45:20.834Z" }, - { url = "https://files.pythonhosted.org/packages/a1/e7/69a3a83b7b030cf283fb06ce074a05a02322359783424d7edf0f15fe5022/multidict-6.7.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:538cec1e18c067d0e6103aa9a74f9e832904c957adc260e61cd9d8cf0c3b3d37", size = 260786, upload-time = "2026-01-26T02:45:22.818Z" }, - { url = "https://files.pythonhosted.org/packages/fe/3b/8ec5074bcfc450fe84273713b4b0a0dd47c0249358f5d82eb8104ffe2520/multidict-6.7.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7eee46ccb30ff48a1e35bb818cc90846c6be2b68240e42a78599166722cea709", size = 248483, upload-time = "2026-01-26T02:45:24.368Z" }, - { url = "https://files.pythonhosted.org/packages/48/5a/d5a99e3acbca0e29c5d9cba8f92ceb15dce78bab963b308ae692981e3a5d/multidict-6.7.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fa263a02f4f2dd2d11a7b1bb4362aa7cb1049f84a9235d31adf63f30143469a0", size = 248403, upload-time = "2026-01-26T02:45:25.982Z" }, - { url = "https://files.pythonhosted.org/packages/35/48/e58cd31f6c7d5102f2a4bf89f96b9cf7e00b6c6f3d04ecc44417c00a5a3c/multidict-6.7.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:2e1425e2f99ec5bd36c15a01b690a1a2456209c5deed58f95469ffb46039ccbb", size = 240315, upload-time = "2026-01-26T02:45:27.487Z" }, - { url = "https://files.pythonhosted.org/packages/94/33/1cd210229559cb90b6786c30676bb0c58249ff42f942765f88793b41fdce/multidict-6.7.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:497394b3239fc6f0e13a78a3e1b61296e72bf1c5f94b4c4eb80b265c37a131cd", size = 245528, upload-time = "2026-01-26T02:45:28.991Z" }, - { url = "https://files.pythonhosted.org/packages/64/f2/6e1107d226278c876c783056b7db43d800bb64c6131cec9c8dfb6903698e/multidict-6.7.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:233b398c29d3f1b9676b4b6f75c518a06fcb2ea0b925119fb2c1bc35c05e1601", size = 258784, upload-time = "2026-01-26T02:45:30.503Z" }, - { url = "https://files.pythonhosted.org/packages/4d/c1/11f664f14d525e4a1b5327a82d4de61a1db604ab34c6603bb3c2cc63ad34/multidict-6.7.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:93b1818e4a6e0930454f0f2af7dfce69307ca03cdcfb3739bf4d91241967b6c1", size = 251980, upload-time = "2026-01-26T02:45:32.603Z" }, - { url = "https://files.pythonhosted.org/packages/e1/9f/75a9ac888121d0c5bbd4ecf4eead45668b1766f6baabfb3b7f66a410e231/multidict-6.7.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f33dc2a3abe9249ea5d8360f969ec7f4142e7ac45ee7014d8f8d5acddf178b7b", size = 243602, upload-time = "2026-01-26T02:45:34.043Z" }, - { url = "https://files.pythonhosted.org/packages/9a/e7/50bf7b004cc8525d80dbbbedfdc7aed3e4c323810890be4413e589074032/multidict-6.7.1-cp314-cp314-win32.whl", hash = "sha256:3ab8b9d8b75aef9df299595d5388b14530839f6422333357af1339443cff777d", size = 40930, upload-time = "2026-01-26T02:45:36.278Z" }, - { url = "https://files.pythonhosted.org/packages/e0/bf/52f25716bbe93745595800f36fb17b73711f14da59ed0bb2eba141bc9f0f/multidict-6.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:5e01429a929600e7dab7b166062d9bb54a5eed752384c7384c968c2afab8f50f", size = 45074, upload-time = "2026-01-26T02:45:37.546Z" }, - { url = "https://files.pythonhosted.org/packages/97/ab/22803b03285fa3a525f48217963da3a65ae40f6a1b6f6cf2768879e208f9/multidict-6.7.1-cp314-cp314-win_arm64.whl", hash = "sha256:4885cb0e817aef5d00a2e8451d4665c1808378dc27c2705f1bf4ef8505c0d2e5", size = 42471, upload-time = "2026-01-26T02:45:38.889Z" }, - { url = "https://files.pythonhosted.org/packages/e0/6d/f9293baa6146ba9507e360ea0292b6422b016907c393e2f63fc40ab7b7b5/multidict-6.7.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:0458c978acd8e6ea53c81eefaddbbee9c6c5e591f41b3f5e8e194780fe026581", size = 82401, upload-time = "2026-01-26T02:45:40.254Z" }, - { url = "https://files.pythonhosted.org/packages/7a/68/53b5494738d83558d87c3c71a486504d8373421c3e0dbb6d0db48ad42ee0/multidict-6.7.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:c0abd12629b0af3cf590982c0b413b1e7395cd4ec026f30986818ab95bfaa94a", size = 48143, upload-time = "2026-01-26T02:45:41.635Z" }, - { url = "https://files.pythonhosted.org/packages/37/e8/5284c53310dcdc99ce5d66563f6e5773531a9b9fe9ec7a615e9bc306b05f/multidict-6.7.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:14525a5f61d7d0c94b368a42cff4c9a4e7ba2d52e2672a7b23d84dc86fb02b0c", size = 46507, upload-time = "2026-01-26T02:45:42.99Z" }, - { url = "https://files.pythonhosted.org/packages/e4/fc/6800d0e5b3875568b4083ecf5f310dcf91d86d52573160834fb4bfcf5e4f/multidict-6.7.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:17307b22c217b4cf05033dabefe68255a534d637c6c9b0cc8382718f87be4262", size = 239358, upload-time = "2026-01-26T02:45:44.376Z" }, - { url = "https://files.pythonhosted.org/packages/41/75/4ad0973179361cdf3a113905e6e088173198349131be2b390f9fa4da5fc6/multidict-6.7.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a7e590ff876a3eaf1c02a4dfe0724b6e69a9e9de6d8f556816f29c496046e59", size = 246884, upload-time = "2026-01-26T02:45:47.167Z" }, - { url = "https://files.pythonhosted.org/packages/c3/9c/095bb28b5da139bd41fb9a5d5caff412584f377914bd8787c2aa98717130/multidict-6.7.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5fa6a95dfee63893d80a34758cd0e0c118a30b8dcb46372bf75106c591b77889", size = 225878, upload-time = "2026-01-26T02:45:48.698Z" }, - { url = "https://files.pythonhosted.org/packages/07/d0/c0a72000243756e8f5a277b6b514fa005f2c73d481b7d9e47cd4568aa2e4/multidict-6.7.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a0543217a6a017692aa6ae5cc39adb75e587af0f3a82288b1492eb73dd6cc2a4", size = 253542, upload-time = "2026-01-26T02:45:50.164Z" }, - { url = "https://files.pythonhosted.org/packages/c0/6b/f69da15289e384ecf2a68837ec8b5ad8c33e973aa18b266f50fe55f24b8c/multidict-6.7.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f99fe611c312b3c1c0ace793f92464d8cd263cc3b26b5721950d977b006b6c4d", size = 252403, upload-time = "2026-01-26T02:45:51.779Z" }, - { url = "https://files.pythonhosted.org/packages/a2/76/b9669547afa5a1a25cd93eaca91c0da1c095b06b6d2d8ec25b713588d3a1/multidict-6.7.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9004d8386d133b7e6135679424c91b0b854d2d164af6ea3f289f8f2761064609", size = 244889, upload-time = "2026-01-26T02:45:53.27Z" }, - { url = "https://files.pythonhosted.org/packages/7e/a9/a50d2669e506dad33cfc45b5d574a205587b7b8a5f426f2fbb2e90882588/multidict-6.7.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e628ef0e6859ffd8273c69412a2465c4be4a9517d07261b33334b5ec6f3c7489", size = 241982, upload-time = "2026-01-26T02:45:54.919Z" }, - { url = "https://files.pythonhosted.org/packages/c5/bb/1609558ad8b456b4827d3c5a5b775c93b87878fd3117ed3db3423dfbce1b/multidict-6.7.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:841189848ba629c3552035a6a7f5bf3b02eb304e9fea7492ca220a8eda6b0e5c", size = 232415, upload-time = "2026-01-26T02:45:56.981Z" }, - { url = "https://files.pythonhosted.org/packages/d8/59/6f61039d2aa9261871e03ab9dc058a550d240f25859b05b67fd70f80d4b3/multidict-6.7.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:ce1bbd7d780bb5a0da032e095c951f7014d6b0a205f8318308140f1a6aba159e", size = 240337, upload-time = "2026-01-26T02:45:58.698Z" }, - { url = "https://files.pythonhosted.org/packages/a1/29/fdc6a43c203890dc2ae9249971ecd0c41deaedfe00d25cb6564b2edd99eb/multidict-6.7.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b26684587228afed0d50cf804cc71062cc9c1cdf55051c4c6345d372947b268c", size = 248788, upload-time = "2026-01-26T02:46:00.862Z" }, - { url = "https://files.pythonhosted.org/packages/a9/14/a153a06101323e4cf086ecee3faadba52ff71633d471f9685c42e3736163/multidict-6.7.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:9f9af11306994335398293f9958071019e3ab95e9a707dc1383a35613f6abcb9", size = 242842, upload-time = "2026-01-26T02:46:02.824Z" }, - { url = "https://files.pythonhosted.org/packages/41/5f/604ae839e64a4a6efc80db94465348d3b328ee955e37acb24badbcd24d83/multidict-6.7.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b4938326284c4f1224178a560987b6cf8b4d38458b113d9b8c1db1a836e640a2", size = 240237, upload-time = "2026-01-26T02:46:05.898Z" }, - { url = "https://files.pythonhosted.org/packages/5f/60/c3a5187bf66f6fb546ff4ab8fb5a077cbdd832d7b1908d4365c7f74a1917/multidict-6.7.1-cp314-cp314t-win32.whl", hash = "sha256:98655c737850c064a65e006a3df7c997cd3b220be4ec8fe26215760b9697d4d7", size = 48008, upload-time = "2026-01-26T02:46:07.468Z" }, - { url = "https://files.pythonhosted.org/packages/0c/f7/addf1087b860ac60e6f382240f64fb99f8bfb532bb06f7c542b83c29ca61/multidict-6.7.1-cp314-cp314t-win_amd64.whl", hash = "sha256:497bde6223c212ba11d462853cfa4f0ae6ef97465033e7dc9940cdb3ab5b48e5", size = 53542, upload-time = "2026-01-26T02:46:08.809Z" }, - { url = "https://files.pythonhosted.org/packages/4c/81/4629d0aa32302ef7b2ec65c75a728cc5ff4fa410c50096174c1632e70b3e/multidict-6.7.1-cp314-cp314t-win_arm64.whl", hash = "sha256:2bbd113e0d4af5db41d5ebfe9ccaff89de2120578164f86a5d17d5a576d1e5b2", size = 44719, upload-time = "2026-01-26T02:46:11.146Z" }, - { url = "https://files.pythonhosted.org/packages/81/08/7036c080d7117f28a4af526d794aab6a84463126db031b007717c1a6676e/multidict-6.7.1-py3-none-any.whl", hash = "sha256:55d97cc6dae627efa6a6e548885712d4864b81110ac76fa4e534c03819fa4a56", size = 12319, upload-time = "2026-01-26T02:46:44.004Z" }, -] - -[[package]] -name = "murmurhash" -version = "1.0.15" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/23/2e/88c147931ea9725d634840d538622e94122bceaf346233349b7b5c62964b/murmurhash-1.0.15.tar.gz", hash = "sha256:58e2b27b7847f9e2a6edf10b47a8c8dd70a4705f45dccb7bf76aeadacf56ba01", size = 13291, upload-time = "2025-11-14T09:51:15.272Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1c/69/726df275edf07688146966e15eaaa23168100b933a2e1a29b37eb56c6db8/murmurhash-1.0.15-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:7c4280136b738e85ff76b4bdc4341d0b867ee753e73fd8b6994288080c040d0b", size = 28029, upload-time = "2025-11-14T09:50:44.124Z" }, - { url = "https://files.pythonhosted.org/packages/59/8f/24ecf9061bc2b20933df8aba47c73e904274ea8811c8300cab92f6f82372/murmurhash-1.0.15-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:d4d681f474830489e2ec1d912095cfff027fbaf2baa5414c7e9d25b89f0fab68", size = 27912, upload-time = "2025-11-14T09:50:45.266Z" }, - { url = "https://files.pythonhosted.org/packages/ba/26/fff3caba25aa3c0622114e03c69fb66c839b22335b04d7cce91a3a126d44/murmurhash-1.0.15-cp314-cp314-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d7e47c5746785db6a43b65fac47b9e63dd71dfbd89a8c92693425b9715e68c6e", size = 131847, upload-time = "2025-11-14T09:50:46.819Z" }, - { url = "https://files.pythonhosted.org/packages/df/e4/0f2b9fc533467a27afb4e906c33f32d5f637477de87dd94690e0c44335a6/murmurhash-1.0.15-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e8e674f02a99828c8a671ba99cd03299381b2f0744e6f25c29cadfc6151dc724", size = 132267, upload-time = "2025-11-14T09:50:48.298Z" }, - { url = "https://files.pythonhosted.org/packages/da/bf/9d1c107989728ec46e25773d503aa54070b32822a18cfa7f9d5f41bc17a5/murmurhash-1.0.15-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:26fd7c7855ac4850ad8737991d7b0e3e501df93ebaf0cf45aa5954303085fdba", size = 131894, upload-time = "2025-11-14T09:50:49.485Z" }, - { url = "https://files.pythonhosted.org/packages/0d/81/dcf27c71445c0e993b10e33169a098ca60ee702c5c58fcbde205fa6332a6/murmurhash-1.0.15-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:cb8ebafae60d5f892acff533cc599a359954d8c016a829514cb3f6e9ee10f322", size = 132054, upload-time = "2025-11-14T09:50:50.747Z" }, - { url = "https://files.pythonhosted.org/packages/bc/32/e874a14b2d2246bd2d16f80f49fad393a3865d4ee7d66d2cae939a67a29a/murmurhash-1.0.15-cp314-cp314-win_amd64.whl", hash = "sha256:898a629bf111f1aeba4437e533b5b836c0a9d2dd12d6880a9c75f6ca13e30e22", size = 26579, upload-time = "2025-11-14T09:50:52.278Z" }, - { url = "https://files.pythonhosted.org/packages/af/8e/4fca051ed8ae4d23a15aaf0a82b18cb368e8cf84f1e3b474d5749ec46069/murmurhash-1.0.15-cp314-cp314-win_arm64.whl", hash = "sha256:88dc1dd53b7b37c0df1b8b6bce190c12763014492f0269ff7620dc6027f470f4", size = 24341, upload-time = "2025-11-14T09:50:53.295Z" }, - { url = "https://files.pythonhosted.org/packages/38/9c/c72c2a4edd86aac829337ab9f83cf04cdb15e5d503e4c9a3a243f30a261c/murmurhash-1.0.15-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:6cb4e962ec4f928b30c271b2d84e6707eff6d942552765b663743cfa618b294b", size = 30146, upload-time = "2025-11-14T09:50:54.705Z" }, - { url = "https://files.pythonhosted.org/packages/ac/d7/72b47ebc86436cd0aa1fd4c6e8779521ec389397ac11389990278d0f7a47/murmurhash-1.0.15-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5678a3ea4fbf0cbaaca2bed9b445f556f294d5f799c67185d05ffcb221a77faf", size = 30141, upload-time = "2025-11-14T09:50:55.829Z" }, - { url = "https://files.pythonhosted.org/packages/64/bb/6d2f09135079c34dc2d26e961c52742d558b320c61503f273eab6ba743d9/murmurhash-1.0.15-cp314-cp314t-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ef19f38c6b858eef83caf710773db98c8f7eb2193b4c324650c74f3d8ba299e0", size = 163898, upload-time = "2025-11-14T09:50:56.946Z" }, - { url = "https://files.pythonhosted.org/packages/b9/e2/9c1b462e33f9cb2d632056f07c90b502fc20bd7da50a15d0557343bd2fed/murmurhash-1.0.15-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22aa3ceaedd2e57078b491ed08852d512b84ff4ff9bb2ff3f9bf0eec7f214c9e", size = 168040, upload-time = "2025-11-14T09:50:58.234Z" }, - { url = "https://files.pythonhosted.org/packages/e8/73/8694db1408fcdfa73589f7df6c445437ea146986fa1e393ec60d26d6e30c/murmurhash-1.0.15-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bba0e0262c0d08682b028cb963ac477bd9839029486fa1333fc5c01fb6072749", size = 164239, upload-time = "2025-11-14T09:50:59.95Z" }, - { url = "https://files.pythonhosted.org/packages/2d/f9/8e360bdfc3c44e267e7e046f0e0b9922766da92da26959a6963f597e6bb5/murmurhash-1.0.15-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:4fd8189ee293a09f30f4931408f40c28ccd42d9de4f66595f8814879339378bc", size = 161811, upload-time = "2025-11-14T09:51:01.289Z" }, - { url = "https://files.pythonhosted.org/packages/f9/31/97649680595b1096803d877ababb9a67c07f4378f177ec885eea28b9db6d/murmurhash-1.0.15-cp314-cp314t-win_amd64.whl", hash = "sha256:66395b1388f7daa5103db92debe06842ae3be4c0749ef6db68b444518666cdcc", size = 29817, upload-time = "2025-11-14T09:51:02.493Z" }, - { url = "https://files.pythonhosted.org/packages/76/66/4fce8755f25d77324401886c00017c556be7ca3039575b94037aff905385/murmurhash-1.0.15-cp314-cp314t-win_arm64.whl", hash = "sha256:c22e56c6a0b70598a66e456de5272f76088bc623688da84ef403148a6d41851d", size = 26219, upload-time = "2025-11-14T09:51:03.563Z" }, -] - -[[package]] -name = "networkx" -version = "3.6.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6a/51/63fe664f3908c97be9d2e4f1158eb633317598cfa6e1fc14af5383f17512/networkx-3.6.1.tar.gz", hash = "sha256:26b7c357accc0c8cde558ad486283728b65b6a95d85ee1cd66bafab4c8168509", size = 2517025, upload-time = "2025-12-08T17:02:39.908Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/c9/b2622292ea83fbb4ec318f5b9ab867d0a28ab43c5717bb85b0a5f6b3b0a4/networkx-3.6.1-py3-none-any.whl", hash = "sha256:d47fbf302e7d9cbbb9e2555a0d267983d2aa476bac30e90dfbe5669bd57f3762", size = 2068504, upload-time = "2025-12-08T17:02:38.159Z" }, -] - [[package]] name = "niquests" version = "3.18.5" @@ -1531,18 +979,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/bf/ca/e23538eff77d5921e53b6561002c86d60bcf04f62546ef1de7759c437bea/niquests-3.18.5-py3-none-any.whl", hash = "sha256:fe486ce71204f9f5ade1266ab1568cc09e4af772409070e26e65b0f8fda5c642", size = 208624, upload-time = "2026-04-10T13:20:38.032Z" }, ] -[[package]] -name = "num2words" -version = "0.5.14" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "docopt" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f6/58/ad645bd38b4b648eb2fc2ba1b909398e54eb0cbb6a7dbd2b4953e38c9621/num2words-0.5.14.tar.gz", hash = "sha256:b066ec18e56b6616a3b38086b5747daafbaa8868b226a36127e0451c0cf379c6", size = 218213, upload-time = "2024-12-17T20:17:10.191Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d6/5b/545e9267a1cc080c8a1be2746113a063e34bcdd0f5173fd665a5c13cb234/num2words-0.5.14-py3-none-any.whl", hash = "sha256:1c8e5b00142fc2966fd8d685001e36c4a9911e070d1b120e1beb721fa1edb33d", size = 163525, upload-time = "2024-12-17T20:17:06.074Z" }, -] - [[package]] name = "numpy" version = "2.4.4" @@ -1572,155 +1008,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/58/78/548fb8e07b1a341746bfbecb32f2c268470f45fa028aacdbd10d9bc73aab/numpy-2.4.4-cp314-cp314t-win_arm64.whl", hash = "sha256:ba203255017337d39f89bdd58417f03c4426f12beed0440cfd933cb15f8669c7", size = 10566643, upload-time = "2026-03-29T13:21:34.339Z" }, ] -[[package]] -name = "nvidia-cublas" -version = "13.1.0.3" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e1/a5/fce49e2ae977e0ccc084e5adafceb4f0ac0c8333cb6863501618a7277f67/nvidia_cublas-13.1.0.3-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:c86fc7f7ae36d7528288c5d88098edcb7b02c633d262e7ddbb86b0ad91be5df2", size = 542851226, upload-time = "2025-10-09T08:59:04.818Z" }, - { url = "https://files.pythonhosted.org/packages/e7/44/423ac00af4dd95a5aeb27207e2c0d9b7118702149bf4704c3ddb55bb7429/nvidia_cublas-13.1.0.3-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:ee8722c1f0145ab246bccb9e452153b5e0515fd094c3678df50b2a0888b8b171", size = 423133236, upload-time = "2025-10-09T08:59:32.536Z" }, -] - -[[package]] -name = "nvidia-cuda-cupti" -version = "13.0.85" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/2a/80353b103fc20ce05ef51e928daed4b6015db4aaa9162ed0997090fe2250/nvidia_cuda_cupti-13.0.85-py3-none-manylinux_2_25_aarch64.whl", hash = "sha256:796bd679890ee55fb14a94629b698b6db54bcfd833d391d5e94017dd9d7d3151", size = 10310827, upload-time = "2025-09-04T08:26:42.012Z" }, - { url = "https://files.pythonhosted.org/packages/33/6d/737d164b4837a9bbd202f5ae3078975f0525a55730fe871d8ed4e3b952b0/nvidia_cuda_cupti-13.0.85-py3-none-manylinux_2_25_x86_64.whl", hash = "sha256:4eb01c08e859bf924d222250d2e8f8b8ff6d3db4721288cf35d14252a4d933c8", size = 10715597, upload-time = "2025-09-04T08:26:51.312Z" }, -] - -[[package]] -name = "nvidia-cuda-nvrtc" -version = "13.0.88" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c3/68/483a78f5e8f31b08fb1bb671559968c0ca3a065ac7acabfc7cee55214fd6/nvidia_cuda_nvrtc-13.0.88-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:ad9b6d2ead2435f11cbb6868809d2adeeee302e9bb94bcf0539c7a40d80e8575", size = 90215200, upload-time = "2025-09-04T08:28:44.204Z" }, - { url = "https://files.pythonhosted.org/packages/b7/dc/6bb80850e0b7edd6588d560758f17e0550893a1feaf436807d64d2da040f/nvidia_cuda_nvrtc-13.0.88-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d27f20a0ca67a4bb34268a5e951033496c5b74870b868bacd046b1b8e0c3267b", size = 43015449, upload-time = "2025-09-04T08:28:20.239Z" }, -] - -[[package]] -name = "nvidia-cuda-runtime" -version = "13.0.96" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/87/4f/17d7b9b8e285199c58ce28e31b5c5bbaa4d8271af06a89b6405258245de2/nvidia_cuda_runtime-13.0.96-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ef9bcbe90493a2b9d810e43d249adb3d02e98dd30200d86607d8d02687c43f55", size = 2261060, upload-time = "2025-10-09T08:55:15.78Z" }, - { url = "https://files.pythonhosted.org/packages/2e/24/d1558f3b68b1d26e706813b1d10aa1d785e4698c425af8db8edc3dced472/nvidia_cuda_runtime-13.0.96-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7f82250d7782aa23b6cfe765ecc7db554bd3c2870c43f3d1821f1d18aebf0548", size = 2243632, upload-time = "2025-10-09T08:55:36.117Z" }, -] - -[[package]] -name = "nvidia-cudnn-cu13" -version = "9.19.0.56" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-cublas" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/f1/84/26025437c1e6b61a707442184fa0c03d083b661adf3a3eecfd6d21677740/nvidia_cudnn_cu13-9.19.0.56-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:6ed29ffaee1176c612daf442e4dd6cfeb6a0caa43ddcbeb59da94953030b1be4", size = 433781201, upload-time = "2026-02-03T20:40:53.805Z" }, - { url = "https://files.pythonhosted.org/packages/a3/22/0b4b932655d17a6da1b92fa92ab12844b053bb2ac2475e179ba6f043da1e/nvidia_cudnn_cu13-9.19.0.56-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:d20e1734305e9d68889a96e3f35094d733ff1f83932ebe462753973e53a572bf", size = 366066321, upload-time = "2026-02-03T20:44:52.837Z" }, -] - -[[package]] -name = "nvidia-cufft" -version = "12.0.0.61" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-nvjitlink" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/8b/ae/f417a75c0259e85c1d2f83ca4e960289a5f814ed0cea74d18c353d3e989d/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2708c852ef8cd89d1d2068bdbece0aa188813a0c934db3779b9b1faa8442e5f5", size = 214053554, upload-time = "2025-09-04T08:31:38.196Z" }, - { url = "https://files.pythonhosted.org/packages/a8/2f/7b57e29836ea8714f81e9898409196f47d772d5ddedddf1592eadb8ab743/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6c44f692dce8fd5ffd3e3df134b6cdb9c2f72d99cf40b62c32dde45eea9ddad3", size = 214085489, upload-time = "2025-09-04T08:31:56.044Z" }, -] - -[[package]] -name = "nvidia-cufile" -version = "1.15.1.6" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/70/4f193de89a48b71714e74602ee14d04e4019ad36a5a9f20c425776e72cd6/nvidia_cufile-1.15.1.6-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:08a3ecefae5a01c7f5117351c64f17c7c62efa5fffdbe24fc7d298da19cd0b44", size = 1223672, upload-time = "2025-09-04T08:32:22.779Z" }, - { url = "https://files.pythonhosted.org/packages/ab/73/cc4a14c9813a8a0d509417cf5f4bdaba76e924d58beb9864f5a7baceefbf/nvidia_cufile-1.15.1.6-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:bdc0deedc61f548bddf7733bdc216456c2fdb101d020e1ab4b88d232d5e2f6d1", size = 1136992, upload-time = "2025-09-04T08:32:14.119Z" }, -] - -[[package]] -name = "nvidia-curand" -version = "10.4.0.35" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/72/7c2ae24fb6b63a32e6ae5d241cc65263ea18d08802aaae087d9f013335a2/nvidia_curand-10.4.0.35-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:133df5a7509c3e292aaa2b477afd0194f06ce4ea24d714d616ff36439cee349a", size = 61962106, upload-time = "2025-08-04T10:21:41.128Z" }, - { url = "https://files.pythonhosted.org/packages/a5/9f/be0a41ca4a4917abf5cb9ae0daff1a6060cc5de950aec0396de9f3b52bc5/nvidia_curand-10.4.0.35-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:1aee33a5da6e1db083fe2b90082def8915f30f3248d5896bcec36a579d941bfc", size = 59544258, upload-time = "2025-08-04T10:22:03.992Z" }, -] - -[[package]] -name = "nvidia-cusolver" -version = "12.0.4.66" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-cublas" }, - { name = "nvidia-cusparse" }, - { name = "nvidia-nvjitlink" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/c3/b30c9e935fc01e3da443ec0116ed1b2a009bb867f5324d3f2d7e533e776b/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:02c2457eaa9e39de20f880f4bd8820e6a1cfb9f9a34f820eb12a155aa5bc92d2", size = 223467760, upload-time = "2025-09-04T08:33:04.222Z" }, - { url = "https://files.pythonhosted.org/packages/5f/67/cba3777620cdacb99102da4042883709c41c709f4b6323c10781a9c3aa34/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:0a759da5dea5c0ea10fd307de75cdeb59e7ea4fcb8add0924859b944babf1112", size = 200941980, upload-time = "2025-09-04T08:33:22.767Z" }, -] - -[[package]] -name = "nvidia-cusparse" -version = "12.6.3.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-nvjitlink" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/f8/94/5c26f33738ae35276672f12615a64bd008ed5be6d1ebcb23579285d960a9/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:80bcc4662f23f1054ee334a15c72b8940402975e0eab63178fc7e670aa59472c", size = 162155568, upload-time = "2025-09-04T08:33:42.864Z" }, - { url = "https://files.pythonhosted.org/packages/fa/18/623c77619c31d62efd55302939756966f3ecc8d724a14dab2b75f1508850/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2b3c89c88d01ee0e477cb7f82ef60a11a4bcd57b6b87c33f789350b59759360b", size = 145942937, upload-time = "2025-09-04T08:33:58.029Z" }, -] - -[[package]] -name = "nvidia-cusparselt-cu13" -version = "0.8.0" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/46/10/8dcd1175260706a2fc92a16a52e306b71d4c1ea0b0cc4a9484183399818a/nvidia_cusparselt_cu13-0.8.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:400c6ed1cf6780fc6efedd64ec9f1345871767e6a1a0a552a1ea0578117ea77c", size = 220791277, upload-time = "2025-08-13T19:22:40.982Z" }, - { url = "https://files.pythonhosted.org/packages/fd/53/43b0d71f4e702fa9733f8b4571fdca50a8813f1e450b656c239beff12315/nvidia_cusparselt_cu13-0.8.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:25e30a8a7323935d4ad0340b95a0b69926eee755767e8e0b1cf8dd85b197d3fd", size = 169884119, upload-time = "2025-08-13T19:23:41.967Z" }, -] - -[[package]] -name = "nvidia-nccl-cu13" -version = "2.28.9" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/39/55/1920646a2e43ffd4fc958536b276197ed740e9e0c54105b4bb3521591fc7/nvidia_nccl_cu13-2.28.9-py3-none-manylinux_2_18_aarch64.whl", hash = "sha256:01c873ba1626b54caa12272ed228dc5b2781545e0ae8ba3f432a8ef1c6d78643", size = 196561677, upload-time = "2025-11-18T05:49:03.45Z" }, - { url = "https://files.pythonhosted.org/packages/b0/b4/878fefaad5b2bcc6fcf8d474a25e3e3774bc5133e4b58adff4d0bca238bc/nvidia_nccl_cu13-2.28.9-py3-none-manylinux_2_18_x86_64.whl", hash = "sha256:e4553a30f34195f3fa1da02a6da3d6337d28f2003943aa0a3d247bbc25fefc42", size = 196493177, upload-time = "2025-11-18T05:49:17.677Z" }, -] - -[[package]] -name = "nvidia-nvjitlink" -version = "13.0.88" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/56/7a/123e033aaff487c77107195fa5a2b8686795ca537935a24efae476c41f05/nvidia_nvjitlink-13.0.88-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:13a74f429e23b921c1109976abefacc69835f2f433ebd323d3946e11d804e47b", size = 40713933, upload-time = "2025-09-04T08:35:43.553Z" }, - { url = "https://files.pythonhosted.org/packages/ab/2c/93c5250e64df4f894f1cbb397c6fd71f79813f9fd79d7cd61de3f97b3c2d/nvidia_nvjitlink-13.0.88-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e931536ccc7d467a98ba1d8b89ff7fa7f1fa3b13f2b0069118cd7f47bff07d0c", size = 38768748, upload-time = "2025-09-04T08:35:20.008Z" }, -] - -[[package]] -name = "nvidia-nvshmem-cu13" -version = "3.4.5" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/0f/05cc9c720236dcd2db9c1ab97fff629e96821be2e63103569da0c9b72f19/nvidia_nvshmem_cu13-3.4.5-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6dc2a197f38e5d0376ad52cd1a2a3617d3cdc150fd5966f4aee9bcebb1d68fe9", size = 60215947, upload-time = "2025-09-06T00:32:20.022Z" }, - { url = "https://files.pythonhosted.org/packages/3c/35/a9bf80a609e74e3b000fef598933235c908fcefcef9026042b8e6dfde2a9/nvidia_nvshmem_cu13-3.4.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:290f0a2ee94c9f3687a02502f3b9299a9f9fe826e6d0287ee18482e78d495b80", size = 60412546, upload-time = "2025-09-06T00:32:41.564Z" }, -] - -[[package]] -name = "nvidia-nvtx" -version = "13.0.85" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/f3/d86c845465a2723ad7e1e5c36dcd75ddb82898b3f53be47ebd429fb2fa5d/nvidia_nvtx-13.0.85-py3-none-manylinux1_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4936d1d6780fbe68db454f5e72a42ff64d1fd6397df9f363ae786930fd5c1cd4", size = 148047, upload-time = "2025-09-04T08:29:01.761Z" }, - { url = "https://files.pythonhosted.org/packages/a8/64/3708a90d1ebe202ffdeb7185f878a3c84d15c2b2c31858da2ce0583e2def/nvidia_nvtx-13.0.85-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cb7780edb6b14107373c835bf8b72e7a178bac7367e23da7acb108f973f157a6", size = 148878, upload-time = "2025-09-04T08:28:53.627Z" }, -] - [[package]] name = "onnxruntime" version = "1.24.4" @@ -1752,19 +1039,36 @@ wheels = [ ] [[package]] -name = "phonemizer-fork" -version = "3.3.2" +name = "pillow" +version = "12.2.0" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "attrs" }, - { name = "dlinfo" }, - { name = "joblib" }, - { name = "segments" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/42/fa/9294d2f11890ca49d0bdac7a4da60cbe5686629bfd4987cae0ad75e051cc/phonemizer_fork-3.3.2.tar.gz", hash = "sha256:10e16e827d0443b087062e21b55e805c00989cf1343b2e81e734cae5f6c0cf69", size = 300989, upload-time = "2025-01-30T13:02:31.201Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/21/c2bcdd5906101a30244eaffc1b6e6ce71a31bd0742a01eb89e660ebfac2d/pillow-12.2.0.tar.gz", hash = "sha256:a830b1a40919539d07806aa58e1b114df53ddd43213d9c8b75847eee6c0182b5", size = 46987819, upload-time = "2026-04-01T14:46:17.687Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/64/f1/0dcce21b0ae16a82df4b6583f8f3ad8e55b35f7e98b6bf536a4dd225fa08/phonemizer_fork-3.3.2-py3-none-any.whl", hash = "sha256:97305c76f4183b3825dae8f4c032265fe78c9946ce58c47d4b62161349264b74", size = 82700, upload-time = "2025-01-30T13:02:28.667Z" }, + { url = "https://files.pythonhosted.org/packages/bf/98/4595daa2365416a86cb0d495248a393dfc84e96d62ad080c8546256cb9c0/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:3adc9215e8be0448ed6e814966ecf3d9952f0ea40eb14e89a102b87f450660d8", size = 4100848, upload-time = "2026-04-01T14:44:48.48Z" }, + { url = "https://files.pythonhosted.org/packages/0b/79/40184d464cf89f6663e18dfcf7ca21aae2491fff1a16127681bf1fa9b8cf/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:6a9adfc6d24b10f89588096364cc726174118c62130c817c2837c60cf08a392b", size = 4176515, upload-time = "2026-04-01T14:44:51.353Z" }, + { url = "https://files.pythonhosted.org/packages/b0/63/703f86fd4c422a9cf722833670f4f71418fb116b2853ff7da722ea43f184/pillow-12.2.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:6a6e67ea2e6feda684ed370f9a1c52e7a243631c025ba42149a2cc5934dec295", size = 3640159, upload-time = "2026-04-01T14:44:53.588Z" }, + { url = "https://files.pythonhosted.org/packages/71/e0/fb22f797187d0be2270f83500aab851536101b254bfa1eae10795709d283/pillow-12.2.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2bb4a8d594eacdfc59d9e5ad972aa8afdd48d584ffd5f13a937a664c3e7db0ed", size = 5312185, upload-time = "2026-04-01T14:44:56.039Z" }, + { url = "https://files.pythonhosted.org/packages/ba/8c/1a9e46228571de18f8e28f16fabdfc20212a5d019f3e3303452b3f0a580d/pillow-12.2.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:80b2da48193b2f33ed0c32c38140f9d3186583ce7d516526d462645fd98660ae", size = 4695386, upload-time = "2026-04-01T14:44:58.663Z" }, + { url = "https://files.pythonhosted.org/packages/70/62/98f6b7f0c88b9addd0e87c217ded307b36be024d4ff8869a812b241d1345/pillow-12.2.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22db17c68434de69d8ecfc2fe821569195c0c373b25cccb9cbdacf2c6e53c601", size = 6280384, upload-time = "2026-04-01T14:45:01.5Z" }, + { url = "https://files.pythonhosted.org/packages/5e/03/688747d2e91cfbe0e64f316cd2e8005698f76ada3130d0194664174fa5de/pillow-12.2.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7b14cc0106cd9aecda615dd6903840a058b4700fcb817687d0ee4fc8b6e389be", size = 8091599, upload-time = "2026-04-01T14:45:04.5Z" }, + { url = "https://files.pythonhosted.org/packages/f6/35/577e22b936fcdd66537329b33af0b4ccfefaeabd8aec04b266528cddb33c/pillow-12.2.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cbeb542b2ebc6fcdacabf8aca8c1a97c9b3ad3927d46b8723f9d4f033288a0f", size = 6396021, upload-time = "2026-04-01T14:45:07.117Z" }, + { url = "https://files.pythonhosted.org/packages/11/8d/d2532ad2a603ca2b93ad9f5135732124e57811d0168155852f37fbce2458/pillow-12.2.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4bfd07bc812fbd20395212969e41931001fd59eb55a60658b0e5710872e95286", size = 7083360, upload-time = "2026-04-01T14:45:09.763Z" }, + { url = "https://files.pythonhosted.org/packages/5e/26/d325f9f56c7e039034897e7380e9cc202b1e368bfd04d4cbe6a441f02885/pillow-12.2.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9aba9a17b623ef750a4d11b742cbafffeb48a869821252b30ee21b5e91392c50", size = 6507628, upload-time = "2026-04-01T14:45:12.378Z" }, + { url = "https://files.pythonhosted.org/packages/5f/f7/769d5632ffb0988f1c5e7660b3e731e30f7f8ec4318e94d0a5d674eb65a4/pillow-12.2.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:deede7c263feb25dba4e82ea23058a235dcc2fe1f6021025dc71f2b618e26104", size = 7209321, upload-time = "2026-04-01T14:45:15.122Z" }, + { url = "https://files.pythonhosted.org/packages/6a/7a/c253e3c645cd47f1aceea6a8bacdba9991bf45bb7dfe927f7c893e89c93c/pillow-12.2.0-cp314-cp314-win32.whl", hash = "sha256:632ff19b2778e43162304d50da0181ce24ac5bb8180122cbe1bf4673428328c7", size = 6479723, upload-time = "2026-04-01T14:45:17.797Z" }, + { url = "https://files.pythonhosted.org/packages/cd/8b/601e6566b957ca50e28725cb6c355c59c2c8609751efbecd980db44e0349/pillow-12.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:4e6c62e9d237e9b65fac06857d511e90d8461a32adcc1b9065ea0c0fa3a28150", size = 7217400, upload-time = "2026-04-01T14:45:20.529Z" }, + { url = "https://files.pythonhosted.org/packages/d6/94/220e46c73065c3e2951bb91c11a1fb636c8c9ad427ac3ce7d7f3359b9b2f/pillow-12.2.0-cp314-cp314-win_arm64.whl", hash = "sha256:b1c1fbd8a5a1af3412a0810d060a78b5136ec0836c8a4ef9aa11807f2a22f4e1", size = 2554835, upload-time = "2026-04-01T14:45:23.162Z" }, + { url = "https://files.pythonhosted.org/packages/b6/ab/1b426a3974cb0e7da5c29ccff4807871d48110933a57207b5a676cccc155/pillow-12.2.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:57850958fe9c751670e49b2cecf6294acc99e562531f4bd317fa5ddee2068463", size = 5314225, upload-time = "2026-04-01T14:45:25.637Z" }, + { url = "https://files.pythonhosted.org/packages/19/1e/dce46f371be2438eecfee2a1960ee2a243bbe5e961890146d2dee1ff0f12/pillow-12.2.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d5d38f1411c0ed9f97bcb49b7bd59b6b7c314e0e27420e34d99d844b9ce3b6f3", size = 4698541, upload-time = "2026-04-01T14:45:28.355Z" }, + { url = "https://files.pythonhosted.org/packages/55/c3/7fbecf70adb3a0c33b77a300dc52e424dc22ad8cdc06557a2e49523b703d/pillow-12.2.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5c0a9f29ca8e79f09de89293f82fc9b0270bb4af1d58bc98f540cc4aedf03166", size = 6322251, upload-time = "2026-04-01T14:45:30.924Z" }, + { url = "https://files.pythonhosted.org/packages/1c/3c/7fbc17cfb7e4fe0ef1642e0abc17fc6c94c9f7a16be41498e12e2ba60408/pillow-12.2.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1610dd6c61621ae1cf811bef44d77e149ce3f7b95afe66a4512f8c59f25d9ebe", size = 8127807, upload-time = "2026-04-01T14:45:33.908Z" }, + { url = "https://files.pythonhosted.org/packages/ff/c3/a8ae14d6defd2e448493ff512fae903b1e9bd40b72efb6ec55ce0048c8ce/pillow-12.2.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a34329707af4f73cf1782a36cd2289c0368880654a2c11f027bcee9052d35dd", size = 6433935, upload-time = "2026-04-01T14:45:36.623Z" }, + { url = "https://files.pythonhosted.org/packages/6e/32/2880fb3a074847ac159d8f902cb43278a61e85f681661e7419e6596803ed/pillow-12.2.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e9c4f5b3c546fa3458a29ab22646c1c6c787ea8f5ef51300e5a60300736905e", size = 7116720, upload-time = "2026-04-01T14:45:39.258Z" }, + { url = "https://files.pythonhosted.org/packages/46/87/495cc9c30e0129501643f24d320076f4cc54f718341df18cc70ec94c44e1/pillow-12.2.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fb043ee2f06b41473269765c2feae53fc2e2fbf96e5e22ca94fb5ad677856f06", size = 6540498, upload-time = "2026-04-01T14:45:41.879Z" }, + { url = "https://files.pythonhosted.org/packages/18/53/773f5edca692009d883a72211b60fdaf8871cbef075eaa9d577f0a2f989e/pillow-12.2.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f278f034eb75b4e8a13a54a876cc4a5ab39173d2cdd93a638e1b467fc545ac43", size = 7239413, upload-time = "2026-04-01T14:45:44.705Z" }, + { url = "https://files.pythonhosted.org/packages/c9/e4/4b64a97d71b2a83158134abbb2f5bd3f8a2ea691361282f010998f339ec7/pillow-12.2.0-cp314-cp314t-win32.whl", hash = "sha256:6bb77b2dcb06b20f9f4b4a8454caa581cd4dd0643a08bacf821216a16d9c8354", size = 6482084, upload-time = "2026-04-01T14:45:47.568Z" }, + { url = "https://files.pythonhosted.org/packages/ba/13/306d275efd3a3453f72114b7431c877d10b1154014c1ebbedd067770d629/pillow-12.2.0-cp314-cp314t-win_amd64.whl", hash = "sha256:6562ace0d3fb5f20ed7290f1f929cae41b25ae29528f2af1722966a0a02e2aa1", size = 7225152, upload-time = "2026-04-01T14:45:50.032Z" }, + { url = "https://files.pythonhosted.org/packages/ff/6e/cf826fae916b8658848d7b9f38d88da6396895c676e8086fc0988073aaf8/pillow-12.2.0-cp314-cp314t-win_arm64.whl", hash = "sha256:aa88ccfe4e32d362816319ed727a004423aab09c5cea43c01a4b435643fa34eb", size = 2556579, upload-time = "2026-04-01T14:45:52.529Z" }, ] [[package]] @@ -1776,34 +1080,6 @@ 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" }, ] -[[package]] -name = "preshed" -version = "3.0.13" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cymem" }, - { name = "murmurhash" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/43/75/fe6b7bbd0dea530a001b0e24c331b21a0be2786e402abf3c57f5dce43d4b/preshed-3.0.13.tar.gz", hash = "sha256:d75f718bbfd97e992f7827e0fa7faf6a91bdd9c922d5baa4b50d62731396cb89", size = 18338, upload-time = "2026-03-23T08:57:31.378Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bb/b5/993886c98f5caaa6f07a648cac97a7c62a3093091cad65e1e43a1bd41cc4/preshed-3.0.13-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d2f1efae396cadab5f3890a2fd43d2ee65373ef9096ccbb805e51e8d8bcc563b", size = 137882, upload-time = "2026-03-23T08:56:56.878Z" }, - { url = "https://files.pythonhosted.org/packages/c6/86/b7fd137cbf140afd6c45e895946068a15f5b55642916de0075e6eb18581c/preshed-3.0.13-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8d6acc1f5031a535a55a6f7148e2f274554a8343a16309c700cebea0fe7aee8c", size = 138233, upload-time = "2026-03-23T08:56:58.318Z" }, - { url = "https://files.pythonhosted.org/packages/8b/ca/21a7e79625614134273dfed32bca5bb4c2ec1313e33fbd12d41657536f1f/preshed-3.0.13-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7da9d931e7660dcdd757e5870269f0c159126d682ed73ed313971d199eb0f334", size = 834835, upload-time = "2026-03-23T08:56:59.48Z" }, - { url = "https://files.pythonhosted.org/packages/8f/3a/2dbd299516461831ae90e0d5b0637137bf28520c4e6dd0b01d6f1886659a/preshed-3.0.13-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d4ae5cfe075bb7a07982e382bca44f41ddf041f4d24cbd358e8cccfc049259b8", size = 834928, upload-time = "2026-03-23T08:57:01.075Z" }, - { url = "https://files.pythonhosted.org/packages/7c/d3/af654eba4f6587c4ee02c5043e62c194b0a1c4431ffef0c67b9518f6b61c/preshed-3.0.13-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7557963d0125a3a7bcdb2eb6948f3e45da31b5a7f066b55320de3dea22d7557f", size = 1820368, upload-time = "2026-03-23T08:57:02.351Z" }, - { url = "https://files.pythonhosted.org/packages/bf/9b/ebcb2b9e8cb881e40b55b0bf450f8a6b187e2ef3ae0c685cce81d2d85026/preshed-3.0.13-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c4bc60dc994864095d784b7e4d77dba3e64188d169ac88722b699d175561fddb", size = 1888251, upload-time = "2026-03-23T08:57:04.158Z" }, - { url = "https://files.pythonhosted.org/packages/97/f7/c6c012779edcaa6e2cd092c554e98dc53e77f41205b07208655ba77e2327/preshed-3.0.13-cp314-cp314-win_amd64.whl", hash = "sha256:208dcebbe294bf1881ce33fb015d56ab2a7587aece85a09147727174207892e4", size = 125211, upload-time = "2026-03-23T08:57:05.83Z" }, - { url = "https://files.pythonhosted.org/packages/f8/82/390ef87d732ef64e673ef6bf9e5d898453986e979efa50fb3a400e2c0766/preshed-3.0.13-cp314-cp314-win_arm64.whl", hash = "sha256:cf8e1a7a1823b2a7765121446c630140ac6e8650c07a6efbf375e168d1fef4f7", size = 111942, upload-time = "2026-03-23T08:57:06.996Z" }, - { url = "https://files.pythonhosted.org/packages/80/3a/a9dde3167bcecb27ae82ce4567b5ab1aa3989113ae6814c092ce223cc4ef/preshed-3.0.13-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:9ca43ecbc3783eda4d6ab3416ae2ecd9ef23dca5f53995843f69f7457bcd0677", size = 144997, upload-time = "2026-03-23T08:57:08.064Z" }, - { url = "https://files.pythonhosted.org/packages/74/d4/22d9355b50b6a13b407dcad0a81df83fb1d5602092d1f05834674dde8fda/preshed-3.0.13-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c8596e41a258ff213553a441e0bb3eb388fd8158e84a7bf3aae6d8ede2c166d3", size = 147294, upload-time = "2026-03-23T08:57:09.411Z" }, - { url = "https://files.pythonhosted.org/packages/70/42/a225ee83fdb306d2a503f21a627953b820f4e079c90c8a84338957cb8ff5/preshed-3.0.13-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4f8856ca3d88e9b250630d70abb4f260d8933151ddfb413024784b25b009868e", size = 952110, upload-time = "2026-03-23T08:57:10.592Z" }, - { url = "https://files.pythonhosted.org/packages/40/ba/09a9dfe3d22d7e745483fd5d7f2a82cd4d39c161f7d2daa0faa4bd6402be/preshed-3.0.13-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0e5b2865aecbd2e1e10e5d19bb8bfad765863c1307c6c3e51f2a08bd64122409", size = 932217, upload-time = "2026-03-23T08:57:12.124Z" }, - { url = "https://files.pythonhosted.org/packages/6c/5c/e10e2e05133e7fcbd7c40536af1148c82dd24357b8f5726e2c7bc51cfd53/preshed-3.0.13-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:09f96b477c987755b3c945df214ea1c1c80bfb350e9f34e78da89585535b77e8", size = 1896542, upload-time = "2026-03-23T08:57:13.525Z" }, - { url = "https://files.pythonhosted.org/packages/37/aa/51e5b4109a4cdfae28c3613eeeb10764a3794ebef8de93ffbb109465bea3/preshed-3.0.13-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:670db59a52e1823b5f088c764df474e65b686592d4093adbeef14581c95ee2cb", size = 1959473, upload-time = "2026-03-23T08:57:15.706Z" }, - { url = "https://files.pythonhosted.org/packages/0e/6a/1d966f367a14c703dde629d150d996c1b727d442f620300b21c9ec1a24d1/preshed-3.0.13-cp314-cp314t-win_amd64.whl", hash = "sha256:b03e21b0bf95eb56e23973f32cabb930e94f352228652f81c0955dbd6967d904", size = 146229, upload-time = "2026-03-23T08:57:17.457Z" }, - { url = "https://files.pythonhosted.org/packages/22/80/368139067603e590a000122355f9c8576c8ebed4fb0b8849feaa2698489d/preshed-3.0.13-cp314-cp314t-win_arm64.whl", hash = "sha256:b980f3ea9bb74b7f94464bc3d6eb3c9162b6b79b531febd14c6465c24344d2cc", size = 119339, upload-time = "2026-03-23T08:57:18.882Z" }, -] - [[package]] name = "priority" version = "2.0.0" @@ -1813,45 +1089,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5e/5f/82c8074f7e84978129347c2c6ec8b6c59f3584ff1a20bc3c940a3e061790/priority-2.0.0-py3-none-any.whl", hash = "sha256:6f8eefce5f3ad59baf2c080a664037bb4725cd0a790d53d59ab4059288faf6aa", size = 8946, upload-time = "2021-06-27T10:15:03.856Z" }, ] -[[package]] -name = "propcache" -version = "0.4.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9e/da/e9fc233cf63743258bff22b3dfa7ea5baef7b5bc324af47a0ad89b8ffc6f/propcache-0.4.1.tar.gz", hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d", size = 46442, upload-time = "2025-10-08T19:49:02.291Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8e/5c/bca52d654a896f831b8256683457ceddd490ec18d9ec50e97dfd8fc726a8/propcache-0.4.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3f7124c9d820ba5548d431afb4632301acf965db49e666aa21c305cbe8c6de12", size = 78152, upload-time = "2025-10-08T19:47:51.051Z" }, - { url = "https://files.pythonhosted.org/packages/65/9b/03b04e7d82a5f54fb16113d839f5ea1ede58a61e90edf515f6577c66fa8f/propcache-0.4.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c0d4b719b7da33599dfe3b22d3db1ef789210a0597bc650b7cee9c77c2be8c5c", size = 44869, upload-time = "2025-10-08T19:47:52.594Z" }, - { url = "https://files.pythonhosted.org/packages/b2/fa/89a8ef0468d5833a23fff277b143d0573897cf75bd56670a6d28126c7d68/propcache-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9f302f4783709a78240ebc311b793f123328716a60911d667e0c036bc5dcbded", size = 46596, upload-time = "2025-10-08T19:47:54.073Z" }, - { url = "https://files.pythonhosted.org/packages/86/bd/47816020d337f4a746edc42fe8d53669965138f39ee117414c7d7a340cfe/propcache-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c80ee5802e3fb9ea37938e7eecc307fb984837091d5fd262bb37238b1ae97641", size = 206981, upload-time = "2025-10-08T19:47:55.715Z" }, - { url = "https://files.pythonhosted.org/packages/df/f6/c5fa1357cc9748510ee55f37173eb31bfde6d94e98ccd9e6f033f2fc06e1/propcache-0.4.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ed5a841e8bb29a55fb8159ed526b26adc5bdd7e8bd7bf793ce647cb08656cdf4", size = 211490, upload-time = "2025-10-08T19:47:57.499Z" }, - { url = "https://files.pythonhosted.org/packages/80/1e/e5889652a7c4a3846683401a48f0f2e5083ce0ec1a8a5221d8058fbd1adf/propcache-0.4.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:55c72fd6ea2da4c318e74ffdf93c4fe4e926051133657459131a95c846d16d44", size = 215371, upload-time = "2025-10-08T19:47:59.317Z" }, - { url = "https://files.pythonhosted.org/packages/b2/f2/889ad4b2408f72fe1a4f6a19491177b30ea7bf1a0fd5f17050ca08cfc882/propcache-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8326e144341460402713f91df60ade3c999d601e7eb5ff8f6f7862d54de0610d", size = 201424, upload-time = "2025-10-08T19:48:00.67Z" }, - { url = "https://files.pythonhosted.org/packages/27/73/033d63069b57b0812c8bd19f311faebeceb6ba31b8f32b73432d12a0b826/propcache-0.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:060b16ae65bc098da7f6d25bf359f1f31f688384858204fe5d652979e0015e5b", size = 197566, upload-time = "2025-10-08T19:48:02.604Z" }, - { url = "https://files.pythonhosted.org/packages/dc/89/ce24f3dc182630b4e07aa6d15f0ff4b14ed4b9955fae95a0b54c58d66c05/propcache-0.4.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:89eb3fa9524f7bec9de6e83cf3faed9d79bffa560672c118a96a171a6f55831e", size = 193130, upload-time = "2025-10-08T19:48:04.499Z" }, - { url = "https://files.pythonhosted.org/packages/a9/24/ef0d5fd1a811fb5c609278d0209c9f10c35f20581fcc16f818da959fc5b4/propcache-0.4.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:dee69d7015dc235f526fe80a9c90d65eb0039103fe565776250881731f06349f", size = 202625, upload-time = "2025-10-08T19:48:06.213Z" }, - { url = "https://files.pythonhosted.org/packages/f5/02/98ec20ff5546f68d673df2f7a69e8c0d076b5abd05ca882dc7ee3a83653d/propcache-0.4.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5558992a00dfd54ccbc64a32726a3357ec93825a418a401f5cc67df0ac5d9e49", size = 204209, upload-time = "2025-10-08T19:48:08.432Z" }, - { url = "https://files.pythonhosted.org/packages/a0/87/492694f76759b15f0467a2a93ab68d32859672b646aa8a04ce4864e7932d/propcache-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c9b822a577f560fbd9554812526831712c1436d2c046cedee4c3796d3543b144", size = 197797, upload-time = "2025-10-08T19:48:09.968Z" }, - { url = "https://files.pythonhosted.org/packages/ee/36/66367de3575db1d2d3f3d177432bd14ee577a39d3f5d1b3d5df8afe3b6e2/propcache-0.4.1-cp314-cp314-win32.whl", hash = "sha256:ab4c29b49d560fe48b696cdcb127dd36e0bc2472548f3bf56cc5cb3da2b2984f", size = 38140, upload-time = "2025-10-08T19:48:11.232Z" }, - { url = "https://files.pythonhosted.org/packages/0c/2a/a758b47de253636e1b8aef181c0b4f4f204bf0dd964914fb2af90a95b49b/propcache-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:5a103c3eb905fcea0ab98be99c3a9a5ab2de60228aa5aceedc614c0281cf6153", size = 41257, upload-time = "2025-10-08T19:48:12.707Z" }, - { url = "https://files.pythonhosted.org/packages/34/5e/63bd5896c3fec12edcbd6f12508d4890d23c265df28c74b175e1ef9f4f3b/propcache-0.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:74c1fb26515153e482e00177a1ad654721bf9207da8a494a0c05e797ad27b992", size = 38097, upload-time = "2025-10-08T19:48:13.923Z" }, - { url = "https://files.pythonhosted.org/packages/99/85/9ff785d787ccf9bbb3f3106f79884a130951436f58392000231b4c737c80/propcache-0.4.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:824e908bce90fb2743bd6b59db36eb4f45cd350a39637c9f73b1c1ea66f5b75f", size = 81455, upload-time = "2025-10-08T19:48:15.16Z" }, - { url = "https://files.pythonhosted.org/packages/90/85/2431c10c8e7ddb1445c1f7c4b54d886e8ad20e3c6307e7218f05922cad67/propcache-0.4.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c2b5e7db5328427c57c8e8831abda175421b709672f6cfc3d630c3b7e2146393", size = 46372, upload-time = "2025-10-08T19:48:16.424Z" }, - { url = "https://files.pythonhosted.org/packages/01/20/b0972d902472da9bcb683fa595099911f4d2e86e5683bcc45de60dd05dc3/propcache-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6f6ff873ed40292cd4969ef5310179afd5db59fdf055897e282485043fc80ad0", size = 48411, upload-time = "2025-10-08T19:48:17.577Z" }, - { url = "https://files.pythonhosted.org/packages/e2/e3/7dc89f4f21e8f99bad3d5ddb3a3389afcf9da4ac69e3deb2dcdc96e74169/propcache-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49a2dc67c154db2c1463013594c458881a069fcf98940e61a0569016a583020a", size = 275712, upload-time = "2025-10-08T19:48:18.901Z" }, - { url = "https://files.pythonhosted.org/packages/20/67/89800c8352489b21a8047c773067644e3897f02ecbbd610f4d46b7f08612/propcache-0.4.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:005f08e6a0529984491e37d8dbc3dd86f84bd78a8ceb5fa9a021f4c48d4984be", size = 273557, upload-time = "2025-10-08T19:48:20.762Z" }, - { url = "https://files.pythonhosted.org/packages/e2/a1/b52b055c766a54ce6d9c16d9aca0cad8059acd9637cdf8aa0222f4a026ef/propcache-0.4.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5c3310452e0d31390da9035c348633b43d7e7feb2e37be252be6da45abd1abcc", size = 280015, upload-time = "2025-10-08T19:48:22.592Z" }, - { url = "https://files.pythonhosted.org/packages/48/c8/33cee30bd890672c63743049f3c9e4be087e6780906bfc3ec58528be59c1/propcache-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c3c70630930447f9ef1caac7728c8ad1c56bc5015338b20fed0d08ea2480b3a", size = 262880, upload-time = "2025-10-08T19:48:23.947Z" }, - { url = "https://files.pythonhosted.org/packages/0c/b1/8f08a143b204b418285c88b83d00edbd61afbc2c6415ffafc8905da7038b/propcache-0.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e57061305815dfc910a3634dcf584f08168a8836e6999983569f51a8544cd89", size = 260938, upload-time = "2025-10-08T19:48:25.656Z" }, - { url = "https://files.pythonhosted.org/packages/cf/12/96e4664c82ca2f31e1c8dff86afb867348979eb78d3cb8546a680287a1e9/propcache-0.4.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:521a463429ef54143092c11a77e04056dd00636f72e8c45b70aaa3140d639726", size = 247641, upload-time = "2025-10-08T19:48:27.207Z" }, - { url = "https://files.pythonhosted.org/packages/18/ed/e7a9cfca28133386ba52278136d42209d3125db08d0a6395f0cba0c0285c/propcache-0.4.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:120c964da3fdc75e3731aa392527136d4ad35868cc556fd09bb6d09172d9a367", size = 262510, upload-time = "2025-10-08T19:48:28.65Z" }, - { url = "https://files.pythonhosted.org/packages/f5/76/16d8bf65e8845dd62b4e2b57444ab81f07f40caa5652b8969b87ddcf2ef6/propcache-0.4.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:d8f353eb14ee3441ee844ade4277d560cdd68288838673273b978e3d6d2c8f36", size = 263161, upload-time = "2025-10-08T19:48:30.133Z" }, - { url = "https://files.pythonhosted.org/packages/e7/70/c99e9edb5d91d5ad8a49fa3c1e8285ba64f1476782fed10ab251ff413ba1/propcache-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ab2943be7c652f09638800905ee1bab2c544e537edb57d527997a24c13dc1455", size = 257393, upload-time = "2025-10-08T19:48:31.567Z" }, - { url = "https://files.pythonhosted.org/packages/08/02/87b25304249a35c0915d236575bc3574a323f60b47939a2262b77632a3ee/propcache-0.4.1-cp314-cp314t-win32.whl", hash = "sha256:05674a162469f31358c30bcaa8883cb7829fa3110bf9c0991fe27d7896c42d85", size = 42546, upload-time = "2025-10-08T19:48:32.872Z" }, - { url = "https://files.pythonhosted.org/packages/cb/ef/3c6ecf8b317aa982f309835e8f96987466123c6e596646d4e6a1dfcd080f/propcache-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:990f6b3e2a27d683cb7602ed6c86f15ee6b43b1194736f9baaeb93d0016633b1", size = 46259, upload-time = "2025-10-08T19:48:34.226Z" }, - { url = "https://files.pythonhosted.org/packages/c4/2d/346e946d4951f37eca1e4f55be0f0174c52cd70720f84029b02f296f4a38/propcache-0.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ecef2343af4cc68e05131e45024ba34f6095821988a9d0a02aa7c73fcc448aa9", size = 40428, upload-time = "2025-10-08T19:48:35.441Z" }, - { url = "https://files.pythonhosted.org/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", size = 13305, upload-time = "2025-10-08T19:49:00.792Z" }, -] - [[package]] name = "protobuf" version = "7.34.1" @@ -1868,15 +1105,21 @@ wheels = [ ] [[package]] -name = "py-vapid" -version = "1.9.4" +name = "py-rust-stemmers" +version = "0.1.8" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cryptography" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a3/ed/c648c8018fab319951764f4babe68ddcbbff7f2bbcd7ff7e531eac1788c8/py_vapid-1.9.4.tar.gz", hash = "sha256:a004023560cbc54e34fc06380a0580f04ffcc788e84fb6d19e9339eeb6551a28", size = 74750, upload-time = "2026-01-05T22:13:25.201Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6b/c1/9763f9fb1cd73f9c317a83feeed6e0d4af320c6bbddab47b4a94f3a47d0c/py_rust_stemmers-0.1.8.tar.gz", hash = "sha256:6b0f6f48bc54d607aed802de872fcd5a71bae969a6760976dc78ce55e8eaf3da", size = 9732, upload-time = "2026-05-22T11:00:24.358Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/15/f9d0171e1ad863ca49e826d5afb6b50566f20dc9b4f76965096d3555ce9e/py_vapid-1.9.4-py2.py3-none-any.whl", hash = "sha256:f165a5bf90dcf966b226114f01f178f137579a09784c7f0628fa2f0a299741b6", size = 23912, upload-time = "2026-01-05T20:42:05.455Z" }, + { url = "https://files.pythonhosted.org/packages/b0/7e/f4346adfd44acbd7eaedcbd7d21b7f40ec9712e6c699e71fddad8dae6f8d/py_rust_stemmers-0.1.8-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:526b58958c6ffa36c4a805326cfb624ecbd665d16ba435027dbed0bcbcaa09d2", size = 290379, upload-time = "2026-05-22T11:00:08.192Z" }, + { url = "https://files.pythonhosted.org/packages/c2/d8/988fc3f5dc0dbbd4bf5909f50ff953ab55ee8b5f79a835d00e57847d3123/py_rust_stemmers-0.1.8-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2b607f0b270951fb66479baf4b68716cc63a981585cbd898b0b6b5c359efde7e", size = 275458, upload-time = "2026-05-22T11:00:09.522Z" }, + { url = "https://files.pythonhosted.org/packages/f4/94/e04c8b6a8364bca1b368785cef143755dd2d1ffe74df8f8b47b075bb1043/py_rust_stemmers-0.1.8-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b0327b151ab8a338fb54fdac114ba34394327fc1e2c4c425ad1caf2013e5de3", size = 314711, upload-time = "2026-05-22T11:00:10.878Z" }, + { url = "https://files.pythonhosted.org/packages/4f/cb/f59f9a80caa099cb6625a46c9a8e6e7e80bb3ed284f17e80245c8240a66e/py_rust_stemmers-0.1.8-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dadd0e369703817fc7026987b3093f461f9f58d8dde74e689d546184bc8f3451", size = 319370, upload-time = "2026-05-22T11:00:11.961Z" }, + { url = "https://files.pythonhosted.org/packages/06/59/8211cd0f56e53f7770debd9a78de37985fb5662ae66e3b7b380f4c79888b/py_rust_stemmers-0.1.8-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:245e2c61c52e073341893a9682cd1396b61047154548aee30bb1af3d8ed4b4cc", size = 321373, upload-time = "2026-05-22T11:00:13.213Z" }, + { url = "https://files.pythonhosted.org/packages/10/72/fe33e614c114264d1ba54d39da4b5a4abeb6aedd0d26e5a8fd0637d6ddba/py_rust_stemmers-0.1.8-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:451ee1c02a3f5cf1e161b46ba9032cdda4ba10a8b03ff9ee61c1d34d42a0bc81", size = 321707, upload-time = "2026-05-22T11:00:14.177Z" }, + { url = "https://files.pythonhosted.org/packages/91/f9/3cd18902fe2fa54557d3fe9132552256372d381c7aca71346163055d78b1/py_rust_stemmers-0.1.8-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d396dd25c473c1bc4248c79cd223f4b36356b55a124652f015c6a001547f81ac", size = 492457, upload-time = "2026-05-22T11:00:15.245Z" }, + { url = "https://files.pythonhosted.org/packages/90/d7/32c6d3995e7036b73683389de2771f4dbbf40de192b7efe73c2528ee1eb5/py_rust_stemmers-0.1.8-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:479c77c32d8be692f3cfcde7e19273f02ac81d6f45c6aef49887ef95cab7abbb", size = 596085, upload-time = "2026-05-22T11:00:16.404Z" }, + { url = "https://files.pythonhosted.org/packages/00/8c/e68fa5d862ea6a27fced3535c25ea4eaa26ba1ce00dfef5841924c74b167/py_rust_stemmers-0.1.8-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c786235275c5c2abb7f206b8236aee3ca0bc53c7497daf7fb7b01d3491469547", size = 539747, upload-time = "2026-05-22T11:00:17.414Z" }, + { url = "https://files.pythonhosted.org/packages/44/48/aa584cf3772e01231641c95dc1aa73327a7d986c562639d78d0013733acf/py_rust_stemmers-0.1.8-cp314-cp314-win_amd64.whl", hash = "sha256:931d13570962b093417e5443a9d1bd63d73fa239ebb81e5b1d346663571403e4", size = 209636, upload-time = "2026-05-22T11:00:18.662Z" }, ] [[package]] @@ -1942,6 +1185,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" }, ] +[[package]] +name = "pydantic-settings" +version = "2.14.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, + { name = "python-dotenv" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/07/60/1d1e59c9c90d54591469ada7d268251f71c24bdb765f1a8a832cee8c6653/pydantic_settings-2.14.1.tar.gz", hash = "sha256:e874d3bec7e787b0c9958277956ed9b4dd5de6a80e162188fdaff7c5e26fd5fa", size = 235551, upload-time = "2026-05-08T13:40:06.542Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ae/8d/f1af3832f5e6eb13ba94ee809e72b8ecb5eef226d27ee0bef7d963d943c7/pydantic_settings-2.14.1-py3-none-any.whl", hash = "sha256:6e3c7edfd8277687cdc598f56e5cff0e9bfff0910a3749deaa8d4401c3a2b9de", size = 60964, upload-time = "2026-05-08T13:40:04.958Z" }, +] + [[package]] name = "pygments" version = "2.20.0" @@ -1952,12 +1209,17 @@ wheels = [ ] [[package]] -name = "pyparsing" -version = "3.3.2" +name = "pyjwt" +version = "2.13.0" 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" } +sdist = { url = "https://files.pythonhosted.org/packages/3b/81/58d0ac84e1ef3a3843791d6954d94c0b33d526c75eeb1efbce9d0a4c4077/pyjwt-2.13.0.tar.gz", hash = "sha256:41571c89ca91598c79e8ef18a2d07367d4810fbbd6f637794879baf1b7703423", size = 107515, upload-time = "2026-05-21T19:54:36.618Z" } 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" }, + { url = "https://files.pythonhosted.org/packages/a3/5e/ecf12fdb62546d64385c158514e9b2b671f7832108ef2ecd2020ce0af2d1/pyjwt-2.13.0-py3-none-any.whl", hash = "sha256:66adcc2aff09b3f1bbd95fc1e1577df8ac8723c978552fd43304c8a290ac5728", size = 31274, upload-time = "2026-05-21T19:54:35.362Z" }, +] + +[package.optional-dependencies] +crypto = [ + { name = "cryptography" }, ] [[package]] @@ -2001,28 +1263,31 @@ wheels = [ ] [[package]] -name = "pytz" -version = "2026.1.post1" +name = "python-dotenv" +version = "1.2.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/56/db/b8721d71d945e6a8ac63c0fc900b2067181dbb50805958d4d4661cf7d277/pytz-2026.1.post1.tar.gz", hash = "sha256:3378dde6a0c3d26719182142c56e60c7f9af7e968076f31aae569d72a0358ee1", size = 321088, upload-time = "2026-03-03T07:47:50.683Z" } +sdist = { url = "https://files.pythonhosted.org/packages/82/ed/0301aeeac3e5353ef3d94b6ec08bbcabd04a72018415dcb29e588514bba8/python_dotenv-1.2.2.tar.gz", hash = "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3", size = 50135, upload-time = "2026-03-01T16:00:26.196Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/10/99/781fe0c827be2742bcc775efefccb3b048a3a9c6ce9aec0cbf4a101677e5/pytz-2026.1.post1-py2.py3-none-any.whl", hash = "sha256:f2fd16142fda348286a75e1a524be810bb05d444e5a081f37f7affc635035f7a", size = 510489, upload-time = "2026-03-03T07:47:49.167Z" }, + { url = "https://files.pythonhosted.org/packages/0b/d7/1959b9648791274998a9c3526f6d0ec8fd2233e4d4acce81bbae76b44b2a/python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", size = 22101, upload-time = "2026-03-01T16:00:25.09Z" }, ] [[package]] -name = "pywebpush" -version = "2.3.0" +name = "python-multipart" +version = "0.0.29" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiohttp" }, - { name = "cryptography" }, - { name = "http-ece" }, - { name = "py-vapid" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/87/d9/e497a24bc9f659bfc0e570382a41e6b2d6726fbcfa4d85aaa23fe9c81ba2/pywebpush-2.3.0.tar.gz", hash = "sha256:d1e27db8de9e6757c1875f67292554bd54c41874c36f4b5c4ebb5442dce204f2", size = 28489, upload-time = "2026-02-09T23:30:18.574Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4e/fe/70bd71a6738b09a0bdf6480ca6436b167469ca4578b2a0efbe390b4b0e70/python_multipart-0.0.29.tar.gz", hash = "sha256:643e93849196645e2dbdd81a0f8829a23123ad7f797a84a364c6fb3563f18904", size = 45678, upload-time = "2026-05-17T17:29:47.654Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/d8/ac21241cf8007cb93255eabf318da4f425ec0f75d28c366992253aa8c1b2/pywebpush-2.3.0-py3-none-any.whl", hash = "sha256:3d97469fb14d4323c362319d438183737249a4115b50e146ce233e7f01e3cf98", size = 22851, upload-time = "2026-02-09T23:30:16.093Z" }, + { url = "https://files.pythonhosted.org/packages/8f/cb/769cfc37177252872a45a71f3fbdde9d51b471a3f3c14bfe95dde3407386/python_multipart-0.0.29-py3-none-any.whl", hash = "sha256:2ddcc971cef266225f54f552d8fa10bcfbb1f14446caec199060daac59ff2d69", size = 29640, upload-time = "2026-05-17T17:29:45.69Z" }, +] + +[[package]] +name = "pywin32" +version = "311" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c9/31/097f2e132c4f16d99a22bfb777e0fd88bd8e1c634304e102f313af69ace5/pywin32-311-cp314-cp314-win32.whl", hash = "sha256:b7a2c10b93f8986666d0c803ee19b5990885872a7de910fc460f9b0c2fbf92ee", size = 8840714, upload-time = "2025-07-14T20:13:32.449Z" }, + { url = "https://files.pythonhosted.org/packages/90/4b/07c77d8ba0e01349358082713400435347df8426208171ce297da32c313d/pywin32-311-cp314-cp314-win_amd64.whl", hash = "sha256:3aca44c046bd2ed8c90de9cb8427f581c479e594e99b5c0bb19b29c10fd6cb87", size = 9656800, upload-time = "2025-07-14T20:13:34.312Z" }, + { url = "https://files.pythonhosted.org/packages/c0/d2/21af5c535501a7233e734b8af901574572da66fcc254cb35d0609c9080dd/pywin32-311-cp314-cp314-win_arm64.whl", hash = "sha256:a508e2d9025764a8270f93111a970e1d0fbfc33f4153b388bb649b7eec4f9b42", size = 8932540, upload-time = "2025-07-14T20:13:36.379Z" }, ] [[package]] @@ -2113,18 +1378,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7e/e9/cc28f21f52913adf333f653b9e0a3bf9cb223f5083a26422968ba73edd8d/quart-0.20.0-py3-none-any.whl", hash = "sha256:003c08f551746710acb757de49d9b768986fd431517d0eb127380b656b98b8f1", size = 77960, upload-time = "2024-12-23T13:53:02.842Z" }, ] -[[package]] -name = "rdflib" -version = "7.6.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pyparsing" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/98/f5/18bb77b7af9526add0c727a3b2048959847dc5fb030913e2918bf384fec3/rdflib-7.6.0.tar.gz", hash = "sha256:6c831288d5e4a5a7ece85d0ccde9877d512a3d0f02d7c06455d00d6d0ea379df", size = 4943826, upload-time = "2026-02-13T07:15:55.938Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/10/c2/6604a71269e0c1bd75656d5a001432d16f2cc5b8c057140ec797155c295e/rdflib-7.6.0-py3-none-any.whl", hash = "sha256:30c0a3ebf4c0e09215f066be7246794b6492e054e782d7ac2a34c9f70a15e0dd", size = 615416, upload-time = "2026-02-13T07:15:46.487Z" }, -] - [[package]] name = "recurring-ical-events" version = "3.8.1" @@ -2153,46 +1406,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766, upload-time = "2025-10-13T15:30:47.625Z" }, ] -[[package]] -name = "regex" -version = "2026.4.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cb/0e/3a246dbf05666918bd3664d9d787f84a9108f6f43cc953a077e4a7dfdb7e/regex-2026.4.4.tar.gz", hash = "sha256:e08270659717f6973523ce3afbafa53515c4dc5dcad637dc215b6fd50f689423", size = 416000, upload-time = "2026-04-03T20:56:28.155Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f0/f5/ed97c2dc47b5fbd4b73c0d7d75f9ebc8eca139f2bbef476bba35f28c0a77/regex-2026.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:2da82d643fa698e5e5210e54af90181603d5853cf469f5eedf9bfc8f59b4b8c7", size = 490343, upload-time = "2026-04-03T20:55:15.241Z" }, - { url = "https://files.pythonhosted.org/packages/80/e9/de4828a7385ec166d673a5790ad06ac48cdaa98bc0960108dd4b9cc1aef7/regex-2026.4.4-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:54a1189ad9d9357760557c91103d5e421f0a2dabe68a5cdf9103d0dcf4e00752", size = 291909, upload-time = "2026-04-03T20:55:17.558Z" }, - { url = "https://files.pythonhosted.org/packages/b4/d6/5cfbfc97f3201a4d24b596a77957e092030dcc4205894bc035cedcfce62f/regex-2026.4.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:76d67d5afb1fe402d10a6403bae668d000441e2ab115191a804287d53b772951", size = 289692, upload-time = "2026-04-03T20:55:20.561Z" }, - { url = "https://files.pythonhosted.org/packages/8e/ac/f2212d9fd56fe897e36d0110ba30ba2d247bd6410c5bd98499c7e5a1e1f2/regex-2026.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e7cd3e4ee8d80447a83bbc9ab0c8459781fa77087f856c3e740d7763be0df27f", size = 796979, upload-time = "2026-04-03T20:55:22.56Z" }, - { url = "https://files.pythonhosted.org/packages/c9/e3/a016c12675fbac988a60c7e1c16e67823ff0bc016beb27bd7a001dbdabc6/regex-2026.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2e19e18c568d2866d8b6a6dfad823db86193503f90823a8f66689315ba28fbe8", size = 866744, upload-time = "2026-04-03T20:55:24.646Z" }, - { url = "https://files.pythonhosted.org/packages/af/a4/0b90ca4cf17adc3cb43de80ec71018c37c88ad64987e8d0d481a95ca60b5/regex-2026.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7698a6f38730fd1385d390d1ed07bb13dce39aa616aca6a6d89bea178464b9a4", size = 911613, upload-time = "2026-04-03T20:55:27.033Z" }, - { url = "https://files.pythonhosted.org/packages/8e/3b/2b3dac0b82d41ab43aa87c6ecde63d71189d03fe8854b8ca455a315edac3/regex-2026.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:173a66f3651cdb761018078e2d9487f4cf971232c990035ec0eb1cdc6bf929a9", size = 800551, upload-time = "2026-04-03T20:55:29.532Z" }, - { url = "https://files.pythonhosted.org/packages/25/fe/5365eb7aa0e753c4b5957815c321519ecab033c279c60e1b1ae2367fa810/regex-2026.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fa7922bbb2cc84fa062d37723f199d4c0cd200245ce269c05db82d904db66b83", size = 776911, upload-time = "2026-04-03T20:55:31.526Z" }, - { url = "https://files.pythonhosted.org/packages/aa/b3/7fb0072156bba065e3b778a7bc7b0a6328212be5dd6a86fd207e0c4f2dab/regex-2026.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:59f67cd0a0acaf0e564c20bbd7f767286f23e91e2572c5703bf3e56ea7557edb", size = 785751, upload-time = "2026-04-03T20:55:33.797Z" }, - { url = "https://files.pythonhosted.org/packages/02/1a/9f83677eb699273e56e858f7bd95acdbee376d42f59e8bfca2fd80d79df3/regex-2026.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:475e50f3f73f73614f7cba5524d6de49dee269df00272a1b85e3d19f6d498465", size = 860484, upload-time = "2026-04-03T20:55:35.745Z" }, - { url = "https://files.pythonhosted.org/packages/3b/7a/93937507b61cfcff8b4c5857f1b452852b09f741daa9acae15c971d8554e/regex-2026.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:a1c0c7d67b64d85ac2e1879923bad2f08a08f3004055f2f406ef73c850114bd4", size = 765939, upload-time = "2026-04-03T20:55:37.972Z" }, - { url = "https://files.pythonhosted.org/packages/86/ea/81a7f968a351c6552b1670ead861e2a385be730ee28402233020c67f9e0f/regex-2026.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:1371c2ccbb744d66ee63631cc9ca12aa233d5749972626b68fe1a649dd98e566", size = 851417, upload-time = "2026-04-03T20:55:39.92Z" }, - { url = "https://files.pythonhosted.org/packages/4c/7e/323c18ce4b5b8f44517a36342961a0306e931e499febbd876bb149d900f0/regex-2026.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:59968142787042db793348a3f5b918cf24ced1f23247328530e063f89c128a95", size = 789056, upload-time = "2026-04-03T20:55:42.303Z" }, - { url = "https://files.pythonhosted.org/packages/c0/af/e7510f9b11b1913b0cd44eddb784b2d650b2af6515bfce4cffcc5bfd1d38/regex-2026.4.4-cp314-cp314-win32.whl", hash = "sha256:59efe72d37fd5a91e373e5146f187f921f365f4abc1249a5ab446a60f30dd5f8", size = 272130, upload-time = "2026-04-03T20:55:44.995Z" }, - { url = "https://files.pythonhosted.org/packages/9a/51/57dae534c915e2d3a21490e88836fa2ae79dde3b66255ecc0c0a155d2c10/regex-2026.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:e0aab3ff447845049d676827d2ff714aab4f73f340e155b7de7458cf53baa5a4", size = 280992, upload-time = "2026-04-03T20:55:47.316Z" }, - { url = "https://files.pythonhosted.org/packages/0a/5e/abaf9f4c3792e34edb1434f06717fae2b07888d85cb5cec29f9204931bf8/regex-2026.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:a7a5bb6aa0cf62208bb4fa079b0c756734f8ad0e333b425732e8609bd51ee22f", size = 273563, upload-time = "2026-04-03T20:55:49.273Z" }, - { url = "https://files.pythonhosted.org/packages/ff/06/35da85f9f217b9538b99cbb170738993bcc3b23784322decb77619f11502/regex-2026.4.4-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:97850d0638391bdc7d35dc1c1039974dcb921eaafa8cc935ae4d7f272b1d60b3", size = 494191, upload-time = "2026-04-03T20:55:51.258Z" }, - { url = "https://files.pythonhosted.org/packages/54/5b/1bc35f479eef8285c4baf88d8c002023efdeebb7b44a8735b36195486ae7/regex-2026.4.4-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:ee7337f88f2a580679f7bbfe69dc86c043954f9f9c541012f49abc554a962f2e", size = 293877, upload-time = "2026-04-03T20:55:53.214Z" }, - { url = "https://files.pythonhosted.org/packages/39/5b/f53b9ad17480b3ddd14c90da04bfb55ac6894b129e5dea87bcaf7d00e336/regex-2026.4.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7429f4e6192c11d659900c0648ba8776243bf396ab95558b8c51a345afeddde6", size = 292410, upload-time = "2026-04-03T20:55:55.736Z" }, - { url = "https://files.pythonhosted.org/packages/bb/56/52377f59f60a7c51aa4161eecf0b6032c20b461805aca051250da435ffc9/regex-2026.4.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc4f10fbd5dd13dcf4265b4cc07d69ca70280742870c97ae10093e3d66000359", size = 811831, upload-time = "2026-04-03T20:55:57.802Z" }, - { url = "https://files.pythonhosted.org/packages/dd/63/8026310bf066f702a9c361f83a8c9658f3fe4edb349f9c1e5d5273b7c40c/regex-2026.4.4-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a152560af4f9742b96f3827090f866eeec5becd4765c8e0d3473d9d280e76a5a", size = 871199, upload-time = "2026-04-03T20:56:00.333Z" }, - { url = "https://files.pythonhosted.org/packages/20/9f/a514bbb00a466dbb506d43f187a04047f7be1505f10a9a15615ead5080ee/regex-2026.4.4-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54170b3e95339f415d54651f97df3bff7434a663912f9358237941bbf9143f55", size = 917649, upload-time = "2026-04-03T20:56:02.445Z" }, - { url = "https://files.pythonhosted.org/packages/cb/6b/8399f68dd41a2030218839b9b18360d79b86d22b9fab5ef477c7f23ca67c/regex-2026.4.4-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:07f190d65f5a72dcb9cf7106bfc3d21e7a49dd2879eda2207b683f32165e4d99", size = 816388, upload-time = "2026-04-03T20:56:04.595Z" }, - { url = "https://files.pythonhosted.org/packages/1e/9c/103963f47c24339a483b05edd568594c2be486188f688c0170fd504b2948/regex-2026.4.4-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9a2741ce5a29d3c84b0b94261ba630ab459a1b847a0d6beca7d62d188175c790", size = 785746, upload-time = "2026-04-03T20:56:07.13Z" }, - { url = "https://files.pythonhosted.org/packages/fa/ee/7f6054c0dec0cee3463c304405e4ff42e27cff05bf36fcb34be549ab17bd/regex-2026.4.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:b26c30df3a28fd9793113dac7385a4deb7294a06c0f760dd2b008bd49a9139bc", size = 801483, upload-time = "2026-04-03T20:56:09.365Z" }, - { url = "https://files.pythonhosted.org/packages/30/c2/51d3d941cf6070dc00c3338ecf138615fc3cce0421c3df6abe97a08af61a/regex-2026.4.4-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:421439d1bee44b19f4583ccf42670ca464ffb90e9fdc38d37f39d1ddd1e44f1f", size = 866331, upload-time = "2026-04-03T20:56:12.039Z" }, - { url = "https://files.pythonhosted.org/packages/16/e8/76d50dcc122ac33927d939f350eebcfe3dbcbda96913e03433fc36de5e63/regex-2026.4.4-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:b40379b53ecbc747fd9bdf4a0ea14eb8188ca1bd0f54f78893a39024b28f4863", size = 772673, upload-time = "2026-04-03T20:56:14.558Z" }, - { url = "https://files.pythonhosted.org/packages/a5/6e/5f6bf75e20ea6873d05ba4ec78378c375cbe08cdec571c83fbb01606e563/regex-2026.4.4-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:08c55c13d2eef54f73eeadc33146fb0baaa49e7335eb1aff6ae1324bf0ddbe4a", size = 857146, upload-time = "2026-04-03T20:56:16.663Z" }, - { url = "https://files.pythonhosted.org/packages/0b/33/3c76d9962949e487ebba353a18e89399f292287204ac8f2f4cfc3a51c233/regex-2026.4.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9776b85f510062f5a75ef112afe5f494ef1635607bf1cc220c1391e9ac2f5e81", size = 803463, upload-time = "2026-04-03T20:56:18.923Z" }, - { url = "https://files.pythonhosted.org/packages/19/eb/ef32dcd2cb69b69bc0c3e55205bce94a7def48d495358946bc42186dcccc/regex-2026.4.4-cp314-cp314t-win32.whl", hash = "sha256:385edaebde5db5be103577afc8699fea73a0e36a734ba24870be7ffa61119d74", size = 275709, upload-time = "2026-04-03T20:56:20.996Z" }, - { url = "https://files.pythonhosted.org/packages/a0/86/c291bf740945acbf35ed7dbebf8e2eea2f3f78041f6bd7cdab80cb274dc0/regex-2026.4.4-cp314-cp314t-win_amd64.whl", hash = "sha256:5d354b18839328927832e2fa5f7c95b7a3ccc39e7a681529e1685898e6436d45", size = 285622, upload-time = "2026-04-03T20:56:23.641Z" }, - { url = "https://files.pythonhosted.org/packages/d5/e7/ec846d560ae6a597115153c02ca6138a7877a1748b2072d9521c10a93e58/regex-2026.4.4-cp314-cp314t-win_arm64.whl", hash = "sha256:af0384cb01a33600c49505c27c6c57ab0b27bf84a74e28524c92ca897ebdac9d", size = 275773, upload-time = "2026-04-03T20:56:26.07Z" }, -] - [[package]] name = "requests" version = "2.33.1" @@ -2208,15 +1421,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d7/8e/7540e8a2036f79a125c1d2ebadf69ed7901608859186c856fa0388ef4197/requests-2.33.1-py3-none-any.whl", hash = "sha256:4e6d1ef462f3626a1f0a0a9c42dd93c63bad33f9f1c1937509b8c5c8718ab56a", size = 64947, upload-time = "2026-03-30T16:09:13.83Z" }, ] -[[package]] -name = "rfc3986" -version = "1.5.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/79/30/5b1b6c28c105629cc12b33bdcbb0b11b5bb1880c6cfbd955f9e792921aa8/rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835", size = 49378, upload-time = "2021-05-07T23:29:27.183Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c4/e5/63ca2c4edf4e00657584608bee1001302bbf8c5f569340b78304f2f446cb/rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97", size = 31976, upload-time = "2021-05-07T23:29:25.611Z" }, -] - [[package]] name = "rich" version = "14.3.3" @@ -2292,56 +1496,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/58/ed/dea90a65b7d9e69888890fb14c90d7f51bf0c1e82ad800aeb0160e4bacfd/ruff-0.15.10-py3-none-win_arm64.whl", hash = "sha256:601d1610a9e1f1c2165a4f561eeaa2e2ea1e97f3287c5aa258d3dab8b57c6188", size = 11035607, upload-time = "2026-04-09T14:05:47.593Z" }, ] -[[package]] -name = "safetensors" -version = "0.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/29/9c/6e74567782559a63bd040a236edca26fd71bc7ba88de2ef35d75df3bca5e/safetensors-0.7.0.tar.gz", hash = "sha256:07663963b67e8bd9f0b8ad15bb9163606cd27cc5a1b96235a50d8369803b96b0", size = 200878, upload-time = "2025-11-19T15:18:43.199Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/47/aef6c06649039accf914afef490268e1067ed82be62bcfa5b7e886ad15e8/safetensors-0.7.0-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:c82f4d474cf725255d9e6acf17252991c3c8aac038d6ef363a4bf8be2f6db517", size = 467781, upload-time = "2025-11-19T15:18:35.84Z" }, - { url = "https://files.pythonhosted.org/packages/e8/00/374c0c068e30cd31f1e1b46b4b5738168ec79e7689ca82ee93ddfea05109/safetensors-0.7.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:94fd4858284736bb67a897a41608b5b0c2496c9bdb3bf2af1fa3409127f20d57", size = 447058, upload-time = "2025-11-19T15:18:34.416Z" }, - { url = "https://files.pythonhosted.org/packages/f1/06/578ffed52c2296f93d7fd2d844cabfa92be51a587c38c8afbb8ae449ca89/safetensors-0.7.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e07d91d0c92a31200f25351f4acb2bc6aff7f48094e13ebb1d0fb995b54b6542", size = 491748, upload-time = "2025-11-19T15:18:09.79Z" }, - { url = "https://files.pythonhosted.org/packages/ae/33/1debbbb70e4791dde185edb9413d1fe01619255abb64b300157d7f15dddd/safetensors-0.7.0-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8469155f4cb518bafb4acf4865e8bb9d6804110d2d9bdcaa78564b9fd841e104", size = 503881, upload-time = "2025-11-19T15:18:16.145Z" }, - { url = "https://files.pythonhosted.org/packages/8e/1c/40c2ca924d60792c3be509833df711b553c60effbd91da6f5284a83f7122/safetensors-0.7.0-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:54bef08bf00a2bff599982f6b08e8770e09cc012d7bba00783fc7ea38f1fb37d", size = 623463, upload-time = "2025-11-19T15:18:21.11Z" }, - { url = "https://files.pythonhosted.org/packages/9b/3a/13784a9364bd43b0d61eef4bea2845039bc2030458b16594a1bd787ae26e/safetensors-0.7.0-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:42cb091236206bb2016d245c377ed383aa7f78691748f3bb6ee1bfa51ae2ce6a", size = 532855, upload-time = "2025-11-19T15:18:25.719Z" }, - { url = "https://files.pythonhosted.org/packages/a0/60/429e9b1cb3fc651937727befe258ea24122d9663e4d5709a48c9cbfceecb/safetensors-0.7.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac7252938f0696ddea46f5e855dd3138444e82236e3be475f54929f0c510d48", size = 507152, upload-time = "2025-11-19T15:18:33.023Z" }, - { url = "https://files.pythonhosted.org/packages/3c/a8/4b45e4e059270d17af60359713ffd83f97900d45a6afa73aaa0d737d48b6/safetensors-0.7.0-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1d060c70284127fa805085d8f10fbd0962792aed71879d00864acda69dbab981", size = 541856, upload-time = "2025-11-19T15:18:31.075Z" }, - { url = "https://files.pythonhosted.org/packages/06/87/d26d8407c44175d8ae164a95b5a62707fcc445f3c0c56108e37d98070a3d/safetensors-0.7.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:cdab83a366799fa730f90a4ebb563e494f28e9e92c4819e556152ad55e43591b", size = 674060, upload-time = "2025-11-19T15:18:37.211Z" }, - { url = "https://files.pythonhosted.org/packages/11/f5/57644a2ff08dc6325816ba7217e5095f17269dada2554b658442c66aed51/safetensors-0.7.0-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:672132907fcad9f2aedcb705b2d7b3b93354a2aec1b2f706c4db852abe338f85", size = 771715, upload-time = "2025-11-19T15:18:38.689Z" }, - { url = "https://files.pythonhosted.org/packages/86/31/17883e13a814bd278ae6e266b13282a01049b0c81341da7fd0e3e71a80a3/safetensors-0.7.0-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:5d72abdb8a4d56d4020713724ba81dac065fedb7f3667151c4a637f1d3fb26c0", size = 714377, upload-time = "2025-11-19T15:18:40.162Z" }, - { url = "https://files.pythonhosted.org/packages/4a/d8/0c8a7dc9b41dcac53c4cbf9df2b9c83e0e0097203de8b37a712b345c0be5/safetensors-0.7.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b0f6d66c1c538d5a94a73aa9ddca8ccc4227e6c9ff555322ea40bdd142391dd4", size = 677368, upload-time = "2025-11-19T15:18:41.627Z" }, - { url = "https://files.pythonhosted.org/packages/05/e5/cb4b713c8a93469e3c5be7c3f8d77d307e65fe89673e731f5c2bfd0a9237/safetensors-0.7.0-cp38-abi3-win32.whl", hash = "sha256:c74af94bf3ac15ac4d0f2a7c7b4663a15f8c2ab15ed0fc7531ca61d0835eccba", size = 326423, upload-time = "2025-11-19T15:18:45.74Z" }, - { url = "https://files.pythonhosted.org/packages/5d/e6/ec8471c8072382cb91233ba7267fd931219753bb43814cbc71757bfd4dab/safetensors-0.7.0-cp38-abi3-win_amd64.whl", hash = "sha256:d1239932053f56f3456f32eb9625590cc7582e905021f94636202a864d470755", size = 341380, upload-time = "2025-11-19T15:18:44.427Z" }, -] - -[[package]] -name = "segments" -version = "2.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "csvw" }, - { name = "regex" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/8b/57/85cac3a8e32370e88fa5fa92812edb6025db7fcbed51452bd56ee1524957/segments-2.4.0.tar.gz", hash = "sha256:bba71f5520ddd54c8aa2f4d765a60618c6862162d6e7356a4a097f2223166f5b", size = 18662, upload-time = "2026-03-07T10:01:28.925Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/be/60/eef9acce946177f92c9aabf432224d87ab908bafafac516a36ab924199f3/segments-2.4.0-py2.py3-none-any.whl", hash = "sha256:4021dc67f201cc03c864c74c618bdb163b1af629da3040babbaa37d8813f3db0", size = 16321, upload-time = "2026-03-07T10:01:27.885Z" }, -] - -[[package]] -name = "setuptools" -version = "81.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0d/1c/73e719955c59b8e424d015ab450f51c0af856ae46ea2da83eba51cc88de1/setuptools-81.0.0.tar.gz", hash = "sha256:487b53915f52501f0a79ccfd0c02c165ffe06631443a886740b91af4b7a5845a", size = 1198299, upload-time = "2026-02-06T21:10:39.601Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e1/e3/c164c88b2e5ce7b24d667b9bd83589cf4f3520d97cad01534cd3c4f55fdb/setuptools-81.0.0-py3-none-any.whl", hash = "sha256:fdd925d5c5d9f62e4b74b30d6dd7828ce236fd6ed998a08d81de62ce5a6310d6", size = 1062021, upload-time = "2026-02-06T21:10:37.175Z" }, -] - -[[package]] -name = "sgmllib3k" -version = "1.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9e/bd/3704a8c3e0942d711c1299ebf7b9091930adae6675d7c8f476a7ce48653c/sgmllib3k-1.0.0.tar.gz", hash = "sha256:7868fb1c8bfa764c1ac563d3cf369c381d1325d36124933a726f29fcdaa812e9", size = 5750, upload-time = "2010-08-24T14:33:52.445Z" } - [[package]] name = "shellingham" version = "1.5.4" @@ -2360,106 +1514,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, ] -[[package]] -name = "smart-open" -version = "7.5.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "wrapt" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e8/be/a66598b305763861a9ab15ff0f2fbc44e47b1ce7a776797337a4eef37c66/smart_open-7.5.1.tar.gz", hash = "sha256:3f08e16827c4733699e6b2cc40328a3568f900cb12ad9a3ad233ba6c872d9fe7", size = 54034, upload-time = "2026-02-23T11:01:28.979Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5e/ea/dcdecd68acebb49d3fd560473a43499b1635076f7f1ae8641c060fe7ce74/smart_open-7.5.1-py3-none-any.whl", hash = "sha256:3e07cbbd9c8a908bcb8e25d48becf1a5cbb4886fa975e9f34c672ed171df2318", size = 64108, upload-time = "2026-02-23T11:01:27.429Z" }, -] - -[[package]] -name = "soundfile" -version = "0.13.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cffi" }, - { name = "numpy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e1/41/9b873a8c055582859b239be17902a85339bec6a30ad162f98c9b0288a2cc/soundfile-0.13.1.tar.gz", hash = "sha256:b2c68dab1e30297317080a5b43df57e302584c49e2942defdde0acccc53f0e5b", size = 46156, upload-time = "2025-01-25T09:17:04.831Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/64/28/e2a36573ccbcf3d57c00626a21fe51989380636e821b341d36ccca0c1c3a/soundfile-0.13.1-py2.py3-none-any.whl", hash = "sha256:a23c717560da2cf4c7b5ae1142514e0fd82d6bbd9dfc93a50423447142f2c445", size = 25751, upload-time = "2025-01-25T09:16:44.235Z" }, - { url = "https://files.pythonhosted.org/packages/ea/ab/73e97a5b3cc46bba7ff8650a1504348fa1863a6f9d57d7001c6b67c5f20e/soundfile-0.13.1-py2.py3-none-macosx_10_9_x86_64.whl", hash = "sha256:82dc664d19831933fe59adad199bf3945ad06d84bc111a5b4c0d3089a5b9ec33", size = 1142250, upload-time = "2025-01-25T09:16:47.583Z" }, - { url = "https://files.pythonhosted.org/packages/a0/e5/58fd1a8d7b26fc113af244f966ee3aecf03cb9293cb935daaddc1e455e18/soundfile-0.13.1-py2.py3-none-macosx_11_0_arm64.whl", hash = "sha256:743f12c12c4054921e15736c6be09ac26b3b3d603aef6fd69f9dde68748f2593", size = 1101406, upload-time = "2025-01-25T09:16:49.662Z" }, - { url = "https://files.pythonhosted.org/packages/58/ae/c0e4a53d77cf6e9a04179535766b3321b0b9ced5f70522e4caf9329f0046/soundfile-0.13.1-py2.py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:9c9e855f5a4d06ce4213f31918653ab7de0c5a8d8107cd2427e44b42df547deb", size = 1235729, upload-time = "2025-01-25T09:16:53.018Z" }, - { url = "https://files.pythonhosted.org/packages/57/5e/70bdd9579b35003a489fc850b5047beeda26328053ebadc1fb60f320f7db/soundfile-0.13.1-py2.py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:03267c4e493315294834a0870f31dbb3b28a95561b80b134f0bd3cf2d5f0e618", size = 1313646, upload-time = "2025-01-25T09:16:54.872Z" }, - { url = "https://files.pythonhosted.org/packages/fe/df/8c11dc4dfceda14e3003bb81a0d0edcaaf0796dd7b4f826ea3e532146bba/soundfile-0.13.1-py2.py3-none-win32.whl", hash = "sha256:c734564fab7c5ddf8e9be5bf70bab68042cd17e9c214c06e365e20d64f9a69d5", size = 899881, upload-time = "2025-01-25T09:16:56.663Z" }, - { url = "https://files.pythonhosted.org/packages/14/e9/6b761de83277f2f02ded7e7ea6f07828ec78e4b229b80e4ca55dd205b9dc/soundfile-0.13.1-py2.py3-none-win_amd64.whl", hash = "sha256:1e70a05a0626524a69e9f0f4dd2ec174b4e9567f4d8b6c11d38b5c289be36ee9", size = 1019162, upload-time = "2025-01-25T09:16:59.573Z" }, -] - -[[package]] -name = "spacy" -version = "3.8.13" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "catalogue" }, - { name = "confection" }, - { name = "cymem" }, - { name = "jinja2" }, - { name = "murmurhash" }, - { name = "numpy" }, - { name = "packaging" }, - { name = "preshed" }, - { name = "pydantic" }, - { name = "requests" }, - { name = "setuptools" }, - { name = "spacy-legacy" }, - { name = "spacy-loggers" }, - { name = "srsly" }, - { name = "thinc" }, - { name = "tqdm" }, - { name = "typer" }, - { name = "wasabi" }, - { name = "weasel" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/63/d7/1924f32272f50d2f13275f16d6f869fcec47b2b676b64f91fa206bd30ef4/spacy-3.8.13.tar.gz", hash = "sha256:eb7c03c2bb16593c34d4c91974118f0931c6e6969dbfe895b1a026c6714176cf", size = 1328016, upload-time = "2026-03-23T17:44:32.042Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f2/02/bf2943f61a8bd21cca90e4fe19c4da8752c386f02a76e326b33199e09953/spacy-3.8.13-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:edcdd4f99e2711fc90c6ba3e8e07f7dc9753d111377ba7b7b5eb0d7259b0d211", size = 6209920, upload-time = "2026-03-23T17:43:58.441Z" }, - { url = "https://files.pythonhosted.org/packages/e7/87/fa5e4eb2ccb660d5042eb9144134dc6238c132ec812131bb0aca296bcdd9/spacy-3.8.13-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b8fd0188f78e032ac58f70a00748d591e244f3d4718e0ead2d9418b4148c744d", size = 6040696, upload-time = "2026-03-23T17:44:00.704Z" }, - { url = "https://files.pythonhosted.org/packages/ed/f3/9132878e0c18d627adffc1d23129a3706385d4f0fbe2ea5839523519452c/spacy-3.8.13-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0dc2d058cb919102bf0634b1b41aff64b867d9bab36bd35979ab658b5b2e4c27", size = 32431416, upload-time = "2026-03-23T17:44:05.435Z" }, - { url = "https://files.pythonhosted.org/packages/80/73/396c5c3aaef5004d04abbda4ec736ce4d33944aff6722e974cebdd4023fd/spacy-3.8.13-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ef884df658d3b60d91cbc1e9b62a6d932589d2fa3f322c3f739b6ffdf3303e0c", size = 32483676, upload-time = "2026-03-23T17:44:10.913Z" }, - { url = "https://files.pythonhosted.org/packages/71/01/15ceb2097a817c2912297eac29f801bd71d895e93d7557110937c88f7c77/spacy-3.8.13-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:86f58d686d1ba6f0dc818005ee6ad87ade8dde7224012f1870a0d31abccd349e", size = 31732468, upload-time = "2026-03-23T17:44:15.967Z" }, - { url = "https://files.pythonhosted.org/packages/03/c4/b4c39083df6209414faec7d7471994316dc6fca68c4d2f1f8f099f25c2e3/spacy-3.8.13-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:dbcb411b9749a1cd5e4325953212b3984d8c0f60b4976943e77fb0c7d5027383", size = 32473870, upload-time = "2026-03-23T17:44:21.36Z" }, - { url = "https://files.pythonhosted.org/packages/f2/5c/9b0fa420b01809c0170b3cf0bf509ec06b4da96214995360815615b8e8fa/spacy-3.8.13-cp314-cp314-win_amd64.whl", hash = "sha256:bf9b6879d70201acb73fc5f07d550ff488d3b5f15a959e9896717b2635c8e137", size = 14405303, upload-time = "2026-03-23T17:44:25.83Z" }, - { url = "https://files.pythonhosted.org/packages/fa/58/0001fd8124b62a2a9984278d793e3abfa1b70e969fc26a8668755178db84/spacy-3.8.13-cp314-cp314-win_arm64.whl", hash = "sha256:b2a402f229fcb5dba5454c346468757bd3a5215809e784b85d739ca84916f05b", size = 13834663, upload-time = "2026-03-23T17:44:29.43Z" }, -] - -[[package]] -name = "spacy-curated-transformers" -version = "0.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "curated-tokenizers" }, - { name = "curated-transformers" }, - { name = "torch" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d8/b3/a4fd3cf28008cbe1d95463b5c76a0d9c8da7b9ad4f06289c2be4aae62052/spacy_curated_transformers-0.3.1.tar.gz", hash = "sha256:7e53fccf64260e641b0a3f2b65b6d98381b86cef6eeb21ce279e8db849e8525d", size = 218990, upload-time = "2025-05-28T10:29:32.69Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/42/d8/f053d43125ae4ad14f3e2a12a475a656128233f1f40a272c6e09a05c73e8/spacy_curated_transformers-0.3.1-py2.py3-none-any.whl", hash = "sha256:503559b6a1d6e44ec2c978e18ed871ce5c3d56871dc9216c0e1523428204e610", size = 237943, upload-time = "2025-05-28T10:29:31.058Z" }, -] - -[[package]] -name = "spacy-legacy" -version = "3.0.12" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d9/79/91f9d7cc8db5642acad830dcc4b49ba65a7790152832c4eceb305e46d681/spacy-legacy-3.0.12.tar.gz", hash = "sha256:b37d6e0c9b6e1d7ca1cf5bc7152ab64a4c4671f59c85adaf7a3fcb870357a774", size = 23806, upload-time = "2023-01-23T09:04:15.104Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c3/55/12e842c70ff8828e34e543a2c7176dac4da006ca6901c9e8b43efab8bc6b/spacy_legacy-3.0.12-py2.py3-none-any.whl", hash = "sha256:476e3bd0d05f8c339ed60f40986c07387c0a71479245d6d0f4298dbd52cda55f", size = 29971, upload-time = "2023-01-23T09:04:13.45Z" }, -] - -[[package]] -name = "spacy-loggers" -version = "1.0.5" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/67/3d/926db774c9c98acf66cb4ed7faf6c377746f3e00b84b700d0868b95d0712/spacy-loggers-1.0.5.tar.gz", hash = "sha256:d60b0bdbf915a60e516cc2e653baeff946f0cfc461b452d11a4d5458c6fe5f24", size = 20811, upload-time = "2023-09-11T12:26:52.323Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/33/78/d1a1a026ef3af911159398c939b1509d5c36fe524c7b644f34a5146c4e16/spacy_loggers-1.0.5-py3-none-any.whl", hash = "sha256:196284c9c446cc0cdb944005384270d775fdeaf4f494d8e269466cfa497ef645", size = 22343, upload-time = "2023-09-11T12:26:50.586Z" }, -] - [[package]] name = "sqlalchemy" version = "2.0.49" @@ -2492,30 +1546,28 @@ asyncio = [ ] [[package]] -name = "srsly" -version = "2.5.3" +name = "sse-starlette" +version = "3.4.4" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "catalogue" }, + { name = "anyio" }, + { name = "starlette" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2b/db/f794f219a6c788b881252d2536a8c4a97d2bdaadc690391e1cb53d123d71/srsly-2.5.3.tar.gz", hash = "sha256:08f98dbecbff3a31466c4ae7c833131f59d3655a0ad8ac749e6e2c149e2b0680", size = 490881, upload-time = "2026-03-23T11:56:59.865Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f7/2b/58abc2d1fd397e7dde08e947e05c884d8ef2f78d5e2588c17a12d42d6994/sse_starlette-3.4.4.tar.gz", hash = "sha256:07e0fa0460138baf25cdd5fb28683472c3995dc1642225191b3832d62526bcb0", size = 31819, upload-time = "2026-05-12T17:37:17.019Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e1/5b/e4ef43c2a381711230af98d4c94a5323df48d6a7899ee652e05bf889290e/srsly-2.5.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:39c13d552a9f9674a12cdcdc66b0c2f02f3430d0cd04c5f9cf598824c2bd3d65", size = 661294, upload-time = "2026-03-23T11:56:23.29Z" }, - { url = "https://files.pythonhosted.org/packages/92/2d/ebce7f3717e52cd0a01f4ec570f388f3b7098526794fcf1ad734e0b8f852/srsly-2.5.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:14c930767cc169611a2dc14e23bc7638cfb616d6f79029700ade033607343540", size = 660952, upload-time = "2026-03-23T11:56:24.908Z" }, - { url = "https://files.pythonhosted.org/packages/22/47/a8f3e9b214be2624c8e8a78d38ca7b1d4e26b92d57018412e4bfc4abe89a/srsly-2.5.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2f2d464f0d0237e32fb53f0ec6f05418652c550e772b50e9918e83a1577cba4d", size = 1154554, upload-time = "2026-03-23T11:56:26.608Z" }, - { url = "https://files.pythonhosted.org/packages/d6/71/2a89dc3180a51e633a87a079ca064225f4aaf46c7b2a5fc720e28f261d98/srsly-2.5.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d18933248a5bb0ad56a1bae6003a9a7f37daac2ecb0c5bcbfaaf081b317e1c84", size = 1155746, upload-time = "2026-03-23T11:56:28.102Z" }, - { url = "https://files.pythonhosted.org/packages/b8/36/72e5ce3153927ca404b6f5bf5280e6ff3399c11557df472b153945468e0a/srsly-2.5.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7ea5412ea229e571ac9738cbe14f845cc06c8e4e956afb5f42061ccd087ef31f", size = 1112374, upload-time = "2026-03-23T11:56:29.591Z" }, - { url = "https://files.pythonhosted.org/packages/04/b2/0895de109c28eca0d41a811ab7c076d4e4a505e8466f06bae22f5180a1dd/srsly-2.5.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8d3988970b4cf7d03bdd5b5169302ff84562dd2e1e0f84aeb34df3e5b5dc19bf", size = 1127732, upload-time = "2026-03-23T11:56:31.458Z" }, - { url = "https://files.pythonhosted.org/packages/c7/79/a37fa7759797fbdfe0a2e029ab13e78b1e81e191220d2bb8ff57d869aefb/srsly-2.5.3-cp314-cp314-win_amd64.whl", hash = "sha256:6a02d7dcc16126c8fae1c1c09b2072798a1dc482ab5f9c52b12c7114dac47325", size = 656467, upload-time = "2026-03-23T11:56:33.14Z" }, - { url = "https://files.pythonhosted.org/packages/d7/25/0dae019b3b90ad9037f91de4c390555cdaac9460a93ad62b02b03babdff5/srsly-2.5.3-cp314-cp314-win_arm64.whl", hash = "sha256:1c9129c4abe31903ff7996904a51afdd5428060de6c3d12af49a4da5e8df2821", size = 643040, upload-time = "2026-03-23T11:56:34.448Z" }, - { url = "https://files.pythonhosted.org/packages/3a/44/72dd5285b2e05435d98b0797f101d91d9b345d491ddc1fdb9bd09e27ccb8/srsly-2.5.3-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:29d5d01ba4c2e9c01f936e5e6d5babc4a47b38c9cbd6e1ec23f6d5a49df32605", size = 666200, upload-time = "2026-03-23T11:56:35.753Z" }, - { url = "https://files.pythonhosted.org/packages/d2/ad/002c71b87fc3f648c9bf0ec47de0c3822bf2c95c8896a589dd03e7fd3977/srsly-2.5.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5c8df4039426d99f0148b5743542842ab96b82daded0b342555e15a639927757", size = 667409, upload-time = "2026-03-23T11:56:37.172Z" }, - { url = "https://files.pythonhosted.org/packages/2a/35/2cea3d5e80aeecfc4ece9e7e1783e7792cc3bad7ab85ab585882e1db4e38/srsly-2.5.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:06a43d63bde2e8cccadb953d7fff70b18196ca286b65dd2ad16006d65f3f8166", size = 1265941, upload-time = "2026-03-23T11:56:38.825Z" }, - { url = "https://files.pythonhosted.org/packages/aa/38/8a4d7e86dd0370a2e5af251b646000197bb5b7e0f9aa360c71bbfb253d0d/srsly-2.5.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:808cfafc047f0dec507a34c8fa8e4cda5722737fd33577df73452f52f7aca644", size = 1250693, upload-time = "2026-03-23T11:56:40.449Z" }, - { url = "https://files.pythonhosted.org/packages/99/05/340129de5ea7b237271b12f8a6962cfa7eb0c5a3056794626d348c5ae7c7/srsly-2.5.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:71d4cbe2b2a1335c76ed0acae2dc862163787d8b01a705e1949796907ed94ccd", size = 1242408, upload-time = "2026-03-23T11:56:41.8Z" }, - { url = "https://files.pythonhosted.org/packages/01/cb/d7fee7ab27c6aa2e3f865fb7b50ba18c81a4c763bba12bdf53df246441bc/srsly-2.5.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:565f69083d33cb329cfc74317da937fb3270c0f40fabc1b4488702d8074b4a3e", size = 1242749, upload-time = "2026-03-23T11:56:43.246Z" }, - { url = "https://files.pythonhosted.org/packages/d8/d1/9bad3a0f2fa7b72f4e0cf1d267b00513092d20ef538c47f72823ae4f7656/srsly-2.5.3-cp314-cp314t-win_amd64.whl", hash = "sha256:8ac016ffaeac35bc010992b71bf8afdd39d458f201c8138d84cf78778a936e6c", size = 673783, upload-time = "2026-03-23T11:56:44.875Z" }, - { url = "https://files.pythonhosted.org/packages/2a/ae/57d1d7af907e20c077e113e0e4976f87b82c0a415403d99284a262229dd0/srsly-2.5.3-cp314-cp314t-win_arm64.whl", hash = "sha256:d822083fe26ec6728bd8c273ac121fc4ab3864a0fdf0cf0ff3efb188fcd209ed", size = 650229, upload-time = "2026-03-23T11:56:46.148Z" }, + { url = "https://files.pythonhosted.org/packages/dc/67/805710444ea8cc75fbf70b920ed431a560c4bf9c57f7d5a3117213189399/sse_starlette-3.4.4-py3-none-any.whl", hash = "sha256:3f4dd50d8aed2771a091f3a83000323fc3844541c16b4fe585ae2420cc6df973", size = 16514, upload-time = "2026-05-12T17:37:15.601Z" }, +] + +[[package]] +name = "starlette" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c5/bf/616a066c2760f6c2b1ae3437cc28149734d069fbb46511712beae118a68c/starlette-1.2.0.tar.gz", hash = "sha256:3c5a6b23fff42492914e93890bb80cbfea72dbf37de268eec06185d62a4ca553", size = 2668923, upload-time = "2026-05-28T11:42:50.568Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9f/85/492183764d5d01d4514be3730fdb8e228a80605783099551c51627578b5d/starlette-1.2.0-py3-none-any.whl", hash = "sha256:36e0c76ac59157e75dc4b3bdeafba97fb04eaf1878045f15dbef666a6f092ed7", size = 73213, upload-time = "2026-05-28T11:42:48.801Z" }, ] [[package]] @@ -2530,54 +1582,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" }, ] -[[package]] -name = "termcolor" -version = "3.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/46/79/cf31d7a93a8fdc6aa0fbb665be84426a8c5a557d9240b6239e9e11e35fc5/termcolor-3.3.0.tar.gz", hash = "sha256:348871ca648ec6a9a983a13ab626c0acce02f515b9e1983332b17af7979521c5", size = 14434, upload-time = "2025-12-29T12:55:21.882Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/33/d1/8bb87d21e9aeb323cc03034f5eaf2c8f69841e40e4853c2627edf8111ed3/termcolor-3.3.0-py3-none-any.whl", hash = "sha256:cf642efadaf0a8ebbbf4bc7a31cec2f9b5f21a9f726f4ccbb08192c9c26f43a5", size = 7734, upload-time = "2025-12-29T12:55:20.718Z" }, -] - -[[package]] -name = "thinc" -version = "8.3.13" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "blis" }, - { name = "catalogue" }, - { name = "confection" }, - { name = "cymem" }, - { name = "murmurhash" }, - { name = "numpy" }, - { name = "packaging" }, - { name = "preshed" }, - { name = "pydantic" }, - { name = "setuptools" }, - { name = "srsly" }, - { name = "wasabi" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/13/46/76df95f2c327f9a9cef30c1523bf285627897097163584dcf5f77b2ebce2/thinc-8.3.13.tar.gz", hash = "sha256:68e658549fc1eb3ff92aed5147fcbb9c15d6e9cc0e623b4d0998d16522ffb4f9", size = 194640, upload-time = "2026-03-23T07:22:36.41Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/80/40/f4937d113912c6d669ffe982356ab29dcb6c7fe3be926a15981dbbb6a91c/thinc-8.3.13-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:7badb0be4825535e6362c19e8a41872b65409e9da46d3453a391b843a0720865", size = 817024, upload-time = "2026-03-23T07:22:23.005Z" }, - { url = "https://files.pythonhosted.org/packages/d2/00/4d4ed1a11ba2920b85a03a0683b16d97dc5beb2e78078dbf0e13e43bcea7/thinc-8.3.13-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:565300b7e13de799e5abff00d445f537e9256cf7da4dcb0d0f005fc16748a29e", size = 792096, upload-time = "2026-03-23T07:22:24.349Z" }, - { url = "https://files.pythonhosted.org/packages/44/5d/dc33d6932be8721af2ef76b4a3a6e8020648630eabae61fb916d2a861d1d/thinc-8.3.13-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c17cef1900a1aba7e1487493d16b8aa0a8633116f1b2a51c6649a4000697f17b", size = 3842215, upload-time = "2026-03-23T07:22:25.836Z" }, - { url = "https://files.pythonhosted.org/packages/af/bc/a6d37d8dadc2c5b524f51192413481160c42c9dd6105e8d5551531623225/thinc-8.3.13-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f4f26d1eec9b2a6a8f2e0298a5515d13eb06d70730d0d9e1040bb329e12bf3fb", size = 3849253, upload-time = "2026-03-23T07:22:27.845Z" }, - { url = "https://files.pythonhosted.org/packages/7a/59/ce9c7067f1dfe5985875927de9cf7a79f9dae3e69487fd650dfba558029d/thinc-8.3.13-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a61a31fd0ce3c2771cf4901ba6df70e774ffe32febf1024c5b43d63575cd58fe", size = 4831163, upload-time = "2026-03-23T07:22:29.395Z" }, - { url = "https://files.pythonhosted.org/packages/4f/a8/f57819347fc4d8bef2204d15fcbb9d7dff2d6cdd5f83d5ed91456ddacc55/thinc-8.3.13-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ba8119daf84a12259ae4d251d36426417bafa0b34108890b4b7e2b50966bd990", size = 4986051, upload-time = "2026-03-23T07:22:30.933Z" }, - { url = "https://files.pythonhosted.org/packages/05/ef/a82214bb7c7c1e2d92b69e1a7654be90cfab180082c6108e45a98af2422c/thinc-8.3.13-cp314-cp314-win_amd64.whl", hash = "sha256:433e3826e018da489f1a8068e6de677f6eff3cc93991a599d90f12cd1bc26cdc", size = 1740382, upload-time = "2026-03-23T07:22:32.869Z" }, - { url = "https://files.pythonhosted.org/packages/9f/ef/1648fda54e9689058335ff54f650a7a314db2a42e21af1b83949b2dc748e/thinc-8.3.13-cp314-cp314-win_arm64.whl", hash = "sha256:11754fada9ad5ba2e02d5f3f234f940e24015b82333db58372f4a6aedad9b43f", size = 1667687, upload-time = "2026-03-23T07:22:34.967Z" }, -] - -[[package]] -name = "tld" -version = "0.13.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5c/5d/76b4383ac4e5b5e254e50c09807b3e13820bed6d6c11cd540264988d6802/tld-0.13.2.tar.gz", hash = "sha256:d983fa92b9d717400742fca844e29d5e18271079c7bcfabf66d01b39b4a14345", size = 467175, upload-time = "2026-03-06T23:50:34.498Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/90/39a85a4b63c84213e78b3c17d22e1bf45328acf8ebb33ef93be30d0a3911/tld-0.13.2-py2.py3-none-any.whl", hash = "sha256:9b8fdbdb880e7ba65b216a4937f2c94c49a7226723783d5838fc958ac76f4e0c", size = 296743, upload-time = "2026-03-06T23:50:32.465Z" }, -] - [[package]] name = "tokenizers" version = "0.22.2" @@ -2604,37 +1608,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/72/f4/0de46cfa12cdcbcd464cc59fde36912af405696f687e53a091fb432f694c/tokenizers-0.22.2-cp39-abi3-win_arm64.whl", hash = "sha256:9ce725d22864a1e965217204946f830c37876eee3b2ba6fc6255e8e903d5fcbc", size = 2612133, upload-time = "2026-01-05T10:45:17.232Z" }, ] -[[package]] -name = "torch" -version = "2.11.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cuda-bindings", marker = "sys_platform == 'linux'" }, - { name = "cuda-toolkit", extra = ["cublas", "cudart", "cufft", "cufile", "cupti", "curand", "cusolver", "cusparse", "nvjitlink", "nvrtc", "nvtx"], marker = "sys_platform == 'linux'" }, - { name = "filelock" }, - { name = "fsspec" }, - { name = "jinja2" }, - { name = "networkx" }, - { name = "nvidia-cudnn-cu13", marker = "sys_platform == 'linux'" }, - { name = "nvidia-cusparselt-cu13", marker = "sys_platform == 'linux'" }, - { name = "nvidia-nccl-cu13", marker = "sys_platform == 'linux'" }, - { name = "nvidia-nvshmem-cu13", marker = "sys_platform == 'linux'" }, - { name = "setuptools" }, - { name = "sympy" }, - { name = "triton", marker = "sys_platform == 'linux'" }, - { name = "typing-extensions" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/26/0d/8603382f61abd0db35841148ddc1ffd607bf3100b11c6e1dab6d2fc44e72/torch-2.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:01018087326984a33b64e04c8cb5c2795f9120e0d775ada1f6638840227b04d7", size = 80573442, upload-time = "2026-03-23T18:09:10.117Z" }, - { url = "https://files.pythonhosted.org/packages/c7/86/7cd7c66cb9cec6be330fff36db5bd0eef386d80c031b581ec81be1d4b26c/torch-2.11.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:2bb3cc54bd0dea126b0060bb1ec9de0f9c7f7342d93d436646516b0330cd5be7", size = 419749385, upload-time = "2026-03-23T18:07:33.77Z" }, - { url = "https://files.pythonhosted.org/packages/47/e8/b98ca2d39b2e0e4730c0ee52537e488e7008025bc77ca89552ff91021f7c/torch-2.11.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:4dc8b3809469b6c30b411bb8c4cad3828efd26236153d9beb6a3ec500f211a60", size = 530716756, upload-time = "2026-03-23T18:07:50.02Z" }, - { url = "https://files.pythonhosted.org/packages/78/88/d4a4cda8362f8a30d1ed428564878c3cafb0d87971fbd3947d4c84552095/torch-2.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:2b4e811728bd0cc58fb2b0948fe939a1ee2bf1422f6025be2fca4c7bd9d79718", size = 114552300, upload-time = "2026-03-23T18:09:05.617Z" }, - { url = "https://files.pythonhosted.org/packages/bf/46/4419098ed6d801750f26567b478fc185c3432e11e2cad712bc6b4c2ab0d0/torch-2.11.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:8245477871c3700d4370352ffec94b103cfcb737229445cf9946cddb7b2ca7cd", size = 80959460, upload-time = "2026-03-23T18:09:00.818Z" }, - { url = "https://files.pythonhosted.org/packages/fd/66/54a56a4a6ceaffb567231994a9745821d3af922a854ed33b0b3a278e0a99/torch-2.11.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:ab9a8482f475f9ba20e12db84b0e55e2f58784bdca43a854a6ccd3fd4b9f75e6", size = 419735835, upload-time = "2026-03-23T18:07:18.974Z" }, - { url = "https://files.pythonhosted.org/packages/b1/e7/0b6665f533aa9e337662dc190425abc0af1fe3234088f4454c52393ded61/torch-2.11.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:563ed3d25542d7e7bbc5b235ccfacfeb97fb470c7fee257eae599adb8005c8a2", size = 530613405, upload-time = "2026-03-23T18:08:07.014Z" }, - { url = "https://files.pythonhosted.org/packages/cf/bf/c8d12a2c86dbfd7f40fb2f56fbf5a505ccf2d9ce131eb559dfc7c51e1a04/torch-2.11.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b2a43985ff5ef6ddd923bbcf99943e5f58059805787c5c9a2622bf05ca2965b0", size = 114792991, upload-time = "2026-03-23T18:08:19.216Z" }, -] - [[package]] name = "tqdm" version = "4.67.3" @@ -2647,55 +1620,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/16/e1/3079a9ff9b8e11b846c6ac5c8b5bfb7ff225eee721825310c91b3b50304f/tqdm-4.67.3-py3-none-any.whl", hash = "sha256:ee1e4c0e59148062281c49d80b25b67771a127c85fc9676d3be5f243206826bf", size = 78374, upload-time = "2026-02-03T17:35:50.982Z" }, ] -[[package]] -name = "trafilatura" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "certifi" }, - { name = "charset-normalizer" }, - { name = "courlan" }, - { name = "htmldate" }, - { name = "justext" }, - { name = "lxml" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/06/25/e3ebeefdebfdfae8c4a4396f5a6ea51fc6fa0831d63ce338e5090a8003dc/trafilatura-2.0.0.tar.gz", hash = "sha256:ceb7094a6ecc97e72fea73c7dba36714c5c5b577b6470e4520dca893706d6247", size = 253404, upload-time = "2024-12-03T15:23:24.16Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/b6/097367f180b6383a3581ca1b86fcae284e52075fa941d1232df35293363c/trafilatura-2.0.0-py3-none-any.whl", hash = "sha256:77eb5d1e993747f6f20938e1de2d840020719735690c840b9a1024803a4cd51d", size = 132557, upload-time = "2024-12-03T15:23:21.41Z" }, -] - -[[package]] -name = "transformers" -version = "5.5.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "huggingface-hub" }, - { name = "numpy" }, - { name = "packaging" }, - { name = "pyyaml" }, - { name = "regex" }, - { name = "safetensors" }, - { name = "tokenizers" }, - { name = "tqdm" }, - { name = "typer" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/af/35/cd5b0d1288e65d2c12db4ce84c1ec1074f7ee9bced040de6c9d69e70d620/transformers-5.5.3.tar.gz", hash = "sha256:3f60128e840b40d352655903552e1eed4f94ed49369a4d43e1bc067bd32d3f50", size = 8226047, upload-time = "2026-04-09T15:52:56.231Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a1/0b/f8524551ab2d896dfaca74ddb70a4453d515bbf4ab5451c100c7788ae155/transformers-5.5.3-py3-none-any.whl", hash = "sha256:e48f3ec31dd96505e96e66b63a1e43e1ad7a65749e108d9227caaf51051cdb02", size = 10236257, upload-time = "2026-04-09T15:52:52.866Z" }, -] - -[[package]] -name = "triton" -version = "3.6.0" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/49/55/5ecf0dcaa0f2fbbd4420f7ef227ee3cb172e91e5fede9d0ecaddc43363b4/triton-3.6.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef5523241e7d1abca00f1d240949eebdd7c673b005edbbce0aca95b8191f1d43", size = 176138577, upload-time = "2026-01-20T16:16:25.426Z" }, - { url = "https://files.pythonhosted.org/packages/df/3d/9e7eee57b37c80cec63322c0231bb6da3cfe535a91d7a4d64896fcb89357/triton-3.6.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a17a5d5985f0ac494ed8a8e54568f092f7057ef60e1b0fa09d3fd1512064e803", size = 188273063, upload-time = "2026-01-20T16:01:07.278Z" }, - { url = "https://files.pythonhosted.org/packages/48/db/56ee649cab5eaff4757541325aca81f52d02d4a7cd3506776cad2451e060/triton-3.6.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0b3a97e8ed304dfa9bd23bb41ca04cdf6b2e617d5e782a8653d616037a5d537d", size = 176274804, upload-time = "2026-01-20T16:16:31.528Z" }, - { url = "https://files.pythonhosted.org/packages/f6/56/6113c23ff46c00aae423333eb58b3e60bdfe9179d542781955a5e1514cb3/triton-3.6.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:46bd1c1af4b6704e554cad2eeb3b0a6513a980d470ccfa63189737340c7746a7", size = 188397994, upload-time = "2026-01-20T16:01:14.236Z" }, -] - [[package]] name = "typer" version = "0.24.1" @@ -2753,15 +1677,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c2/14/e2a54fabd4f08cd7af1c07030603c3356b74da07f7cc056e600436edfa17/tzlocal-5.3.1-py3-none-any.whl", hash = "sha256:eb1a66c3ef5847adf7a834f1be0800581b683b5608e74f86ecbcef8ab91bb85d", size = 18026, upload-time = "2025-03-05T21:17:39.857Z" }, ] -[[package]] -name = "uritemplate" -version = "4.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/98/60/f174043244c5306c9988380d2cb10009f91563fc4b31293d27e17201af56/uritemplate-4.2.0.tar.gz", hash = "sha256:480c2ed180878955863323eea31b0ede668795de182617fef9c6ca09e6ec9d0e", size = 33267, upload-time = "2025-06-02T15:12:06.318Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/99/3ae339466c9183ea5b8ae87b34c0b897eda475d2aec2307cae60e5cd4f29/uritemplate-4.2.0-py3-none-any.whl", hash = "sha256:962201ba1c4edcab02e60f9a0d3821e82dfc5d2d6662a21abd533879bdb8a686", size = 11488, upload-time = "2025-06-02T15:12:03.405Z" }, -] - [[package]] name = "urllib3" version = "2.6.3" @@ -2786,15 +1701,16 @@ wheels = [ ] [[package]] -name = "wasabi" -version = "1.1.3" +name = "uvicorn" +version = "0.48.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "click" }, + { name = "h11" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ac/f9/054e6e2f1071e963b5e746b48d1e3727470b2a490834d18ad92364929db3/wasabi-1.1.3.tar.gz", hash = "sha256:4bb3008f003809db0c3e28b4daf20906ea871a2bb43f9914197d540f4f2e0878", size = 30391, upload-time = "2024-05-31T16:56:18.99Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e6/bf/f6544ba992ddb9a6077343a576f9844f7f8f06ab819aefd00206e9255f18/uvicorn-0.48.0.tar.gz", hash = "sha256:a5504207195d08c2511bf9125ede5ac4a4b71725d519e758d01dcf0bc2d31c37", size = 91074, upload-time = "2026-05-24T12:08:41.925Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/06/7c/34330a89da55610daa5f245ddce5aab81244321101614751e7537f125133/wasabi-1.1.3-py3-none-any.whl", hash = "sha256:f76e16e8f7e79f8c4c8be49b4024ac725713ab10cd7f19350ad18a8e3f71728c", size = 27880, upload-time = "2024-05-31T16:56:16.699Z" }, + { url = "https://files.pythonhosted.org/packages/01/be/72532be3da7acc5fdfbccdb95215cd04f995a0886532a5b423f929cda4cc/uvicorn-0.48.0-py3-none-any.whl", hash = "sha256:48097851328b87ec36117d3d575234519eb58c2b22d79666e9bbc6c49a761dad", size = 71410, upload-time = "2026-05-24T12:08:40.258Z" }, ] [[package]] @@ -2806,26 +1722,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0a/10/bd9b185b33ecd2b2523eb83bb1088e6dfdc1dad19136d91312dce9996c37/wassima-2.0.6-py3-none-any.whl", hash = "sha256:24c327cfce58e36b1e554feb809a12cd6677e39158dee419deb0d16b8f648f0d", size = 140613, upload-time = "2026-04-07T01:52:00.835Z" }, ] -[[package]] -name = "weasel" -version = "1.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cloudpathlib" }, - { name = "confection" }, - { name = "httpx" }, - { name = "packaging" }, - { name = "pydantic" }, - { name = "smart-open" }, - { name = "srsly" }, - { name = "typer" }, - { name = "wasabi" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ce/e5/e272bb9a045105a1fdf4b798d8086f5932a178f4d738f17a74f5c9e0ae9a/weasel-1.0.0.tar.gz", hash = "sha256:7b129b44c90cc543b760532974ca1e4eb30dad2aa2026f57bdce66354ae610fc", size = 38682, upload-time = "2026-03-20T08:10:25.266Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0a/07/57ebf7a6798b016c064bd0ca81b4c6a99daa4dc377b898bc7b41eb6b5af0/weasel-1.0.0-py3-none-any.whl", hash = "sha256:89518acee027f49d743126c3502d35e6dd14f5768be5c37c9af47c171b6005cc", size = 50713, upload-time = "2026-03-20T08:10:23.637Z" }, -] - [[package]] name = "werkzeug" version = "3.1.8" @@ -2847,37 +1743,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e1/07/c6fe3ad3e685340704d314d765b7912993bcb8dc198f0e7a89382d37974b/win32_setctime-1.2.0-py3-none-any.whl", hash = "sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390", size = 4083, upload-time = "2024-12-07T15:28:26.465Z" }, ] -[[package]] -name = "wrapt" -version = "2.1.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2e/64/925f213fdcbb9baeb1530449ac71a4d57fc361c053d06bf78d0c5c7cd80c/wrapt-2.1.2.tar.gz", hash = "sha256:3996a67eecc2c68fd47b4e3c564405a5777367adfd9b8abb58387b63ee83b21e", size = 81678, upload-time = "2026-03-06T02:53:25.134Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/39/25/e7ea0b417db02bb796182a5316398a75792cd9a22528783d868755e1f669/wrapt-2.1.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:1370e516598854e5b4366e09ce81e08bfe94d42b0fd569b88ec46cc56d9164a9", size = 61418, upload-time = "2026-03-06T02:53:55.706Z" }, - { url = "https://files.pythonhosted.org/packages/ec/0f/fa539e2f6a770249907757eaeb9a5ff4deb41c026f8466c1c6d799088a9b/wrapt-2.1.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:6de1a3851c27e0bd6a04ca993ea6f80fc53e6c742ee1601f486c08e9f9b900a9", size = 61914, upload-time = "2026-03-06T02:52:53.37Z" }, - { url = "https://files.pythonhosted.org/packages/53/37/02af1867f5b1441aaeda9c82deed061b7cd1372572ddcd717f6df90b5e93/wrapt-2.1.2-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:de9f1a2bbc5ac7f6012ec24525bdd444765a2ff64b5985ac6e0692144838542e", size = 120417, upload-time = "2026-03-06T02:54:30.74Z" }, - { url = "https://files.pythonhosted.org/packages/c3/b7/0138a6238c8ba7476c77cf786a807f871672b37f37a422970342308276e7/wrapt-2.1.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:970d57ed83fa040d8b20c52fe74a6ae7e3775ae8cff5efd6a81e06b19078484c", size = 122797, upload-time = "2026-03-06T02:54:51.539Z" }, - { url = "https://files.pythonhosted.org/packages/e1/ad/819ae558036d6a15b7ed290d5b14e209ca795dd4da9c58e50c067d5927b0/wrapt-2.1.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3969c56e4563c375861c8df14fa55146e81ac11c8db49ea6fb7f2ba58bc1ff9a", size = 117350, upload-time = "2026-03-06T02:54:37.651Z" }, - { url = "https://files.pythonhosted.org/packages/8b/2d/afc18dc57a4600a6e594f77a9ae09db54f55ba455440a54886694a84c71b/wrapt-2.1.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:57d7c0c980abdc5f1d98b11a2aa3bb159790add80258c717fa49a99921456d90", size = 121223, upload-time = "2026-03-06T02:54:35.221Z" }, - { url = "https://files.pythonhosted.org/packages/b9/5b/5ec189b22205697bc56eb3b62aed87a1e0423e9c8285d0781c7a83170d15/wrapt-2.1.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:776867878e83130c7a04237010463372e877c1c994d449ca6aaafeab6aab2586", size = 116287, upload-time = "2026-03-06T02:54:19.654Z" }, - { url = "https://files.pythonhosted.org/packages/f7/2d/f84939a7c9b5e6cdd8a8d0f6a26cabf36a0f7e468b967720e8b0cd2bdf69/wrapt-2.1.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:fab036efe5464ec3291411fabb80a7a39e2dd80bae9bcbeeca5087fdfa891e19", size = 119593, upload-time = "2026-03-06T02:54:16.697Z" }, - { url = "https://files.pythonhosted.org/packages/0b/fe/ccd22a1263159c4ac811ab9374c061bcb4a702773f6e06e38de5f81a1bdc/wrapt-2.1.2-cp314-cp314-win32.whl", hash = "sha256:e6ed62c82ddf58d001096ae84ce7f833db97ae2263bff31c9b336ba8cfe3f508", size = 58631, upload-time = "2026-03-06T02:53:06.498Z" }, - { url = "https://files.pythonhosted.org/packages/65/0a/6bd83be7bff2e7efaac7b4ac9748da9d75a34634bbbbc8ad077d527146df/wrapt-2.1.2-cp314-cp314-win_amd64.whl", hash = "sha256:467e7c76315390331c67073073d00662015bb730c566820c9ca9b54e4d67fd04", size = 60875, upload-time = "2026-03-06T02:53:50.252Z" }, - { url = "https://files.pythonhosted.org/packages/6c/c0/0b3056397fe02ff80e5a5d72d627c11eb885d1ca78e71b1a5c1e8c7d45de/wrapt-2.1.2-cp314-cp314-win_arm64.whl", hash = "sha256:da1f00a557c66225d53b095a97eace0fc5349e3bfda28fa34ffae238978ee575", size = 59164, upload-time = "2026-03-06T02:53:59.128Z" }, - { url = "https://files.pythonhosted.org/packages/71/ed/5d89c798741993b2371396eb9d4634f009ff1ad8a6c78d366fe2883ea7a6/wrapt-2.1.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:62503ffbc2d3a69891cf29beeaccdb4d5e0a126e2b6a851688d4777e01428dbb", size = 63163, upload-time = "2026-03-06T02:52:54.873Z" }, - { url = "https://files.pythonhosted.org/packages/c6/8c/05d277d182bf36b0a13d6bd393ed1dec3468a25b59d01fba2dd70fe4d6ae/wrapt-2.1.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c7e6cd120ef837d5b6f860a6ea3745f8763805c418bb2f12eeb1fa6e25f22d22", size = 63723, upload-time = "2026-03-06T02:52:56.374Z" }, - { url = "https://files.pythonhosted.org/packages/f4/27/6c51ec1eff4413c57e72d6106bb8dec6f0c7cdba6503d78f0fa98767bcc9/wrapt-2.1.2-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:3769a77df8e756d65fbc050333f423c01ae012b4f6731aaf70cf2bef61b34596", size = 152652, upload-time = "2026-03-06T02:53:23.79Z" }, - { url = "https://files.pythonhosted.org/packages/db/4c/d7dd662d6963fc7335bfe29d512b02b71cdfa23eeca7ab3ac74a67505deb/wrapt-2.1.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a76d61a2e851996150ba0f80582dd92a870643fa481f3b3846f229de88caf044", size = 158807, upload-time = "2026-03-06T02:53:35.742Z" }, - { url = "https://files.pythonhosted.org/packages/b4/4d/1e5eea1a78d539d346765727422976676615814029522c76b87a95f6bcdd/wrapt-2.1.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6f97edc9842cf215312b75fe737ee7c8adda75a89979f8e11558dfff6343cc4b", size = 146061, upload-time = "2026-03-06T02:52:57.574Z" }, - { url = "https://files.pythonhosted.org/packages/89/bc/62cabea7695cd12a288023251eeefdcb8465056ddaab6227cb78a2de005b/wrapt-2.1.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:4006c351de6d5007aa33a551f600404ba44228a89e833d2fadc5caa5de8edfbf", size = 155667, upload-time = "2026-03-06T02:53:39.422Z" }, - { url = "https://files.pythonhosted.org/packages/e9/99/6f2888cd68588f24df3a76572c69c2de28287acb9e1972bf0c83ce97dbc1/wrapt-2.1.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:a9372fc3639a878c8e7d87e1556fa209091b0a66e912c611e3f833e2c4202be2", size = 144392, upload-time = "2026-03-06T02:54:22.41Z" }, - { url = "https://files.pythonhosted.org/packages/40/51/1dfc783a6c57971614c48e361a82ca3b6da9055879952587bc99fe1a7171/wrapt-2.1.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3144b027ff30cbd2fca07c0a87e67011adb717eb5f5bd8496325c17e454257a3", size = 150296, upload-time = "2026-03-06T02:54:07.848Z" }, - { url = "https://files.pythonhosted.org/packages/6c/38/cbb8b933a0201076c1f64fc42883b0023002bdc14a4964219154e6ff3350/wrapt-2.1.2-cp314-cp314t-win32.whl", hash = "sha256:3b8d15e52e195813efe5db8cec156eebe339aaf84222f4f4f051a6c01f237ed7", size = 60539, upload-time = "2026-03-06T02:54:00.594Z" }, - { url = "https://files.pythonhosted.org/packages/82/dd/e5176e4b241c9f528402cebb238a36785a628179d7d8b71091154b3e4c9e/wrapt-2.1.2-cp314-cp314t-win_amd64.whl", hash = "sha256:08ffa54146a7559f5b8df4b289b46d963a8e74ed16ba3687f99896101a3990c5", size = 63969, upload-time = "2026-03-06T02:54:39Z" }, - { url = "https://files.pythonhosted.org/packages/5c/99/79f17046cf67e4a95b9987ea129632ba8bcec0bc81f3fb3d19bdb0bd60cd/wrapt-2.1.2-cp314-cp314t-win_arm64.whl", hash = "sha256:72aaa9d0d8e4ed0e2e98019cea47a21f823c9dd4b43c7b77bba6679ffcca6a00", size = 60554, upload-time = "2026-03-06T02:53:14.132Z" }, - { url = "https://files.pythonhosted.org/packages/1a/c7/8528ac2dfa2c1e6708f647df7ae144ead13f0a31146f43c7264b4942bf12/wrapt-2.1.2-py3-none-any.whl", hash = "sha256:b8fd6fa2b2c4e7621808f8c62e8317f4aae56e59721ad933bac5239d913cf0e8", size = 43993, upload-time = "2026-03-06T02:53:12.905Z" }, -] - [[package]] name = "wsproto" version = "1.3.2" @@ -2903,53 +1768,3 @@ sdist = { url = "https://files.pythonhosted.org/packages/79/2b/8ae5f59ab852c8fe3 wheels = [ { url = "https://files.pythonhosted.org/packages/0f/b7/4bac35b4079b76c07d8faddf89467e9891b1610cfe8d03b0ebb5610e4423/x_wr_timezone-2.0.1-py3-none-any.whl", hash = "sha256:e74a53b9f4f7def8138455c240e65e47c224778bce3c024fcd6da2cbe91ca038", size = 11102, upload-time = "2025-02-06T17:10:39.192Z" }, ] - -[[package]] -name = "yarl" -version = "1.23.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "idna" }, - { name = "multidict" }, - { name = "propcache" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/23/6e/beb1beec874a72f23815c1434518bfc4ed2175065173fb138c3705f658d4/yarl-1.23.0.tar.gz", hash = "sha256:53b1ea6ca88ebd4420379c330aea57e258408dd0df9af0992e5de2078dc9f5d5", size = 194676, upload-time = "2026-03-01T22:07:53.373Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/90/98/b85a038d65d1b92c3903ab89444f48d3cee490a883477b716d7a24b1a78c/yarl-1.23.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:21d1b7305a71a15b4794b5ff22e8eef96ff4a6d7f9657155e5aa419444b28912", size = 124455, upload-time = "2026-03-01T22:06:43.615Z" }, - { url = "https://files.pythonhosted.org/packages/39/54/bc2b45559f86543d163b6e294417a107bb87557609007c007ad889afec18/yarl-1.23.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:85610b4f27f69984932a7abbe52703688de3724d9f72bceb1cca667deff27474", size = 86752, upload-time = "2026-03-01T22:06:45.425Z" }, - { url = "https://files.pythonhosted.org/packages/24/f9/e8242b68362bffe6fb536c8db5076861466fc780f0f1b479fc4ffbebb128/yarl-1.23.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:23f371bd662cf44a7630d4d113101eafc0cfa7518a2760d20760b26021454719", size = 86291, upload-time = "2026-03-01T22:06:46.974Z" }, - { url = "https://files.pythonhosted.org/packages/ea/d8/d1cb2378c81dd729e98c716582b1ccb08357e8488e4c24714658cc6630e8/yarl-1.23.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4a80f77dc1acaaa61f0934176fccca7096d9b1ff08c8ba9cddf5ae034a24319", size = 99026, upload-time = "2026-03-01T22:06:48.459Z" }, - { url = "https://files.pythonhosted.org/packages/0a/ff/7196790538f31debe3341283b5b0707e7feb947620fc5e8236ef28d44f72/yarl-1.23.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:bd654fad46d8d9e823afbb4f87c79160b5a374ed1ff5bde24e542e6ba8f41434", size = 92355, upload-time = "2026-03-01T22:06:50.306Z" }, - { url = "https://files.pythonhosted.org/packages/c1/56/25d58c3eddde825890a5fe6aa1866228377354a3c39262235234ab5f616b/yarl-1.23.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:682bae25f0a0dd23a056739f23a134db9f52a63e2afd6bfb37ddc76292bbd723", size = 106417, upload-time = "2026-03-01T22:06:52.1Z" }, - { url = "https://files.pythonhosted.org/packages/51/8a/882c0e7bc8277eb895b31bce0138f51a1ba551fc2e1ec6753ffc1e7c1377/yarl-1.23.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a82836cab5f197a0514235aaf7ffccdc886ccdaa2324bc0aafdd4ae898103039", size = 106422, upload-time = "2026-03-01T22:06:54.424Z" }, - { url = "https://files.pythonhosted.org/packages/42/2b/fef67d616931055bf3d6764885990a3ac647d68734a2d6a9e1d13de437a2/yarl-1.23.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c57676bdedc94cd3bc37724cf6f8cd2779f02f6aba48de45feca073e714fe52", size = 101915, upload-time = "2026-03-01T22:06:55.895Z" }, - { url = "https://files.pythonhosted.org/packages/18/6a/530e16aebce27c5937920f3431c628a29a4b6b430fab3fd1c117b26ff3f6/yarl-1.23.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c7f8dc16c498ff06497c015642333219871effba93e4a2e8604a06264aca5c5c", size = 100690, upload-time = "2026-03-01T22:06:58.21Z" }, - { url = "https://files.pythonhosted.org/packages/88/08/93749219179a45e27b036e03260fda05190b911de8e18225c294ac95bbc9/yarl-1.23.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:5ee586fb17ff8f90c91cf73c6108a434b02d69925f44f5f8e0d7f2f260607eae", size = 98750, upload-time = "2026-03-01T22:06:59.794Z" }, - { url = "https://files.pythonhosted.org/packages/d9/cf/ea424a004969f5d81a362110a6ac1496d79efdc6d50c2c4b2e3ea0fc2519/yarl-1.23.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:17235362f580149742739cc3828b80e24029d08cbb9c4bda0242c7b5bc610a8e", size = 94685, upload-time = "2026-03-01T22:07:01.375Z" }, - { url = "https://files.pythonhosted.org/packages/e2/b7/14341481fe568e2b0408bcf1484c652accafe06a0ade9387b5d3fd9df446/yarl-1.23.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:0793e2bd0cf14234983bbb371591e6bea9e876ddf6896cdcc93450996b0b5c85", size = 106009, upload-time = "2026-03-01T22:07:03.151Z" }, - { url = "https://files.pythonhosted.org/packages/0a/e6/5c744a9b54f4e8007ad35bce96fbc9218338e84812d36f3390cea616881a/yarl-1.23.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:3650dc2480f94f7116c364096bc84b1d602f44224ef7d5c7208425915c0475dd", size = 100033, upload-time = "2026-03-01T22:07:04.701Z" }, - { url = "https://files.pythonhosted.org/packages/0c/23/e3bfc188d0b400f025bc49d99793d02c9abe15752138dcc27e4eaf0c4a9e/yarl-1.23.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f40e782d49630ad384db66d4d8b73ff4f1b8955dc12e26b09a3e3af064b3b9d6", size = 106483, upload-time = "2026-03-01T22:07:06.231Z" }, - { url = "https://files.pythonhosted.org/packages/72/42/f0505f949a90b3f8b7a363d6cbdf398f6e6c58946d85c6d3a3bc70595b26/yarl-1.23.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:94f8575fbdf81749008d980c17796097e645574a3b8c28ee313931068dad14fe", size = 102175, upload-time = "2026-03-01T22:07:08.4Z" }, - { url = "https://files.pythonhosted.org/packages/aa/65/b39290f1d892a9dd671d1c722014ca062a9c35d60885d57e5375db0404b5/yarl-1.23.0-cp314-cp314-win32.whl", hash = "sha256:c8aa34a5c864db1087d911a0b902d60d203ea3607d91f615acd3f3108ac32169", size = 83871, upload-time = "2026-03-01T22:07:09.968Z" }, - { url = "https://files.pythonhosted.org/packages/a9/5b/9b92f54c784c26e2a422e55a8d2607ab15b7ea3349e28359282f84f01d43/yarl-1.23.0-cp314-cp314-win_amd64.whl", hash = "sha256:63e92247f383c85ab00dd0091e8c3fa331a96e865459f5ee80353c70a4a42d70", size = 89093, upload-time = "2026-03-01T22:07:11.501Z" }, - { url = "https://files.pythonhosted.org/packages/e0/7d/8a84dc9381fd4412d5e7ff04926f9865f6372b4c2fd91e10092e65d29eb8/yarl-1.23.0-cp314-cp314-win_arm64.whl", hash = "sha256:70efd20be968c76ece7baa8dafe04c5be06abc57f754d6f36f3741f7aa7a208e", size = 83384, upload-time = "2026-03-01T22:07:13.069Z" }, - { url = "https://files.pythonhosted.org/packages/dd/8d/d2fad34b1c08aa161b74394183daa7d800141aaaee207317e82c790b418d/yarl-1.23.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:9a18d6f9359e45722c064c97464ec883eb0e0366d33eda61cb19a244bf222679", size = 131019, upload-time = "2026-03-01T22:07:14.903Z" }, - { url = "https://files.pythonhosted.org/packages/19/ff/33009a39d3ccf4b94d7d7880dfe17fb5816c5a4fe0096d9b56abceea9ac7/yarl-1.23.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:2803ed8b21ca47a43da80a6fd1ed3019d30061f7061daa35ac54f63933409412", size = 89894, upload-time = "2026-03-01T22:07:17.372Z" }, - { url = "https://files.pythonhosted.org/packages/0c/f1/dab7ac5e7306fb79c0190766a3c00b4cb8d09a1f390ded68c85a5934faf5/yarl-1.23.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:394906945aa8b19fc14a61cf69743a868bb8c465efe85eee687109cc540b98f4", size = 89979, upload-time = "2026-03-01T22:07:19.361Z" }, - { url = "https://files.pythonhosted.org/packages/aa/b1/08e95f3caee1fad6e65017b9f26c1d79877b502622d60e517de01e72f95d/yarl-1.23.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:71d006bee8397a4a89f469b8deb22469fe7508132d3c17fa6ed871e79832691c", size = 95943, upload-time = "2026-03-01T22:07:21.266Z" }, - { url = "https://files.pythonhosted.org/packages/c0/cc/6409f9018864a6aa186c61175b977131f373f1988e198e031236916e87e4/yarl-1.23.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:62694e275c93d54f7ccedcfef57d42761b2aad5234b6be1f3e3026cae4001cd4", size = 88786, upload-time = "2026-03-01T22:07:23.129Z" }, - { url = "https://files.pythonhosted.org/packages/76/40/cc22d1d7714b717fde2006fad2ced5efe5580606cb059ae42117542122f3/yarl-1.23.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a31de1613658308efdb21ada98cbc86a97c181aa050ba22a808120bb5be3ab94", size = 101307, upload-time = "2026-03-01T22:07:24.689Z" }, - { url = "https://files.pythonhosted.org/packages/8f/0d/476c38e85ddb4c6ec6b20b815bdd779aa386a013f3d8b85516feee55c8dc/yarl-1.23.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fb1e8b8d66c278b21d13b0a7ca22c41dd757a7c209c6b12c313e445c31dd3b28", size = 100904, upload-time = "2026-03-01T22:07:26.287Z" }, - { url = "https://files.pythonhosted.org/packages/72/32/0abe4a76d59adf2081dcb0397168553ece4616ada1c54d1c49d8936c74f8/yarl-1.23.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50f9d8d531dfb767c565f348f33dd5139a6c43f5cbdf3f67da40d54241df93f6", size = 97728, upload-time = "2026-03-01T22:07:27.906Z" }, - { url = "https://files.pythonhosted.org/packages/b7/35/7b30f4810fba112f60f5a43237545867504e15b1c7647a785fbaf588fac2/yarl-1.23.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:575aa4405a656e61a540f4a80eaa5260f2a38fff7bfdc4b5f611840d76e9e277", size = 95964, upload-time = "2026-03-01T22:07:30.198Z" }, - { url = "https://files.pythonhosted.org/packages/2d/86/ed7a73ab85ef00e8bb70b0cb5421d8a2a625b81a333941a469a6f4022828/yarl-1.23.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:041b1a4cefacf65840b4e295c6985f334ba83c30607441ae3cf206a0eed1a2e4", size = 95882, upload-time = "2026-03-01T22:07:32.132Z" }, - { url = "https://files.pythonhosted.org/packages/19/90/d56967f61a29d8498efb7afb651e0b2b422a1e9b47b0ab5f4e40a19b699b/yarl-1.23.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:d38c1e8231722c4ce40d7593f28d92b5fc72f3e9774fe73d7e800ec32299f63a", size = 90797, upload-time = "2026-03-01T22:07:34.404Z" }, - { url = "https://files.pythonhosted.org/packages/72/00/8b8f76909259f56647adb1011d7ed8b321bcf97e464515c65016a47ecdf0/yarl-1.23.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:d53834e23c015ee83a99377db6e5e37d8484f333edb03bd15b4bc312cc7254fb", size = 101023, upload-time = "2026-03-01T22:07:35.953Z" }, - { url = "https://files.pythonhosted.org/packages/ac/e2/cab11b126fb7d440281b7df8e9ddbe4851e70a4dde47a202b6642586b8d9/yarl-1.23.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:2e27c8841126e017dd2a054a95771569e6070b9ee1b133366d8b31beb5018a41", size = 96227, upload-time = "2026-03-01T22:07:37.594Z" }, - { url = "https://files.pythonhosted.org/packages/c2/9b/2c893e16bfc50e6b2edf76c1a9eb6cb0c744346197e74c65e99ad8d634d0/yarl-1.23.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:76855800ac56f878847a09ce6dba727c93ca2d89c9e9d63002d26b916810b0a2", size = 100302, upload-time = "2026-03-01T22:07:39.334Z" }, - { url = "https://files.pythonhosted.org/packages/28/ec/5498c4e3a6d5f1003beb23405671c2eb9cdbf3067d1c80f15eeafe301010/yarl-1.23.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e09fd068c2e169a7070d83d3bde728a4d48de0549f975290be3c108c02e499b4", size = 98202, upload-time = "2026-03-01T22:07:41.717Z" }, - { url = "https://files.pythonhosted.org/packages/fe/c3/cd737e2d45e70717907f83e146f6949f20cc23cd4bf7b2688727763aa458/yarl-1.23.0-cp314-cp314t-win32.whl", hash = "sha256:73309162a6a571d4cbd3b6a1dcc703c7311843ae0d1578df6f09be4e98df38d4", size = 90558, upload-time = "2026-03-01T22:07:43.433Z" }, - { url = "https://files.pythonhosted.org/packages/e1/19/3774d162f6732d1cfb0b47b4140a942a35ca82bb19b6db1f80e9e7bdc8f8/yarl-1.23.0-cp314-cp314t-win_amd64.whl", hash = "sha256:4503053d296bc6e4cbd1fad61cf3b6e33b939886c4f249ba7c78b602214fabe2", size = 97610, upload-time = "2026-03-01T22:07:45.773Z" }, - { url = "https://files.pythonhosted.org/packages/51/47/3fa2286c3cb162c71cdb34c4224d5745a1ceceb391b2bd9b19b668a8d724/yarl-1.23.0-cp314-cp314t-win_arm64.whl", hash = "sha256:44bb7bef4ea409384e3f8bc36c063d77ea1b8d4a5b2706956c0d6695f07dcc25", size = 86041, upload-time = "2026-03-01T22:07:49.026Z" }, - { url = "https://files.pythonhosted.org/packages/69/68/c8739671f5699c7dc470580a4f821ef37c32c4cb0b047ce223a7f115757f/yarl-1.23.0-py3-none-any.whl", hash = "sha256:a2df6afe50dea8ae15fa34c9f824a3ee958d785fd5d089063d960bae1daa0a3f", size = 48288, upload-time = "2026-03-01T22:07:51.388Z" }, -]