From 7e7104e655411b2f3924a3f05a3b9df63aecd49e Mon Sep 17 00:00:00 2001 From: Robin Ole Heinemann Date: Tue, 14 Jan 2025 16:52:39 +0100 Subject: [PATCH] Bump sqlmodel, sqlalchemy, pydantic, fastapi We no longer depend on sqlalchemy v1.4 this was. These all need to be updated together because of cross dependencies. --- backend/openapi-schema.yml | 190 +++++++++++++----- backend/pyproject.toml | 9 +- backend/tests/test_doc.py | 23 ++- backend/transcribee_backend/auth.py | 2 +- backend/transcribee_backend/config.py | 31 +-- .../migrations/versions/1ec1e251173a_init.py | 36 ++-- .../versions/6392770332cd_add_taskattempt.py | 16 +- .../versions/937846561faf_add_apitoken.py | 2 +- ...f4844_fix_accidentially_nullable_fields.py | 53 +++++ .../d679c226343d_add_documentsharetoken.py | 4 +- backend/transcribee_backend/main.py | 33 ++- backend/transcribee_backend/metrics.py | 36 ++-- .../transcribee_backend/models/document.py | 36 ++-- backend/transcribee_backend/models/task.py | 22 +- backend/transcribee_backend/models/user.py | 17 +- backend/transcribee_backend/models/worker.py | 12 +- .../transcribee_backend/routers/document.py | 32 ++- backend/transcribee_backend/routers/user.py | 2 +- backend/uv.lock | 188 +++++++++++------ frontend/src/editor/worker_status.tsx | 5 +- frontend/src/openapi-schema.ts | 130 ++++++------ nix/pkgs/backend.nix | 6 - packaging/start_backend.sh | 2 + proto/pyproject.toml | 2 +- proto/transcribee_proto/api.py | 11 +- proto/transcribee_proto/document.py | 2 +- worker/pyproject.toml | 3 +- worker/tests/test_transcribe.py | 8 +- worker/transcribee_worker/api_client.py | 3 +- worker/transcribee_worker/config.py | 9 +- worker/transcribee_worker/worker.py | 23 ++- worker/uv.lock | 117 ++++++++--- 32 files changed, 647 insertions(+), 418 deletions(-) create mode 100644 backend/transcribee_backend/db/migrations/versions/c88376bf4844_fix_accidentially_nullable_fields.py diff --git a/backend/openapi-schema.yml b/backend/openapi-schema.yml index 72228bac..8bae5de6 100644 --- a/backend/openapi-schema.yml +++ b/backend/openapi-schema.yml @@ -10,9 +10,8 @@ components: title: Task Parameters type: object task_type: + const: ALIGN default: ALIGN - enum: - - ALIGN title: Task Type type: string required: @@ -29,6 +28,7 @@ components: title: Created At type: string id: + format: uuid title: Id type: string media_files: @@ -56,7 +56,9 @@ components: AssignedTaskResponse: properties: current_attempt: - $ref: '#/components/schemas/TaskAttemptResponse' + anyOf: + - $ref: '#/components/schemas/TaskAttemptResponse' + - type: 'null' dependencies: items: format: uuid @@ -87,6 +89,7 @@ components: - id - state - dependencies + - current_attempt - document title: AssignedTaskResponse type: object @@ -122,8 +125,11 @@ components: title: Name type: string number_of_speakers: + anyOf: + - minimum: 0.0 + type: integer + - type: 'null' title: Number Of Speakers - type: integer required: - name - model @@ -168,9 +174,11 @@ components: title: Name type: string valid_until: - format: date-time + anyOf: + - format: date-time + type: string + - type: 'null' title: Valid Until - type: string required: - name - can_write @@ -217,6 +225,7 @@ components: title: Created At type: string id: + format: uuid title: Id type: string media_files: @@ -274,12 +283,15 @@ components: title: Token type: string valid_until: - format: date-time + anyOf: + - format: date-time + type: string + - type: 'null' title: Valid Until - type: string required: - id - name + - valid_until - document_id - token - can_write @@ -288,8 +300,10 @@ components: DocumentUpdateRequest: properties: name: + anyOf: + - type: string + - type: 'null' title: Name - type: string title: DocumentUpdateRequest type: object DocumentWithAccessInfo: @@ -307,6 +321,7 @@ components: title: Has Full Access type: boolean id: + format: uuid title: Id type: string media_files: @@ -337,7 +352,6 @@ components: title: ExportError type: object ExportFormat: - description: An enumeration. enum: - VTT - SRT @@ -361,9 +375,8 @@ components: task_parameters: $ref: '#/components/schemas/ExportTaskParameters' task_type: + const: EXPORT default: EXPORT - enum: - - EXPORT title: Task Type type: string required: @@ -382,8 +395,10 @@ components: title: Include Word Timing type: boolean max_line_length: + anyOf: + - type: integer + - type: 'null' title: Max Line Length - type: integer required: - format - include_speaker_names @@ -402,8 +417,10 @@ components: KeepaliveBody: properties: progress: + anyOf: + - type: number + - type: 'null' title: Progress - type: number title: KeepaliveBody type: object LoginResponse: @@ -437,8 +454,10 @@ components: PageConfig: properties: footer_position: + anyOf: + - type: integer + - type: 'null' title: Footer Position - type: integer name: title: Name type: string @@ -453,8 +472,10 @@ components: PublicConfig: properties: logged_out_redirect_url: + anyOf: + - type: string + - type: 'null' title: Logged Out Redirect Url - type: string models: additionalProperties: $ref: '#/components/schemas/ModelConfig' @@ -476,8 +497,10 @@ components: ShortPageConfig: properties: footer_position: + anyOf: + - type: integer + - type: 'null' title: Footer Position - type: integer name: title: Name type: string @@ -495,9 +518,8 @@ components: title: Task Parameters type: object task_type: + const: IDENTIFY_SPEAKERS default: IDENTIFY_SPEAKERS - enum: - - IDENTIFY_SPEAKERS title: Task Type type: string required: @@ -508,8 +530,12 @@ components: TaskAttemptResponse: properties: progress: + anyOf: + - type: number + - type: 'null' title: Progress - type: number + required: + - progress title: TaskAttemptResponse type: object TaskQueueInfoResponse: @@ -530,8 +556,10 @@ components: title: Id type: string remaining_cost: + anyOf: + - type: number + - type: 'null' title: Remaining Cost - type: number state: $ref: '#/components/schemas/TaskState' task_type: @@ -546,7 +574,9 @@ components: TaskResponse: properties: current_attempt: - $ref: '#/components/schemas/TaskAttemptResponse' + anyOf: + - $ref: '#/components/schemas/TaskAttemptResponse' + - type: 'null' dependencies: items: format: uuid @@ -575,18 +605,18 @@ components: - id - state - dependencies + - current_attempt title: TaskResponse type: object TaskState: - description: An enumeration. enum: - NEW - ASSIGNED - COMPLETED - FAILED title: TaskState + type: string TaskType: - description: An enumeration. enum: - IDENTIFY_SPEAKERS - TRANSCRIBE @@ -604,9 +634,8 @@ components: task_parameters: $ref: '#/components/schemas/TranscribeTaskParameters' task_type: + const: TRANSCRIBE default: TRANSCRIBE - enum: - - TRANSCRIBE title: Task Type type: string required: @@ -678,17 +707,21 @@ components: Worker: properties: deactivated_at: - format: date-time + anyOf: + - format: date-time + type: string + - type: 'null' title: Deactivated At - type: string id: format: uuid title: Id type: string last_seen: - format: date-time + anyOf: + - format: date-time + type: string + - type: 'null' title: Last Seen - type: string name: title: Name type: string @@ -697,28 +730,36 @@ components: type: string required: - name + - last_seen + - deactivated_at - token title: Worker type: object WorkerWithId: properties: deactivated_at: - format: date-time + anyOf: + - format: date-time + type: string + - type: 'null' title: Deactivated At - type: string id: format: uuid title: Id type: string last_seen: - format: date-time + anyOf: + - format: date-time + type: string + - type: 'null' title: Last Seen - type: string name: title: Name type: string required: - name + - last_seen + - deactivated_at title: WorkerWithId type: object securitySchemes: @@ -728,7 +769,7 @@ components: info: title: FastAPI version: 0.1.0 -openapi: 3.0.2 +openapi: 3.1.0 paths: /: get: @@ -852,14 +893,18 @@ paths: name: authorization required: false schema: + anyOf: + - type: string + - type: 'null' title: Authorization - type: string - in: header name: Share-Token required: false schema: + anyOf: + - type: string + - type: 'null' title: Share-Token - type: string responses: '200': content: @@ -887,14 +932,18 @@ paths: name: authorization required: false schema: + anyOf: + - type: string + - type: 'null' title: Authorization - type: string - in: header name: Share-Token required: false schema: + anyOf: + - type: string + - type: 'null' title: Share-Token - type: string responses: '200': content: @@ -923,14 +972,18 @@ paths: name: authorization required: false schema: + anyOf: + - type: string + - type: 'null' title: Authorization - type: string - in: header name: Share-Token required: false schema: + anyOf: + - type: string + - type: 'null' title: Share-Token - type: string requestBody: content: application/json: @@ -972,14 +1025,18 @@ paths: name: authorization required: false schema: + anyOf: + - type: string + - type: 'null' title: Authorization - type: string - in: header name: Share-Token required: false schema: + anyOf: + - type: string + - type: 'null' title: Share-Token - type: string requestBody: content: application/json: @@ -1071,20 +1128,26 @@ paths: name: max_line_length required: false schema: + anyOf: + - type: integer + - type: 'null' title: Max Line Length - type: integer - in: header name: authorization required: false schema: + anyOf: + - type: string + - type: 'null' title: Authorization - type: string - in: header name: Share-Token required: false schema: + anyOf: + - type: string + - type: 'null' title: Share-Token - type: string responses: '200': content: @@ -1151,14 +1214,18 @@ paths: name: authorization required: false schema: + anyOf: + - type: string + - type: 'null' title: Authorization - type: string - in: header name: Share-Token required: false schema: + anyOf: + - type: string + - type: 'null' title: Share-Token - type: string responses: '200': content: @@ -1197,8 +1264,10 @@ paths: name: Share-Token required: false schema: + anyOf: + - type: string + - type: 'null' title: Share-Token - type: string requestBody: content: application/json: @@ -1241,14 +1310,18 @@ paths: name: authorization required: false schema: + anyOf: + - type: string + - type: 'null' title: Authorization - type: string - in: header name: Share-Token required: false schema: + anyOf: + - type: string + - type: 'null' title: Share-Token - type: string responses: '200': content: @@ -1277,14 +1350,18 @@ paths: name: authorization required: false schema: + anyOf: + - type: string + - type: 'null' title: Authorization - type: string - in: header name: Share-Token required: false schema: + anyOf: + - type: string + - type: 'null' title: Share-Token - type: string responses: '200': content: @@ -1414,6 +1491,7 @@ paths: schema: items: $ref: '#/components/schemas/TaskType' + title: Task Type type: array - in: header name: authorization @@ -1426,7 +1504,11 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/AssignedTaskResponse' + anyOf: + - $ref: '#/components/schemas/AssignedTaskResponse' + - type: 'null' + title: Response Claim Unassigned Task Api V1 Tasks Claim Unassigned + Task Post description: Successful Response '422': content: diff --git a/backend/pyproject.toml b/backend/pyproject.toml index eb8e00cf..c16fa83a 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -11,9 +11,9 @@ authors = [ dependencies = [ "redis~=5.0.1", - "fastapi~=0.92.0", + "fastapi~=0.115", "uvicorn[standard]~=0.20.0", - "sqlmodel~=0.0.11", + "sqlmodel~=0.0.22", "alembic~=1.11.1", "python-multipart~=0.0.6", "filetype~=1.2.0", @@ -23,6 +23,8 @@ dependencies = [ "python-frontmatter~=1.0.0", "psycopg2~=2.9.9", "prometheus-fastapi-instrumentator~=6.1.0", + "pydantic~=2.2", + "pydantic-settings>=2.7.1", ] requires-python = ">=3.11" readme = "./README.md" @@ -45,9 +47,6 @@ notebooks = [ ] [tool.uv] -override-dependencies = [ - "sqlalchemy==1.4.41" -] config-settings = { editable_mode = "compat" } [tool.uv.sources] diff --git a/backend/tests/test_doc.py b/backend/tests/test_doc.py index 18b5e517..c4463717 100644 --- a/backend/tests/test_doc.py +++ b/backend/tests/test_doc.py @@ -1,6 +1,8 @@ +import uuid + import pytest from fastapi.testclient import TestClient -from sqlmodel import Session +from sqlmodel import Session, col, func, select from transcribee_backend.auth import generate_share_token from transcribee_backend.config import settings from transcribee_backend.models import ( @@ -23,7 +25,7 @@ def document_id(memory_session: Session, logged_in_client: TestClient): data={"name": "test document", "model": "tiny", "language": "auto"}, ) assert req.status_code == 200 - document_id = req.json()["id"] + document_id = uuid.UUID(req.json()["id"]) memory_session.add(DocumentUpdate(document_id=document_id, change_bytes=b"")) memory_session.commit() @@ -50,7 +52,7 @@ def test_doc_delete( ] counts = {} for table in checked_tables: - counts[table] = memory_session.query(table).count() + counts[table] = memory_session.exec(select(func.count(col(table.id)))).one() files = set(str(x) for x in settings.storage_path.glob("*")) @@ -60,14 +62,15 @@ def test_doc_delete( data={"name": "test document", "model": "tiny", "language": "auto"}, ) assert req.status_code == 200 - document_id = req.json()["id"] + document_id = uuid.UUID(req.json()["id"]) req = logged_in_client.get(f"/api/v1/documents/{document_id}/tasks/") + task_id = uuid.UUID(req.json()[0]["id"]) assert req.status_code == 200 assert len(req.json()) >= 1 memory_session.add(DocumentUpdate(document_id=document_id, change_bytes=b"")) - memory_session.add(TaskAttempt(task_id=req.json()[0]["id"], attempt_number=1)) + memory_session.add(TaskAttempt(task_id=task_id, attempt_number=1)) memory_session.add( generate_share_token( document_id=document_id, name="Test Token", valid_until=None, can_write=True @@ -76,7 +79,9 @@ def test_doc_delete( memory_session.commit() for table in checked_tables: - assert counts[table] < memory_session.query(table).count() + assert ( + counts[table] < memory_session.exec(select(func.count(col(table.id)))).one() + ) assert files < set(str(x) for x in settings.storage_path.glob("*")) @@ -87,7 +92,10 @@ def test_doc_delete( assert req.status_code == 200 for table in checked_tables: - assert counts[table] == memory_session.query(table).count() + assert ( + counts[table] + == memory_session.exec(select(func.count(col(table.id)))).one() + ) assert files == set(str(x) for x in settings.storage_path.glob("*")) @@ -144,6 +152,7 @@ def test_doc_share( f"/api/v1/documents/{document_id}/share_tokens/", json={"name": "test token", "can_write": can_write}, ) + print("hello", req) assert req.status_code == 200 assert "token" in req.json() token = req.json()["token"] diff --git a/backend/transcribee_backend/auth.py b/backend/transcribee_backend/auth.py index 0177405b..0887e3c0 100644 --- a/backend/transcribee_backend/auth.py +++ b/backend/transcribee_backend/auth.py @@ -72,7 +72,7 @@ def validate_user_authorization(session: Session, authorization: str): raise HTTPException(status_code=400, detail="Invalid Token") user_id, provided_token = token_data.split(":", maxsplit=1) statement = select(UserToken).where( - UserToken.user_id == user_id, UserToken.valid_until >= now_tz_aware() + UserToken.user_id == uuid.UUID(user_id), UserToken.valid_until >= now_tz_aware() ) results = session.exec(statement) for token in results: diff --git a/backend/transcribee_backend/config.py b/backend/transcribee_backend/config.py index 7bc482b8..79ca058f 100644 --- a/backend/transcribee_backend/config.py +++ b/backend/transcribee_backend/config.py @@ -2,29 +2,30 @@ from typing import Dict, List, Optional import frontmatter -from pydantic import BaseModel, BaseSettings, parse_file_as, parse_obj_as +from pydantic import BaseModel, TypeAdapter +from pydantic_settings import BaseSettings pages = None class Settings(BaseSettings): storage_path: Path = Path("storage/") - secret_key = "insecure-secret-key" - worker_timeout = 60 # in seconds - media_signature_max_age = 3600 # in seconds - task_attempt_limit = 5 + secret_key: str = "insecure-secret-key" + worker_timeout: int = 60 # in seconds + media_signature_max_age: int = 3600 # in seconds + task_attempt_limit: int = 5 - media_url_base = "http://localhost:8000/" + media_url_base: str = "http://localhost:8000/" logged_out_redirect_url: None | str = None model_config_path: Path = Path("data/models.json") pages_dir: Path = Path("data/pages/") - metrics_username = "transcribee" - metrics_password = "transcribee" + metrics_username: str = "transcribee" + metrics_password: str = "transcribee" - redis_host = "localhost" - redis_port = 6379 + redis_host: str = "localhost" + redis_port: int = 6379 class ModelConfig(BaseModel): @@ -35,12 +36,12 @@ class ModelConfig(BaseModel): class PublicConfig(BaseModel): models: Dict[str, ModelConfig] - logged_out_redirect_url: str | None + logged_out_redirect_url: str | None = None class ShortPageConfig(BaseModel): name: str - footer_position: Optional[int] + footer_position: Optional[int] = None class PageConfig(ShortPageConfig): @@ -48,7 +49,9 @@ class PageConfig(ShortPageConfig): def get_model_config(): - return parse_file_as(Dict[str, ModelConfig], settings.model_config_path) + return TypeAdapter(Dict[str, ModelConfig]).validate_json( + Path(settings.model_config_path).read_text() + ) def load_pages_from_disk() -> Dict[str, PageConfig]: @@ -73,7 +76,7 @@ def get_page_config(): def get_short_page_config() -> Dict[str, ShortPageConfig]: - return parse_obj_as(Dict[str, ShortPageConfig], get_page_config()) + return TypeAdapter(Dict[str, ShortPageConfig]).validate_python(get_page_config()) def get_public_config(): diff --git a/backend/transcribee_backend/db/migrations/versions/1ec1e251173a_init.py b/backend/transcribee_backend/db/migrations/versions/1ec1e251173a_init.py index d5b36d17..4c2d2c42 100644 --- a/backend/transcribee_backend/db/migrations/versions/1ec1e251173a_init.py +++ b/backend/transcribee_backend/db/migrations/versions/1ec1e251173a_init.py @@ -22,7 +22,7 @@ def upgrade() -> None: op.create_table( "user", sa.Column("username", sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column("id", sqlmodel.sql.sqltypes.GUID(), nullable=False), + sa.Column("id", sa.Uuid, nullable=False), sa.Column("password_hash", sa.LargeBinary(), nullable=False), sa.Column("password_salt", sa.LargeBinary(), nullable=False), sa.PrimaryKeyConstraint("id"), @@ -35,7 +35,7 @@ def upgrade() -> None: "worker", sa.Column("last_seen", sa.DateTime(timezone=True), nullable=True), sa.Column("name", sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column("id", sqlmodel.sql.sqltypes.GUID(), nullable=False), + sa.Column("id", sa.Uuid, nullable=False), sa.Column("token", sqlmodel.sql.sqltypes.AutoString(), nullable=False), sa.PrimaryKeyConstraint("id"), ) @@ -47,8 +47,8 @@ def upgrade() -> None: sa.Column("created_at", sa.DateTime(timezone=True), nullable=False), sa.Column("changed_at", sa.DateTime(timezone=True), nullable=False), sa.Column("name", sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column("id", sqlmodel.sql.sqltypes.GUID(), nullable=False), - sa.Column("user_id", sqlmodel.sql.sqltypes.GUID(), nullable=False), + sa.Column("id", sa.Uuid, nullable=False), + sa.Column("user_id", sa.Uuid, nullable=False), sa.ForeignKeyConstraint( ["user_id"], ["user.id"], @@ -61,8 +61,8 @@ def upgrade() -> None: op.create_table( "usertoken", sa.Column("valid_until", sa.DateTime(timezone=True), nullable=False), - sa.Column("id", sqlmodel.sql.sqltypes.GUID(), nullable=False), - sa.Column("user_id", sqlmodel.sql.sqltypes.GUID(), nullable=False), + sa.Column("id", sa.Uuid, nullable=False), + sa.Column("user_id", sa.Uuid, nullable=False), sa.Column("token_hash", sa.LargeBinary(), nullable=False), sa.Column("token_salt", sa.LargeBinary(), nullable=False), sa.ForeignKeyConstraint( @@ -78,8 +78,8 @@ def upgrade() -> None: "documentmediafile", sa.Column("created_at", sa.DateTime(timezone=True), nullable=False), sa.Column("changed_at", sa.DateTime(timezone=True), nullable=False), - sa.Column("id", sqlmodel.sql.sqltypes.GUID(), nullable=False), - sa.Column("document_id", sqlmodel.sql.sqltypes.GUID(), nullable=False), + sa.Column("id", sa.Uuid, nullable=False), + sa.Column("document_id", sa.Uuid, nullable=False), sa.Column("file", sqlmodel.sql.sqltypes.AutoString(), nullable=False), sa.Column("content_type", sqlmodel.sql.sqltypes.AutoString(), nullable=False), sa.ForeignKeyConstraint( @@ -96,8 +96,8 @@ def upgrade() -> None: op.create_table( "documentupdate", sa.Column("change_bytes", sa.LargeBinary(), nullable=False), - sa.Column("id", sqlmodel.sql.sqltypes.GUID(), nullable=False), - sa.Column("document_id", sqlmodel.sql.sqltypes.GUID(), nullable=False), + sa.Column("id", sa.Uuid, nullable=False), + sa.Column("document_id", sa.Uuid, nullable=False), sa.ForeignKeyConstraint( ["document_id"], ["document.id"], @@ -111,10 +111,10 @@ def upgrade() -> None: "task", sa.Column("task_parameters", sa.JSON(), nullable=False), sa.Column("task_type", sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column("document_id", sqlmodel.sql.sqltypes.GUID(), nullable=False), - sa.Column("id", sqlmodel.sql.sqltypes.GUID(), nullable=False), + sa.Column("document_id", sa.Uuid, nullable=False), + sa.Column("id", sa.Uuid, nullable=False), sa.Column("progress", sa.Float(), nullable=True), - sa.Column("assigned_worker_id", sqlmodel.sql.sqltypes.GUID(), nullable=True), + sa.Column("assigned_worker_id", sa.Uuid, nullable=True), sa.Column("assigned_at", sa.DateTime(), nullable=True), sa.Column("last_keepalive", sa.DateTime(), nullable=True), sa.Column("is_completed", sa.Boolean(), nullable=False), @@ -134,9 +134,9 @@ def upgrade() -> None: op.create_table( "documentmediatag", - sa.Column("id", sqlmodel.sql.sqltypes.GUID(), nullable=False), + sa.Column("id", sa.Uuid, nullable=False), sa.Column("tag", sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column("media_file_id", sqlmodel.sql.sqltypes.GUID(), nullable=False), + sa.Column("media_file_id", sa.Uuid, nullable=False), sa.ForeignKeyConstraint( ["media_file_id"], ["documentmediafile.id"], @@ -150,9 +150,9 @@ def upgrade() -> None: op.create_table( "taskdependency", - sa.Column("id", sqlmodel.sql.sqltypes.GUID(), nullable=False), - sa.Column("dependent_task_id", sqlmodel.sql.sqltypes.GUID(), nullable=False), - sa.Column("dependant_on_id", sqlmodel.sql.sqltypes.GUID(), nullable=False), + sa.Column("id", sa.Uuid, nullable=False), + sa.Column("dependent_task_id", sa.Uuid, nullable=False), + sa.Column("dependant_on_id", sa.Uuid, nullable=False), sa.ForeignKeyConstraint( ["dependant_on_id"], ["task.id"], diff --git a/backend/transcribee_backend/db/migrations/versions/6392770332cd_add_taskattempt.py b/backend/transcribee_backend/db/migrations/versions/6392770332cd_add_taskattempt.py index 8b481798..4d1431c5 100644 --- a/backend/transcribee_backend/db/migrations/versions/6392770332cd_add_taskattempt.py +++ b/backend/transcribee_backend/db/migrations/versions/6392770332cd_add_taskattempt.py @@ -32,9 +32,9 @@ def upgrade_with_autocommit() -> None: TaskAttempt = op.create_table( "taskattempt", sa.Column("extra_data", sa.JSON(), nullable=True), - sa.Column("id", sqlmodel.sql.sqltypes.GUID(), nullable=False), - sa.Column("task_id", sqlmodel.sql.sqltypes.GUID(), nullable=False), - sa.Column("assigned_worker_id", sqlmodel.sql.sqltypes.GUID(), nullable=True), + sa.Column("id", sa.Uuid, nullable=False), + sa.Column("task_id", sa.Uuid, nullable=False), + sa.Column("assigned_worker_id", sa.Uuid, nullable=True), sa.Column("attempt_number", sa.Integer(), nullable=False), sa.Column("started_at", sa.DateTime(), nullable=True), sa.Column("last_keepalive", sa.DateTime(), nullable=True), @@ -51,9 +51,7 @@ def upgrade_with_autocommit() -> None: batch_op.create_index(batch_op.f("ix_taskattempt_id"), ["id"], unique=False) with op.batch_alter_table("task", schema=None) as batch_op: - batch_op.add_column( - sa.Column("current_attempt_id", sqlmodel.sql.sqltypes.GUID(), nullable=True) - ) + batch_op.add_column(sa.Column("current_attempt_id", sa.Uuid, nullable=True)) batch_op.add_column( sa.Column( "attempt_counter", sa.Integer(), nullable=True, server_default="0" @@ -90,20 +88,20 @@ def upgrade_with_autocommit() -> None: Task = sa.table( "task", - sa.column("id", sqlmodel.sql.sqltypes.GUID()), + sa.column("id", sa.Uuid), sa.column("assigned_at", sa.DateTime()), sa.column("last_keepalive", sa.DateTime()), sa.column("completed_at", sa.DateTime()), sa.column("is_completed", sa.Boolean()), sa.column("completion_data", sa.JSON()), - sa.column("assigned_worker_id", sqlmodel.sql.sqltypes.GUID()), + sa.column("assigned_worker_id", sa.Uuid), sa.column("state_changed_at", sa.DateTime()), sa.column( "state", sa.Enum("NEW", "ASSIGNED", "COMPLETED", "FAILED", name="taskstate"), ), sa.column("remaining_attempts", sa.Integer()), - sa.column("current_attempt_id", sqlmodel.sql.sqltypes.GUID()), + sa.column("current_attempt_id", sa.Uuid), sa.column("progress", sa.Float()), sa.column("attempt_counter", sa.Integer()), ) diff --git a/backend/transcribee_backend/db/migrations/versions/937846561faf_add_apitoken.py b/backend/transcribee_backend/db/migrations/versions/937846561faf_add_apitoken.py index 8428a865..1a711c46 100644 --- a/backend/transcribee_backend/db/migrations/versions/937846561faf_add_apitoken.py +++ b/backend/transcribee_backend/db/migrations/versions/937846561faf_add_apitoken.py @@ -20,7 +20,7 @@ def upgrade() -> None: # ### commands auto generated by Alembic - please adjust! ### op.create_table( "apitoken", - sa.Column("id", sqlmodel.sql.sqltypes.GUID(), nullable=False), + sa.Column("id", sa.Uuid, nullable=False), sa.Column("name", sqlmodel.sql.sqltypes.AutoString(), nullable=False), sa.Column("token", sqlmodel.sql.sqltypes.AutoString(), nullable=False), sa.PrimaryKeyConstraint("id"), diff --git a/backend/transcribee_backend/db/migrations/versions/c88376bf4844_fix_accidentially_nullable_fields.py b/backend/transcribee_backend/db/migrations/versions/c88376bf4844_fix_accidentially_nullable_fields.py new file mode 100644 index 00000000..0ef0cf64 --- /dev/null +++ b/backend/transcribee_backend/db/migrations/versions/c88376bf4844_fix_accidentially_nullable_fields.py @@ -0,0 +1,53 @@ +"""fix accidentially nullable fields + +Revision ID: c88376bf4844 +Revises: 417eece003cb +Create Date: 2025-01-14 16:23:54.386629 + +""" +from alembic import op +import sqlalchemy as sa +import sqlmodel + + +# revision identifiers, used by Alembic. +revision = 'c88376bf4844' +down_revision = '417eece003cb' +branch_labels = None +depends_on = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('task', schema=None) as batch_op: + batch_op.alter_column('document_id', + existing_type=sa.UUID(), + nullable=False) + + with op.batch_alter_table('taskdependency', schema=None) as batch_op: + batch_op.alter_column('dependent_task_id', + existing_type=sa.UUID(), + nullable=False) + batch_op.alter_column('dependant_on_id', + existing_type=sa.UUID(), + nullable=False) + + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('taskdependency', schema=None) as batch_op: + batch_op.alter_column('dependant_on_id', + existing_type=sa.UUID(), + nullable=True) + batch_op.alter_column('dependent_task_id', + existing_type=sa.UUID(), + nullable=True) + + with op.batch_alter_table('task', schema=None) as batch_op: + batch_op.alter_column('document_id', + existing_type=sa.UUID(), + nullable=True) + + # ### end Alembic commands ### diff --git a/backend/transcribee_backend/db/migrations/versions/d679c226343d_add_documentsharetoken.py b/backend/transcribee_backend/db/migrations/versions/d679c226343d_add_documentsharetoken.py index 08089c43..c2bb8586 100644 --- a/backend/transcribee_backend/db/migrations/versions/d679c226343d_add_documentsharetoken.py +++ b/backend/transcribee_backend/db/migrations/versions/d679c226343d_add_documentsharetoken.py @@ -22,9 +22,9 @@ def upgrade() -> None: op.create_table( "documentsharetoken", sa.Column("valid_until", sa.DateTime(timezone=True), nullable=True), - sa.Column("id", sqlmodel.sql.sqltypes.GUID(), nullable=False), + sa.Column("id", sa.Uuid, nullable=False), sa.Column("name", sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column("document_id", sqlmodel.sql.sqltypes.GUID(), nullable=False), + sa.Column("document_id", sa.Uuid, nullable=False), sa.Column("token", sqlmodel.sql.sqltypes.AutoString(), nullable=False), sa.Column("can_write", sa.Boolean(), nullable=False), sa.ForeignKeyConstraint( diff --git a/backend/transcribee_backend/main.py b/backend/transcribee_backend/main.py index 33467c3e..422a878c 100644 --- a/backend/transcribee_backend/main.py +++ b/backend/transcribee_backend/main.py @@ -1,4 +1,5 @@ import asyncio +from contextlib import asynccontextmanager from fastapi import Depends, FastAPI from fastapi.middleware.cors import CORSMiddleware @@ -17,7 +18,28 @@ from .media_storage import serve_media -app = FastAPI() + +async def setup_periodic_tasks(): + return [ + asyncio.create_task( + run_periodic(timeout_attempts, seconds=min(30, settings.worker_timeout)) + ), + asyncio.create_task( + run_periodic(remove_expired_tokens, seconds=60 * 60) + ), # 1 hour + asyncio.create_task(run_periodic(refresh_metrics, seconds=1)), + ] + + +@asynccontextmanager +async def lifespan(_: FastAPI): + tasks = await setup_periodic_tasks() + yield + for task in tasks: + task.cancel() + + +app = FastAPI(lifespan=lifespan) Instrumentator().instrument(app).expose(app, dependencies=[Depends(metrics_auth)]) init_metrics() @@ -46,12 +68,3 @@ async def root(): app.get("/media/{file}")(serve_media) - - -@app.on_event("startup") -async def setup_periodic_tasks(): - asyncio.create_task( - run_periodic(timeout_attempts, seconds=min(30, settings.worker_timeout)) - ) - asyncio.create_task(run_periodic(remove_expired_tokens, seconds=60 * 60)) # 1 hour - asyncio.create_task(run_periodic(refresh_metrics, seconds=1)) diff --git a/backend/transcribee_backend/metrics.py b/backend/transcribee_backend/metrics.py index 83213057..9bbcd93a 100644 --- a/backend/transcribee_backend/metrics.py +++ b/backend/transcribee_backend/metrics.py @@ -6,7 +6,7 @@ from fastapi import Depends, HTTPException, status from fastapi.security import HTTPBasic, HTTPBasicCredentials from prometheus_client import Gauge -from sqlmodel import Session, col, func +from sqlmodel import Session, col, func, select from transcribee_proto.api import TaskType from transcribee_backend.config import settings @@ -31,11 +31,11 @@ def __init__(self): ) def refresh(self, session: Session): - result = ( - session.query(Task.state, Task.task_type, func.count()) - .group_by(Task.state, Task.task_type) - .all() - ) + result = session.exec( + select(Task.state, Task.task_type, func.count()).group_by( + Task.state, Task.task_type + ) + ).all() counts = {(x, y): 0 for x in TaskState for y in TaskType} for state, task_type, count in result: counts[(state, task_type)] = count @@ -50,22 +50,18 @@ def __init__(self): self.collector = Gauge("transcribee_workers", "Workers", ["group"]) def refresh(self, session: Session): - (result,) = ( - session.query(func.count(Worker.id)) - .where(col(Worker.deactivated_at).is_(None)) - .one() - ) + result = session.exec( + select(func.count(Worker.id)).where(col(Worker.deactivated_at).is_(None)) + ).one() self.collector.labels(group="all").set(result) now = now_tz_aware() worker_timeout_ago = now - datetime.timedelta(seconds=settings.worker_timeout) - (result,) = ( - session.query(func.count(Worker.id)) - .where( + result = session.exec( + select(func.count(Worker.id)).where( col(Worker.last_seen) >= worker_timeout_ago, ) - .one() - ) + ).one() self.collector.labels(group="alive").set(result) @@ -74,7 +70,7 @@ def __init__(self): self.collector = Gauge("transcribee_users", "Registered users") def refresh(self, session: Session): - (result,) = session.query(func.count(User.id)).one() + result = session.exec(select(func.count(User.id))).one() self.collector.set(result) @@ -83,7 +79,7 @@ def __init__(self): self.collector = Gauge("transcribe_documents", "Documents") def refresh(self, session: Session): - (result,) = session.query(func.count(Document.id)).one() + result = session.exec(select(func.count(Document.id))).one() self.collector.set(result) @@ -94,8 +90,8 @@ def __init__(self): ) def refresh(self, session: Session): - result = ( - session.query( + result = session.exec( + select( Task.task_type, func.coalesce( func.sum( diff --git a/backend/transcribee_backend/models/document.py b/backend/transcribee_backend/models/document.py index b9b813d5..06f1a4c8 100644 --- a/backend/transcribee_backend/models/document.py +++ b/backend/transcribee_backend/models/document.py @@ -1,8 +1,8 @@ -import datetime import uuid from typing import List, Optional -from sqlmodel import Column, DateTime, Field, Relationship, SQLModel +from pydantic.types import AwareDatetime +from sqlmodel import DateTime, Field, Relationship, SQLModel from transcribee_proto.api import Document as ApiDocument from transcribee_proto.api import DocumentMedia as ApiDocumentMedia @@ -27,20 +27,16 @@ class Document(DocumentBase, table=True): ) user_id: uuid.UUID = Field(foreign_key="user.id") user: "User" = Relationship() - created_at: datetime.datetime = Field( - sa_column=Column(DateTime(timezone=True), nullable=False) - ) - changed_at: datetime.datetime = Field( - sa_column=Column(DateTime(timezone=True), nullable=False) - ) + created_at: AwareDatetime = Field(sa_type=DateTime(timezone=True)) + changed_at: AwareDatetime = Field(sa_type=DateTime(timezone=True)) media_files: List["DocumentMediaFile"] = Relationship() updates: List["DocumentUpdate"] = Relationship( - sa_relationship_kwargs={"cascade": "all,delete"} + sa_relationship_kwargs={"cascade": "all"} ) share_tokens: List["DocumentShareToken"] = Relationship( - sa_relationship_kwargs={"cascade": "all,delete"} + sa_relationship_kwargs={"cascade": "all"} ) - tasks: List["Task"] = Relationship(sa_relationship_kwargs={"cascade": "all,delete"}) + tasks: List["Task"] = Relationship(sa_relationship_kwargs={"cascade": "all"}) def as_api_document(self) -> ApiDocumentWithTasks: tasks = [TaskResponse.from_orm(task) for task in self.tasks] @@ -67,12 +63,8 @@ class DocumentMediaFile(DocumentMediaFileBase, table=True): index=True, nullable=False, ) - created_at: datetime.datetime = Field( - sa_column=Column(DateTime(timezone=True), nullable=False) - ) - changed_at: datetime.datetime = Field( - sa_column=Column(DateTime(timezone=True), nullable=False) - ) + created_at: AwareDatetime = Field(sa_type=DateTime(timezone=True)) + changed_at: AwareDatetime = Field(sa_type=DateTime(timezone=True)) document: Document = Relationship(back_populates="media_files") document_id: uuid.UUID = Field(foreign_key="document.id") @@ -80,7 +72,7 @@ class DocumentMediaFile(DocumentMediaFileBase, table=True): file: str content_type: str tags: List["DocumentMediaTag"] = Relationship( - sa_relationship_kwargs={"cascade": "all,delete"} + sa_relationship_kwargs={"cascade": "all"} ) def as_api_media_file(self) -> ApiDocumentMedia: @@ -121,9 +113,7 @@ class DocumentUpdate(DocumentUpdateBase, table=True): class DocumentShareTokenBase(SQLModel): id: uuid.UUID name: str - valid_until: Optional[datetime.datetime] = Field( - sa_column=Column(DateTime(timezone=True), nullable=True) - ) + valid_until: Optional[AwareDatetime] = Field(sa_type=DateTime(timezone=True)) document_id: uuid.UUID = Field(foreign_key="document.id") token: str can_write: bool @@ -144,5 +134,5 @@ class DocumentShareToken(DocumentShareTokenBase, table=True): from .task import Task, TaskResponse # noqa: E402 from .user import User # noqa: E402 -ApiDocumentWithTasks.update_forward_refs() -Document.update_forward_refs() +ApiDocumentWithTasks.model_rebuild() +Document.model_rebuild() diff --git a/backend/transcribee_backend/models/task.py b/backend/transcribee_backend/models/task.py index e0363828..cff43445 100644 --- a/backend/transcribee_backend/models/task.py +++ b/backend/transcribee_backend/models/task.py @@ -4,8 +4,7 @@ from dataclasses import dataclass from typing import Any, Dict, List, Literal, Optional -from sqlmodel import JSON, Column, Field, ForeignKey, Relationship, SQLModel, col -from sqlmodel.sql.sqltypes import GUID +from sqlmodel import JSON, UUID, Column, Field, ForeignKey, Relationship, SQLModel from transcribee_proto.api import Document as ApiDocument from transcribee_proto.api import ExportTaskParameters, TaskType from typing_extensions import Self @@ -41,10 +40,10 @@ class TaskDependency(SQLModel, table=True): ) dependent_task_id: uuid.UUID = Field( - sa_column=Column(GUID, ForeignKey("task.id", ondelete="CASCADE"), unique=False), + foreign_key="task.id", ondelete="CASCADE", unique=False ) dependant_on_id: uuid.UUID = Field( - sa_column=Column(GUID, ForeignKey("task.id", ondelete="CASCADE"), unique=False), + foreign_key="task.id", ondelete="CASCADE", unique=False ) @@ -56,9 +55,7 @@ class Task(TaskBase, table=True): nullable=False, ) document_id: uuid.UUID = Field( - sa_column=Column( - GUID, ForeignKey("document.id", ondelete="CASCADE"), unique=False - ), + foreign_key="document.id", ondelete="CASCADE", unique=False ) document: Document = Relationship(back_populates="tasks") @@ -98,7 +95,7 @@ def initial_cost(self) -> float | None: current_attempt_id: Optional[uuid.UUID] = Field( sa_column=Column( - GUID, ForeignKey("taskattempt.id", ondelete="SET NULL", use_alter=True) + UUID, ForeignKey("taskattempt.id", ondelete="SET NULL", use_alter=True) ), default=None, ) @@ -145,12 +142,7 @@ class TaskAttempt(SQLModel, table=True): ) task_id: uuid.UUID = Field( - sa_column=Column( - GUID, - ForeignKey(col(Task.id), ondelete="CASCADE"), - nullable=False, - unique=False, - ), + foreign_key="task.id", ondelete="CASCADE", nullable=False, unique=False ) task: Task = Relationship( back_populates="attempts", @@ -207,7 +199,7 @@ def from_orm(cls, task: Task, update={}) -> Self: state=task.state, dependencies=[x.dependant_on_id for x in task.dependency_links], current_attempt=( - TaskAttemptResponse.from_orm(task.current_attempt) + TaskAttemptResponse.model_validate(task.current_attempt) if task.current_attempt is not None else None ), diff --git a/backend/transcribee_backend/models/user.py b/backend/transcribee_backend/models/user.py index 2060cee3..2f279f5a 100644 --- a/backend/transcribee_backend/models/user.py +++ b/backend/transcribee_backend/models/user.py @@ -1,8 +1,9 @@ -import datetime import uuid +from typing import Annotated -from pydantic import BaseModel, ConstrainedStr -from sqlmodel import Column, DateTime, Field, Relationship, SQLModel +from pydantic import BaseModel, StringConstraints +from pydantic.types import AwareDatetime +from sqlmodel import DateTime, Field, Relationship, SQLModel class UserBase(SQLModel): @@ -39,15 +40,9 @@ class UserToken(UserTokenBase, table=True): user: User = Relationship() token_hash: bytes token_salt: bytes - valid_until: datetime.datetime = Field( - sa_column=Column(DateTime(timezone=True), nullable=False) - ) - - -class PasswordConstrainedStr(ConstrainedStr): - min_length = 6 + valid_until: AwareDatetime = Field(sa_type=DateTime(timezone=True)) class ChangePasswordRequest(BaseModel): old_password: str - new_password: PasswordConstrainedStr + new_password: Annotated[str, StringConstraints(min_length=6)] diff --git a/backend/transcribee_backend/models/worker.py b/backend/transcribee_backend/models/worker.py index 1dce1eb3..064783c9 100644 --- a/backend/transcribee_backend/models/worker.py +++ b/backend/transcribee_backend/models/worker.py @@ -1,19 +1,15 @@ -import datetime import uuid from typing import Optional -from sqlmodel import Column, DateTime, Field, SQLModel +from pydantic.types import AwareDatetime +from sqlmodel import DateTime, Field, SQLModel class WorkerBase(SQLModel): name: str - last_seen: Optional[datetime.datetime] = Field( - sa_column=Column(DateTime(timezone=True), nullable=True) - ) - deactivated_at: Optional[datetime.datetime] = Field( - sa_column=Column(DateTime(timezone=True), nullable=True) - ) + last_seen: Optional[AwareDatetime] = Field(sa_type=DateTime(timezone=True)) + deactivated_at: Optional[AwareDatetime] = Field(sa_type=DateTime(timezone=True)) class WorkerWithId(WorkerBase): diff --git a/backend/transcribee_backend/routers/document.py b/backend/transcribee_backend/routers/document.py index 0abf65d6..65fa380c 100644 --- a/backend/transcribee_backend/routers/document.py +++ b/backend/transcribee_backend/routers/document.py @@ -1,6 +1,5 @@ import datetime import enum -import json import pathlib import uuid from dataclasses import dataclass @@ -23,8 +22,7 @@ ) from fastapi.exceptions import RequestValidationError from fastapi.responses import PlainTextResponse -from pydantic import BaseModel, parse_obj_as -from pydantic.error_wrappers import ErrorWrapper +from pydantic import BaseModel, TypeAdapter from sqlalchemy.orm import selectinload from sqlalchemy.sql.expression import desc from sqlmodel import Session, col, select @@ -311,18 +309,14 @@ async def create_document( if model not in model_configs: raise RequestValidationError( - [ErrorWrapper(ValueError(f"Unknown model '{model}'"), ("body", "model"))] + [ValueError(f"Unknown model '{model}'"), ("body", "model")] ) if language not in model_configs[model].languages: raise RequestValidationError( [ - ErrorWrapper( - ValueError( - f"Model '{model}' does not support language '{language}'" - ), - ("body", "language"), - ) + ValueError(f"Model '{model}' does not support language '{language}'"), + ("body", "language"), ] ) @@ -412,10 +406,8 @@ def list_documents( .where(Document.user == token.user) .order_by(desc(Document.changed_at), Document.id) .options( - selectinload("tasks"), - selectinload("media_files"), - selectinload("media_files.tags"), - selectinload("tasks.dependency_links"), + selectinload(Document.tasks).selectinload(Task.dependency_links), + selectinload(Document.media_files).selectinload(DocumentMediaFile.tags), ) ) results = session.exec(statement) @@ -553,7 +545,7 @@ def update_document( class CreateShareToken(BaseModel): name: str - valid_until: Optional[datetime.datetime] + valid_until: Optional[datetime.datetime] = None can_write: bool @@ -572,6 +564,7 @@ def share( ) session.add(db_token) session.commit() + session.refresh(db_token) return db_token @@ -628,15 +621,14 @@ async def export( ): export_task = Task( task_type=TaskType.EXPORT, - task_parameters=export_parameters.dict(), + task_parameters=export_parameters.model_dump(), document_id=auth.document.id, ) session.add(export_task) session.commit() - result = parse_obj_as( - ExportRes, - json.loads(await redis_task_channel.wait_for_result(str(export_task.id))), + result = TypeAdapter(ExportRes).validate_json( + await redis_task_channel.wait_for_result(str(export_task.id)) ) if isinstance(result, ExportError): raise Exception(result.error) @@ -651,4 +643,4 @@ async def add_export_result( auth: AuthInfo = Depends(get_doc_worker_auth), redis_task_channel: RedisTaskChannel = Depends(get_redis_task_channel), ) -> None: - await redis_task_channel.put_result(task_id, result.json()) + await redis_task_channel.put_result(task_id, result.model_dump_json()) diff --git a/backend/transcribee_backend/routers/user.py b/backend/transcribee_backend/routers/user.py index a2518ada..e336bef6 100644 --- a/backend/transcribee_backend/routers/user.py +++ b/backend/transcribee_backend/routers/user.py @@ -84,6 +84,6 @@ def change_password( username=authorized_user.username, new_password=body.new_password, ) - session.execute(delete(UserToken).where(UserToken.user_id == authorized_user.id)) + session.exec(delete(UserToken).where(UserToken.user_id == authorized_user.id)) session.commit() return UserBase(username=user.username) diff --git a/backend/uv.lock b/backend/uv.lock index 2fc7ae1c..988a168b 100644 --- a/backend/uv.lock +++ b/backend/uv.lock @@ -1,9 +1,6 @@ version = 1 requires-python = ">=3.11" -[manifest] -overrides = [{ name = "sqlalchemy", specifier = "==1.4.41" }] - [[package]] name = "alembic" version = "1.11.3" @@ -18,6 +15,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ab/7d/b572fc6a51bc430b1fa0ef59591db32b14105093324d472eed8ea296d2df/alembic-1.11.3-py3-none-any.whl", hash = "sha256:d6c96c2482740592777c400550a523bc7a9aada4e210cae2e733354ddae6f6f8", size = 225449 }, ] +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, +] + [[package]] name = "anyio" version = "4.8.0" @@ -406,15 +412,16 @@ wheels = [ [[package]] name = "fastapi" -version = "0.92.0" +version = "0.115.6" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic" }, { name = "starlette" }, + { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/11/92/6577adcb71d85378700765b5298929ac0fcafaa47a7c39bdc205289d9c74/fastapi-0.92.0.tar.gz", hash = "sha256:023a0f5bd2c8b2609014d3bba1e14a1d7df96c6abea0a73070621c9862b9a4de", size = 9741200 } +sdist = { url = "https://files.pythonhosted.org/packages/93/72/d83b98cd106541e8f5e5bfab8ef2974ab45a62e8a6c5b5e6940f26d2ed4b/fastapi-0.115.6.tar.gz", hash = "sha256:9ec46f7addc14ea472958a96aae5b5de65f39721a46aaf5705c480d9a8b76654", size = 301336 } wheels = [ - { url = "https://files.pythonhosted.org/packages/cd/ec/878318da126d6c83641d23644899ca94838b839490bcdbcddad6814fe07a/fastapi-0.92.0-py3-none-any.whl", hash = "sha256:ae7b97c778e2f2ec3fb3cb4fb14162129411d99907fb71920f6d69a524340ebf", size = 56238 }, + { url = "https://files.pythonhosted.org/packages/52/b3/7e4df40e585df024fac2f80d1a2d579c854ac37109675db2b0cc22c0bb9e/fastapi-0.115.6-py3-none-any.whl", hash = "sha256:e9240b29e36fa8f4bb7290316988e90c381e5092e0cbe84e7818cc3713bcf305", size = 94843 }, ] [[package]] @@ -1487,35 +1494,82 @@ wheels = [ [[package]] name = "pydantic" -version = "1.10.20" +version = "2.10.5" source = { registry = "https://pypi.org/simple" } dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c3/eb/081e5c6bad10570613df9b769bc8553bab771554cbd71371115e38d5e303/pydantic-1.10.20.tar.gz", hash = "sha256:8dbb8fb6f0ee469c197047e5e69f51677f0bf6297619c98c814a22965a5486d4", size = 356518 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6c/0e/b5c0d7e7950ff6816ba9e2cabcb8655f34137bedf961780fe2254a23b6db/pydantic-1.10.20-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bed9ec215fa46fb4024777d4098816e2779b5c0192c3646aa1766c7527031c7d", size = 2845160 }, - { url = "https://files.pythonhosted.org/packages/7a/f6/e8bba6e8dfa7d17a04463361f8d4442417f15e2fad5da5d219ba9b376d85/pydantic-1.10.20-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e6fbf8fdc5991560709cb24cebab2e3812b7c8e3eac922a8bf51a531046fb494", size = 2552963 }, - { url = "https://files.pythonhosted.org/packages/8c/13/113f132d8638dda224b06a37cad967c84c2e612d0c07480550a55bbda619/pydantic-1.10.20-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f6d485ef36dcd597f7d6226bf8cb222b3b5a1a9191261b7ec5677b08e9230bc", size = 3140364 }, - { url = "https://files.pythonhosted.org/packages/95/51/8c1642cb9c80f84fa05c650395caf19c1fceba335a9960914cda26d33f84/pydantic-1.10.20-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:664110d074bfc14432326d59ec51da0c260050935fe7cdb3579d24b02766dd93", size = 3213620 }, - { url = "https://files.pythonhosted.org/packages/2a/0a/93d1e3825d8a2e687f1025b03251108df86171831bff4b2ff0db58b83eef/pydantic-1.10.20-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d6efdd3653713c1e24a46ec966e296faf28933da319692e2adbe5bb6d980418", size = 3320922 }, - { url = "https://files.pythonhosted.org/packages/ec/ab/2193ce40469409f94e23a6eb859809f91a0513c57ada65037af2d74639e4/pydantic-1.10.20-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:604904991eb38e68674892bfea9e9fce6cea62b4d9b9593a2575080842512edb", size = 3243736 }, - { url = "https://files.pythonhosted.org/packages/94/20/811fd66e7381a8d3005d5fab4b426634ed17505487be242d1f1890f5b13a/pydantic-1.10.20-cp311-cp311-win_amd64.whl", hash = "sha256:875f6b2f99b621c1fcdb3ba947e4a89120c5199ffe873b5c0f81b0668a2b1647", size = 2307225 }, - { url = "https://files.pythonhosted.org/packages/5c/17/0c010d2288843e103c42a04891923b2dd6c548ed3f654f8f6c963feb0c64/pydantic-1.10.20-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ef6feab9add46f970cbe5676da7681700ddade1d9aeeb206d61c3e6e47d39e8f", size = 2794434 }, - { url = "https://files.pythonhosted.org/packages/1c/82/62c1c6e45b7b536a30d6255a56e34c276162417701708d7eb1da17003090/pydantic-1.10.20-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:31e699a67b53c9a07780caacf662d4bc6eed15cb70b3bc30256cbef000ad6874", size = 2533956 }, - { url = "https://files.pythonhosted.org/packages/ff/1c/a50964e655dadfa4a6260bf01ddc6b84554a68024beedda95b6232b0d4e8/pydantic-1.10.20-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:657f0977327dd41b3e25c2adec81cf58a7e62b868876bef80b9deec38d264428", size = 2993534 }, - { url = "https://files.pythonhosted.org/packages/b8/a5/1d0b04786fac9920dcb69a1023777c6176a6fbd9b940920c7432366dec2f/pydantic-1.10.20-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd4726eb30445555fa2e95795af1603af6023d216e9d9f45d940e0f07a1eda7", size = 3036293 }, - { url = "https://files.pythonhosted.org/packages/be/1e/8a56c63d4c4d02ab33585fc3a75af9679540360716e7596058613d81a3b3/pydantic-1.10.20-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:64793e059a9e254a1dd63e70bb946e550ac6ba465e13b9aec40408bc10b038c0", size = 3171156 }, - { url = "https://files.pythonhosted.org/packages/58/fc/6cb5a4b8e924f461b8f8ac7c19ef6b0ab84b46d243977992b4b99a0dcd23/pydantic-1.10.20-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:88f552e84c27b321310c0bdefe90b5279f5c4255867b4fcbbbdfe8bb86831750", size = 3123165 }, - { url = "https://files.pythonhosted.org/packages/53/ea/8bb04a3b3566a3cb76d615bd67d2c4263a9b779b81100f9259e1f3d7e580/pydantic-1.10.20-cp312-cp312-win_amd64.whl", hash = "sha256:0a2351eb63b8c7016d30b1d8fa390f4666488a0ed4071972afb63696810ecc8f", size = 2189333 }, - { url = "https://files.pythonhosted.org/packages/43/93/51030ea841583508154c8626dfde32a8556fd5b1e60dbc292f49d0e7554c/pydantic-1.10.20-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d9041e27b36597a54fbe59c4f8e205748d72d455dfc29e98b5d72d97f7d0c036", size = 2796668 }, - { url = "https://files.pythonhosted.org/packages/73/78/948d20580c07a99ce2468860a42e135e599b171c0f13f6adac78323919ad/pydantic-1.10.20-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:13262acfdc60b8af048ee27483ebbcbdf5ea3217c8b8e4eede94373c8d65f6a5", size = 2538168 }, - { url = "https://files.pythonhosted.org/packages/0c/23/d49b2249eeca644e37f1f60a09c72973070e98e929e993ccdcc4ab15de4c/pydantic-1.10.20-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1a58b0b07a8bce4c8fb368435571a8bcf0457332c503055b1bbff5e4e65c13e", size = 2985709 }, - { url = "https://files.pythonhosted.org/packages/54/9f/83e888afb1133f54076b8af749b5e247330ad45c36b9457831d6fc8d0b7b/pydantic-1.10.20-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b378c2fd51dc0c475a01c46748a5bcf7d41d23e4620d7622a373d7dab6dc2cf", size = 3015169 }, - { url = "https://files.pythonhosted.org/packages/ad/4b/07b9cc10fc0ad1defc6531dc598922929807f12620f4ea515bd47a10e6d7/pydantic-1.10.20-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:61bcdd86348dd5e268f0f306d8d9c00c0e10f158af53538ce270447e67af88b0", size = 3163895 }, - { url = "https://files.pythonhosted.org/packages/cb/92/fde1bd6ba297eeaac12b1454c8c294dd0cdba0e8de71ff2269b3678d08c3/pydantic-1.10.20-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7fb3d37f29cfa83b00eef518187509044626162511da5367ada03ba29647630f", size = 3117174 }, - { url = "https://files.pythonhosted.org/packages/46/81/81764ecb32de8725e5b77fca9c83d871ad1c29664e907eaf77f4344240ea/pydantic-1.10.20-cp313-cp313-win_amd64.whl", hash = "sha256:64aae7ea5c88db33fabeb3017f8d25f2ebff0244a3db3819c38bdcef42e917d6", size = 2172937 }, - { url = "https://files.pythonhosted.org/packages/e8/08/ce63c53094a258983667f1ab22f0fb841310f16a07dba6a550e8006bb2e8/pydantic-1.10.20-py3-none-any.whl", hash = "sha256:cd9c1a6a60d54281defb35292d3f2b70bce1b62fe404fd991688fa146715936a", size = 166366 }, +sdist = { url = "https://files.pythonhosted.org/packages/6a/c7/ca334c2ef6f2e046b1144fe4bb2a5da8a4c574e7f2ebf7e16b34a6a2fa92/pydantic-2.10.5.tar.gz", hash = "sha256:278b38dbbaec562011d659ee05f63346951b3a248a6f3642e1bc68894ea2b4ff", size = 761287 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/58/26/82663c79010b28eddf29dcdd0ea723439535fa917fce5905885c0e9ba562/pydantic-2.10.5-py3-none-any.whl", hash = "sha256:4dd4e322dbe55472cb7ca7e73f4b63574eecccf2835ffa2af9021ce113c83c53", size = 431426 }, +] + +[[package]] +name = "pydantic-core" +version = "2.27.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fc/01/f3e5ac5e7c25833db5eb555f7b7ab24cd6f8c322d3a3ad2d67a952dc0abc/pydantic_core-2.27.2.tar.gz", hash = "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39", size = 413443 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/89/f3450af9d09d44eea1f2c369f49e8f181d742f28220f88cc4dfaae91ea6e/pydantic_core-2.27.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:8e10c99ef58cfdf2a66fc15d66b16c4a04f62bca39db589ae8cba08bc55331bc", size = 1893421 }, + { url = "https://files.pythonhosted.org/packages/9e/e3/71fe85af2021f3f386da42d291412e5baf6ce7716bd7101ea49c810eda90/pydantic_core-2.27.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:26f32e0adf166a84d0cb63be85c562ca8a6fa8de28e5f0d92250c6b7e9e2aff7", size = 1814998 }, + { url = "https://files.pythonhosted.org/packages/a6/3c/724039e0d848fd69dbf5806894e26479577316c6f0f112bacaf67aa889ac/pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c19d1ea0673cd13cc2f872f6c9ab42acc4e4f492a7ca9d3795ce2b112dd7e15", size = 1826167 }, + { url = "https://files.pythonhosted.org/packages/2b/5b/1b29e8c1fb5f3199a9a57c1452004ff39f494bbe9bdbe9a81e18172e40d3/pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e68c4446fe0810e959cdff46ab0a41ce2f2c86d227d96dc3847af0ba7def306", size = 1865071 }, + { url = "https://files.pythonhosted.org/packages/89/6c/3985203863d76bb7d7266e36970d7e3b6385148c18a68cc8915fd8c84d57/pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9640b0059ff4f14d1f37321b94061c6db164fbe49b334b31643e0528d100d99", size = 2036244 }, + { url = "https://files.pythonhosted.org/packages/0e/41/f15316858a246b5d723f7d7f599f79e37493b2e84bfc789e58d88c209f8a/pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40d02e7d45c9f8af700f3452f329ead92da4c5f4317ca9b896de7ce7199ea459", size = 2737470 }, + { url = "https://files.pythonhosted.org/packages/a8/7c/b860618c25678bbd6d1d99dbdfdf0510ccb50790099b963ff78a124b754f/pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c1fd185014191700554795c99b347d64f2bb637966c4cfc16998a0ca700d048", size = 1992291 }, + { url = "https://files.pythonhosted.org/packages/bf/73/42c3742a391eccbeab39f15213ecda3104ae8682ba3c0c28069fbcb8c10d/pydantic_core-2.27.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d81d2068e1c1228a565af076598f9e7451712700b673de8f502f0334f281387d", size = 1994613 }, + { url = "https://files.pythonhosted.org/packages/94/7a/941e89096d1175d56f59340f3a8ebaf20762fef222c298ea96d36a6328c5/pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1a4207639fb02ec2dbb76227d7c751a20b1a6b4bc52850568e52260cae64ca3b", size = 2002355 }, + { url = "https://files.pythonhosted.org/packages/6e/95/2359937a73d49e336a5a19848713555605d4d8d6940c3ec6c6c0ca4dcf25/pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:3de3ce3c9ddc8bbd88f6e0e304dea0e66d843ec9de1b0042b0911c1663ffd474", size = 2126661 }, + { url = "https://files.pythonhosted.org/packages/2b/4c/ca02b7bdb6012a1adef21a50625b14f43ed4d11f1fc237f9d7490aa5078c/pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:30c5f68ded0c36466acede341551106821043e9afaad516adfb6e8fa80a4e6a6", size = 2153261 }, + { url = "https://files.pythonhosted.org/packages/72/9d/a241db83f973049a1092a079272ffe2e3e82e98561ef6214ab53fe53b1c7/pydantic_core-2.27.2-cp311-cp311-win32.whl", hash = "sha256:c70c26d2c99f78b125a3459f8afe1aed4d9687c24fd677c6a4436bc042e50d6c", size = 1812361 }, + { url = "https://files.pythonhosted.org/packages/e8/ef/013f07248041b74abd48a385e2110aa3a9bbfef0fbd97d4e6d07d2f5b89a/pydantic_core-2.27.2-cp311-cp311-win_amd64.whl", hash = "sha256:08e125dbdc505fa69ca7d9c499639ab6407cfa909214d500897d02afb816e7cc", size = 1982484 }, + { url = "https://files.pythonhosted.org/packages/10/1c/16b3a3e3398fd29dca77cea0a1d998d6bde3902fa2706985191e2313cc76/pydantic_core-2.27.2-cp311-cp311-win_arm64.whl", hash = "sha256:26f0d68d4b235a2bae0c3fc585c585b4ecc51382db0e3ba402a22cbc440915e4", size = 1867102 }, + { url = "https://files.pythonhosted.org/packages/d6/74/51c8a5482ca447871c93e142d9d4a92ead74de6c8dc5e66733e22c9bba89/pydantic_core-2.27.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9e0c8cfefa0ef83b4da9588448b6d8d2a2bf1a53c3f1ae5fca39eb3061e2f0b0", size = 1893127 }, + { url = "https://files.pythonhosted.org/packages/d3/f3/c97e80721735868313c58b89d2de85fa80fe8dfeeed84dc51598b92a135e/pydantic_core-2.27.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83097677b8e3bd7eaa6775720ec8e0405f1575015a463285a92bfdfe254529ef", size = 1811340 }, + { url = "https://files.pythonhosted.org/packages/9e/91/840ec1375e686dbae1bd80a9e46c26a1e0083e1186abc610efa3d9a36180/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:172fce187655fece0c90d90a678424b013f8fbb0ca8b036ac266749c09438cb7", size = 1822900 }, + { url = "https://files.pythonhosted.org/packages/f6/31/4240bc96025035500c18adc149aa6ffdf1a0062a4b525c932065ceb4d868/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:519f29f5213271eeeeb3093f662ba2fd512b91c5f188f3bb7b27bc5973816934", size = 1869177 }, + { url = "https://files.pythonhosted.org/packages/fa/20/02fbaadb7808be578317015c462655c317a77a7c8f0ef274bc016a784c54/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05e3a55d124407fffba0dd6b0c0cd056d10e983ceb4e5dbd10dda135c31071d6", size = 2038046 }, + { url = "https://files.pythonhosted.org/packages/06/86/7f306b904e6c9eccf0668248b3f272090e49c275bc488a7b88b0823444a4/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c3ed807c7b91de05e63930188f19e921d1fe90de6b4f5cd43ee7fcc3525cb8c", size = 2685386 }, + { url = "https://files.pythonhosted.org/packages/8d/f0/49129b27c43396581a635d8710dae54a791b17dfc50c70164866bbf865e3/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fb4aadc0b9a0c063206846d603b92030eb6f03069151a625667f982887153e2", size = 1997060 }, + { url = "https://files.pythonhosted.org/packages/0d/0f/943b4af7cd416c477fd40b187036c4f89b416a33d3cc0ab7b82708a667aa/pydantic_core-2.27.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28ccb213807e037460326424ceb8b5245acb88f32f3d2777427476e1b32c48c4", size = 2004870 }, + { url = "https://files.pythonhosted.org/packages/35/40/aea70b5b1a63911c53a4c8117c0a828d6790483f858041f47bab0b779f44/pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:de3cd1899e2c279b140adde9357c4495ed9d47131b4a4eaff9052f23398076b3", size = 1999822 }, + { url = "https://files.pythonhosted.org/packages/f2/b3/807b94fd337d58effc5498fd1a7a4d9d59af4133e83e32ae39a96fddec9d/pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:220f892729375e2d736b97d0e51466252ad84c51857d4d15f5e9692f9ef12be4", size = 2130364 }, + { url = "https://files.pythonhosted.org/packages/fc/df/791c827cd4ee6efd59248dca9369fb35e80a9484462c33c6649a8d02b565/pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a0fcd29cd6b4e74fe8ddd2c90330fd8edf2e30cb52acda47f06dd615ae72da57", size = 2158303 }, + { url = "https://files.pythonhosted.org/packages/9b/67/4e197c300976af185b7cef4c02203e175fb127e414125916bf1128b639a9/pydantic_core-2.27.2-cp312-cp312-win32.whl", hash = "sha256:1e2cb691ed9834cd6a8be61228471d0a503731abfb42f82458ff27be7b2186fc", size = 1834064 }, + { url = "https://files.pythonhosted.org/packages/1f/ea/cd7209a889163b8dcca139fe32b9687dd05249161a3edda62860430457a5/pydantic_core-2.27.2-cp312-cp312-win_amd64.whl", hash = "sha256:cc3f1a99a4f4f9dd1de4fe0312c114e740b5ddead65bb4102884b384c15d8bc9", size = 1989046 }, + { url = "https://files.pythonhosted.org/packages/bc/49/c54baab2f4658c26ac633d798dab66b4c3a9bbf47cff5284e9c182f4137a/pydantic_core-2.27.2-cp312-cp312-win_arm64.whl", hash = "sha256:3911ac9284cd8a1792d3cb26a2da18f3ca26c6908cc434a18f730dc0db7bfa3b", size = 1885092 }, + { url = "https://files.pythonhosted.org/packages/41/b1/9bc383f48f8002f99104e3acff6cba1231b29ef76cfa45d1506a5cad1f84/pydantic_core-2.27.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b", size = 1892709 }, + { url = "https://files.pythonhosted.org/packages/10/6c/e62b8657b834f3eb2961b49ec8e301eb99946245e70bf42c8817350cbefc/pydantic_core-2.27.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154", size = 1811273 }, + { url = "https://files.pythonhosted.org/packages/ba/15/52cfe49c8c986e081b863b102d6b859d9defc63446b642ccbbb3742bf371/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9", size = 1823027 }, + { url = "https://files.pythonhosted.org/packages/b1/1c/b6f402cfc18ec0024120602bdbcebc7bdd5b856528c013bd4d13865ca473/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9", size = 1868888 }, + { url = "https://files.pythonhosted.org/packages/bd/7b/8cb75b66ac37bc2975a3b7de99f3c6f355fcc4d89820b61dffa8f1e81677/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1", size = 2037738 }, + { url = "https://files.pythonhosted.org/packages/c8/f1/786d8fe78970a06f61df22cba58e365ce304bf9b9f46cc71c8c424e0c334/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a", size = 2685138 }, + { url = "https://files.pythonhosted.org/packages/a6/74/d12b2cd841d8724dc8ffb13fc5cef86566a53ed358103150209ecd5d1999/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e", size = 1997025 }, + { url = "https://files.pythonhosted.org/packages/a0/6e/940bcd631bc4d9a06c9539b51f070b66e8f370ed0933f392db6ff350d873/pydantic_core-2.27.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4", size = 2004633 }, + { url = "https://files.pythonhosted.org/packages/50/cc/a46b34f1708d82498c227d5d80ce615b2dd502ddcfd8376fc14a36655af1/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27", size = 1999404 }, + { url = "https://files.pythonhosted.org/packages/ca/2d/c365cfa930ed23bc58c41463bae347d1005537dc8db79e998af8ba28d35e/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee", size = 2130130 }, + { url = "https://files.pythonhosted.org/packages/f4/d7/eb64d015c350b7cdb371145b54d96c919d4db516817f31cd1c650cae3b21/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1", size = 2157946 }, + { url = "https://files.pythonhosted.org/packages/a4/99/bddde3ddde76c03b65dfd5a66ab436c4e58ffc42927d4ff1198ffbf96f5f/pydantic_core-2.27.2-cp313-cp313-win32.whl", hash = "sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130", size = 1834387 }, + { url = "https://files.pythonhosted.org/packages/71/47/82b5e846e01b26ac6f1893d3c5f9f3a2eb6ba79be26eef0b759b4fe72946/pydantic_core-2.27.2-cp313-cp313-win_amd64.whl", hash = "sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee", size = 1990453 }, + { url = "https://files.pythonhosted.org/packages/51/b2/b2b50d5ecf21acf870190ae5d093602d95f66c9c31f9d5de6062eb329ad1/pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b", size = 1885186 }, +] + +[[package]] +name = "pydantic-settings" +version = "2.7.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, + { name = "python-dotenv" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/73/7b/c58a586cd7d9ac66d2ee4ba60ca2d241fa837c02bca9bea80a9a8c3d22a9/pydantic_settings-2.7.1.tar.gz", hash = "sha256:10c9caad35e64bfb3c2fbf70a078c0e25cc92499782e5200747f942a065dec93", size = 79920 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b4/46/93416fdae86d40879714f72956ac14df9c7b76f7d41a4d68aa9f71a0028b/pydantic_settings-2.7.1-py3-none-any.whl", hash = "sha256:590be9e6e24d06db33a4262829edef682500ef008565a969c73d39d5f8bfb3fd", size = 29718 }, ] [[package]] @@ -1968,44 +2022,52 @@ wheels = [ [[package]] name = "sqlalchemy" -version = "1.4.41" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "greenlet", marker = "platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/67/a0/97da2cb07e013fd6c37fd896a86b374aa726e4161cafd57185e8418d59aa/SQLAlchemy-1.4.41.tar.gz", hash = "sha256:0292f70d1797e3c54e862e6f30ae474014648bc9c723e14a2fda730adb0a9791", size = 8281227 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fe/28/f22792eee334cd83a15ef34b825761ee057d330b9b24d3f1496b95faa557/SQLAlchemy-1.4.41-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:90484a2b00baedad361402c257895b13faa3f01780f18f4a104a2f5c413e4536", size = 1548661 }, - { url = "https://files.pythonhosted.org/packages/ea/4e/4bcd7e756fa2e989e7eed239bca3c3fc57101b7d0c49864f8e41d202d1ce/SQLAlchemy-1.4.41-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b67fc780cfe2b306180e56daaa411dd3186bf979d50a6a7c2a5b5036575cbdbb", size = 1604615 }, - { url = "https://files.pythonhosted.org/packages/bf/f2/69c9f96515b4eb65fac522c8b81ec10666ee4789484b0c123452c1f22505/SQLAlchemy-1.4.41-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ad2b727fc41c7f8757098903f85fafb4bf587ca6605f82d9bf5604bd9c7cded", size = 1602718 }, - { url = "https://files.pythonhosted.org/packages/93/0c/377daa276fa54ad65a6dbd0323285cf0892972fa88a4dbe17113ec440c32/SQLAlchemy-1.4.41-cp311-cp311-win32.whl", hash = "sha256:59bdc291165b6119fc6cdbc287c36f7f2859e6051dd923bdf47b4c55fd2f8bd0", size = 1565751 }, - { url = "https://files.pythonhosted.org/packages/37/b5/136c78031fb88f3f79fa1090c339f36a7b9bbb359651767b617f2bbf655a/SQLAlchemy-1.4.41-cp311-cp311-win_amd64.whl", hash = "sha256:d2e054aed4645f9b755db85bc69fc4ed2c9020c19c8027976f66576b906a74f1", size = 1567852 }, -] - -[[package]] -name = "sqlalchemy2-stubs" -version = "0.0.2a38" +version = "2.0.37" source = { registry = "https://pypi.org/simple" } dependencies = [ + { name = "greenlet", marker = "(python_full_version < '3.14' and platform_machine == 'AMD64') or (python_full_version < '3.14' and platform_machine == 'WIN32') or (python_full_version < '3.14' and platform_machine == 'aarch64') or (python_full_version < '3.14' and platform_machine == 'amd64') or (python_full_version < '3.14' and platform_machine == 'ppc64le') or (python_full_version < '3.14' and platform_machine == 'win32') or (python_full_version < '3.14' and platform_machine == 'x86_64')" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/94/df/be9e0062f649fd2e9b9b73a98c250f520fa10beaebddb8948dffad23c3b6/sqlalchemy2-stubs-0.0.2a38.tar.gz", hash = "sha256:861d722abeb12f13eacd775a9f09379b11a5a9076f469ccd4099961b95800f9e", size = 123950 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ee/ea/6eff437d0a26c894da2982429ed2c148ecaf0526349ddc8cda1e7151c171/sqlalchemy2_stubs-0.0.2a38-py3-none-any.whl", hash = "sha256:b62aa46943807287550e2033dafe07564b33b6a815fbaa3c144e396f9cc53bcb", size = 191786 }, +sdist = { url = "https://files.pythonhosted.org/packages/3b/20/93ea2518df4d7a14ebe9ace9ab8bb92aaf7df0072b9007644de74172b06c/sqlalchemy-2.0.37.tar.gz", hash = "sha256:12b28d99a9c14eaf4055810df1001557176716de0167b91026e648e65229bffb", size = 9626249 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/37/4915290c1849337be6d24012227fb3c30c575151eec2b182ee5f45e96ce7/SQLAlchemy-2.0.37-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:78361be6dc9073ed17ab380985d1e45e48a642313ab68ab6afa2457354ff692c", size = 2104098 }, + { url = "https://files.pythonhosted.org/packages/4c/f5/8cce9196434014a24cc65f6c68faa9a887080932361ee285986c0a35892d/SQLAlchemy-2.0.37-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b661b49d0cb0ab311a189b31e25576b7ac3e20783beb1e1817d72d9d02508bf5", size = 2094492 }, + { url = "https://files.pythonhosted.org/packages/9c/54/2df4b3d0d11b384b6e9a8788d0f1123243f2d2356e2ccf626f93dcc1a09f/SQLAlchemy-2.0.37-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d57bafbab289e147d064ffbd5cca2d7b1394b63417c0636cea1f2e93d16eb9e8", size = 3212789 }, + { url = "https://files.pythonhosted.org/packages/57/4f/e1db9475f940f1c54c365ed02d4f6390f884fc95a6a4022ece7725956664/SQLAlchemy-2.0.37-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fa2c0913f02341d25fb858e4fb2031e6b0813494cca1ba07d417674128ce11b", size = 3212784 }, + { url = "https://files.pythonhosted.org/packages/89/57/d93212e827d1f03a6cd4d0ea13775957c2a95161330fa47449b91153bd09/SQLAlchemy-2.0.37-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9df21b8d9e5c136ea6cde1c50d2b1c29a2b5ff2b1d610165c23ff250e0704087", size = 3149616 }, + { url = "https://files.pythonhosted.org/packages/5f/c2/759347419f69cf0bbb76d330fbdbd24cefb15842095fe86bca623759b9e8/SQLAlchemy-2.0.37-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:db18ff6b8c0f1917f8b20f8eca35c28bbccb9f83afa94743e03d40203ed83de9", size = 3169944 }, + { url = "https://files.pythonhosted.org/packages/22/04/a19ecb53aa19bb8cf491ecdb6bf8c1ac74959cd4962e119e91d4e2b8ecaa/SQLAlchemy-2.0.37-cp311-cp311-win32.whl", hash = "sha256:46954173612617a99a64aee103bcd3f078901b9a8dcfc6ae80cbf34ba23df989", size = 2074686 }, + { url = "https://files.pythonhosted.org/packages/7b/9d/6e030cc2c675539dbc5ef73aa97a3cbe09341e27ad38caed2b70c4273aff/SQLAlchemy-2.0.37-cp311-cp311-win_amd64.whl", hash = "sha256:7b7e772dc4bc507fdec4ee20182f15bd60d2a84f1e087a8accf5b5b7a0dcf2ba", size = 2099891 }, + { url = "https://files.pythonhosted.org/packages/86/62/e5de4a5e0c4f5ceffb2b461aaa2378c0ee00642930a8c38e5b80338add0f/SQLAlchemy-2.0.37-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2952748ecd67ed3b56773c185e85fc084f6bdcdec10e5032a7c25a6bc7d682ef", size = 2102692 }, + { url = "https://files.pythonhosted.org/packages/01/44/3b65f4f16abeffd611da0ebab9e3aadfca45d041a78a67835c41c6d28289/SQLAlchemy-2.0.37-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3151822aa1db0eb5afd65ccfafebe0ef5cda3a7701a279c8d0bf17781a793bb4", size = 2093079 }, + { url = "https://files.pythonhosted.org/packages/a4/d8/e3a6622e86e3ae3a41ba470d1bb095c1f2dedf6b71feae0b4b94b5951017/SQLAlchemy-2.0.37-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eaa8039b6d20137a4e02603aba37d12cd2dde7887500b8855356682fc33933f4", size = 3242509 }, + { url = "https://files.pythonhosted.org/packages/3a/ef/5a53a6a60ac5a5d4ed28959317dac1ff72bc16773ccd9b3fe79713fe27f3/SQLAlchemy-2.0.37-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1cdba1f73b64530c47b27118b7053b8447e6d6f3c8104e3ac59f3d40c33aa9fd", size = 3253368 }, + { url = "https://files.pythonhosted.org/packages/67/f2/30f5012379031cd5389eb06455282f926a4f99258e5ee5ccdcea27f30d67/SQLAlchemy-2.0.37-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1b2690456528a87234a75d1a1644cdb330a6926f455403c8e4f6cad6921f9098", size = 3188655 }, + { url = "https://files.pythonhosted.org/packages/fe/df/905499aa051605aeda62c1faf33d941ffb7fda291159ab1c24ef5207a079/SQLAlchemy-2.0.37-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:cf5ae8a9dcf657fd72144a7fd01f243236ea39e7344e579a121c4205aedf07bb", size = 3215281 }, + { url = "https://files.pythonhosted.org/packages/94/54/f2769e7e356520f75016d82ca43ed85e47ba50e636a34124db4625ae5976/SQLAlchemy-2.0.37-cp312-cp312-win32.whl", hash = "sha256:ea308cec940905ba008291d93619d92edaf83232ec85fbd514dcb329f3192761", size = 2072972 }, + { url = "https://files.pythonhosted.org/packages/c2/7f/241f059e0b7edb85845368f43964d6b0b41733c2f7fffaa993f8e66548a5/SQLAlchemy-2.0.37-cp312-cp312-win_amd64.whl", hash = "sha256:635d8a21577341dfe4f7fa59ec394b346da12420b86624a69e466d446de16aff", size = 2098597 }, + { url = "https://files.pythonhosted.org/packages/45/d1/e63e56ceab148e69f545703a74b90c8c6dc0a04a857e4e63a4c07a23cf91/SQLAlchemy-2.0.37-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8c4096727193762e72ce9437e2a86a110cf081241919ce3fab8e89c02f6b6658", size = 2097968 }, + { url = "https://files.pythonhosted.org/packages/fd/e5/93ce63310347062bd42aaa8b6785615c78539787ef4380252fcf8e2dcee3/SQLAlchemy-2.0.37-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e4fb5ac86d8fe8151966814f6720996430462e633d225497566b3996966b9bdb", size = 2088445 }, + { url = "https://files.pythonhosted.org/packages/1b/8c/d0e0081c09188dd26040fc8a09c7d87f539e1964df1ac60611b98ff2985a/SQLAlchemy-2.0.37-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e56a139bfe136a22c438478a86f8204c1eb5eed36f4e15c4224e4b9db01cb3e4", size = 3174880 }, + { url = "https://files.pythonhosted.org/packages/79/f7/3396038d8d4ea92c72f636a007e2fac71faae0b59b7e21af46b635243d09/SQLAlchemy-2.0.37-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2f95fc8e3f34b5f6b3effb49d10ac97c569ec8e32f985612d9b25dd12d0d2e94", size = 3188226 }, + { url = "https://files.pythonhosted.org/packages/ef/33/7a1d85716b29c86a744ed43690e243cb0e9c32e3b68a67a97eaa6b49ef66/SQLAlchemy-2.0.37-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c505edd429abdfe3643fa3b2e83efb3445a34a9dc49d5f692dd087be966020e0", size = 3121425 }, + { url = "https://files.pythonhosted.org/packages/27/11/fa63a77c88eb2f79bb8b438271fbacd66a546a438e4eaba32d62f11298e2/SQLAlchemy-2.0.37-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:12b0f1ec623cccf058cf21cb544f0e74656618165b083d78145cafde156ea7b6", size = 3149589 }, + { url = "https://files.pythonhosted.org/packages/b6/04/fcdd103b6871f2110460b8275d1c4828daa806997b0fa5a01c1cd7fd522d/SQLAlchemy-2.0.37-cp313-cp313-win32.whl", hash = "sha256:293f9ade06b2e68dd03cfb14d49202fac47b7bb94bffcff174568c951fbc7af2", size = 2070746 }, + { url = "https://files.pythonhosted.org/packages/d4/7c/e024719205bdc1465b7b7d3d22ece8e1ad57bc7d76ef6ed78bb5f812634a/SQLAlchemy-2.0.37-cp313-cp313-win_amd64.whl", hash = "sha256:d70f53a0646cc418ca4853da57cf3ddddbccb8c98406791f24426f2dd77fd0e2", size = 2094612 }, + { url = "https://files.pythonhosted.org/packages/3b/36/59cc97c365f2f79ac9f3f51446cae56dfd82c4f2dd98497e6be6de20fb91/SQLAlchemy-2.0.37-py3-none-any.whl", hash = "sha256:a8998bf9f8658bd3839cbc44ddbe982955641863da0c1efe5b00c1ab4f5c16b1", size = 1894113 }, ] [[package]] name = "sqlmodel" -version = "0.0.11" +version = "0.0.22" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic" }, { name = "sqlalchemy" }, - { name = "sqlalchemy2-stubs" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ed/c2/cea6067fa006f29780e56784172fd91472b54443b965a9131729d800997d/sqlmodel-0.0.11.tar.gz", hash = "sha256:fc33abbf7ec29caafabe3d0e1db61e33597857a289e5fd1ecdb91be702b26084", size = 21875 } +sdist = { url = "https://files.pythonhosted.org/packages/b5/39/8641040ab0d5e1d8a1c2325ae89a01ae659fc96c61a43d158fb71c9a0bf0/sqlmodel-0.0.22.tar.gz", hash = "sha256:7d37c882a30c43464d143e35e9ecaf945d88035e20117bf5ec2834a23cbe505e", size = 116392 } wheels = [ - { url = "https://files.pythonhosted.org/packages/9f/d0/8938bcf1c2d5c1f2a4aadece26364996aef437dbc7d5fcfa87cd1e13f7c6/sqlmodel-0.0.11-py3-none-any.whl", hash = "sha256:bc0d64c4b901d919d2f16bbd79aefb07cb268c29f7c1dd83a84758772ccc95c6", size = 21933 }, + { url = "https://files.pythonhosted.org/packages/dd/b1/3af5104b716c420e40a6ea1b09886cae3a1b9f4538343875f637755cae5b/sqlmodel-0.0.22-py3-none-any.whl", hash = "sha256:a1ed13e28a1f4057cbf4ff6cdb4fc09e85702621d3259ba17b3c230bfb2f941b", size = 28276 }, ] [[package]] @@ -2024,14 +2086,14 @@ wheels = [ [[package]] name = "starlette" -version = "0.25.0" +version = "0.41.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3b/89/f873d6392441a8be9bcbc0abbc92aa73849879e72565331bd9b54cfb8675/starlette-0.25.0.tar.gz", hash = "sha256:854c71e73736c429c2bdb07801f2c76c9cba497e7c3cf4988fde5e95fe4cdb3c", size = 50805 } +sdist = { url = "https://files.pythonhosted.org/packages/1a/4c/9b5764bd22eec91c4039ef4c55334e9187085da2d8a2df7bd570869aae18/starlette-0.41.3.tar.gz", hash = "sha256:0e4ab3d16522a255be6b28260b938eae2482f98ce5cc934cb08dce8dc3ba5835", size = 2574159 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b0/56/e7e16e133428ce19ff429d3d71aa7114048730385eea7d0985df5cf9aed6/starlette-0.25.0-py3-none-any.whl", hash = "sha256:774f1df1983fd594b9b6fb3ded39c2aa1979d10ac45caac0f4255cbe2acb8628", size = 66410 }, + { url = "https://files.pythonhosted.org/packages/96/00/2b325970b3060c7cecebab6d295afe763365822b1306a12eeab198f74323/starlette-0.41.3-py3-none-any.whl", hash = "sha256:44cedb2b7c77a9de33a8b74b2b90e9f50d11fcf25d8270ea525ad71a25374ff7", size = 73225 }, ] [[package]] @@ -2106,6 +2168,8 @@ dependencies = [ { name = "filetype" }, { name = "prometheus-fastapi-instrumentator" }, { name = "psycopg2" }, + { name = "pydantic" }, + { name = "pydantic-settings" }, { name = "python-frontmatter" }, { name = "python-magic" }, { name = "python-multipart" }, @@ -2135,15 +2199,17 @@ notebooks = [ [package.metadata] requires-dist = [ { name = "alembic", specifier = "~=1.11.1" }, - { name = "fastapi", specifier = "~=0.92.0" }, + { name = "fastapi", specifier = "~=0.115" }, { name = "filetype", specifier = "~=1.2.0" }, { name = "prometheus-fastapi-instrumentator", specifier = "~=6.1.0" }, { name = "psycopg2", specifier = "~=2.9.9" }, + { name = "pydantic", specifier = "~=2.2" }, + { name = "pydantic-settings", specifier = ">=2.7.1" }, { name = "python-frontmatter", specifier = "~=1.0.0" }, { name = "python-magic", specifier = "~=0.4.27" }, { name = "python-multipart", specifier = "~=0.0.6" }, { name = "redis", specifier = "~=5.0.1" }, - { name = "sqlmodel", specifier = "~=0.0.11" }, + { name = "sqlmodel", specifier = "~=0.0.22" }, { name = "transcribee-proto", editable = "../proto" }, { name = "uvicorn", extras = ["standard"], specifier = "~=0.20.0" }, { name = "websockets", specifier = "~=10.4" }, @@ -2174,7 +2240,7 @@ dependencies = [ ] [package.metadata] -requires-dist = [{ name = "pydantic", specifier = "~=1.10" }] +requires-dist = [{ name = "pydantic", specifier = "~=2.2" }] [[package]] name = "types-python-dateutil" diff --git a/frontend/src/editor/worker_status.tsx b/frontend/src/editor/worker_status.tsx index 70c24098..0bb00c77 100644 --- a/frontend/src/editor/worker_status.tsx +++ b/frontend/src/editor/worker_status.tsx @@ -11,8 +11,9 @@ type Task = RequestDataType[0]; function formatProgress(task: Task | null): string | undefined { if (!task) return; - if (task.state == 'ASSIGNED' && task.current_attempt?.progress !== undefined) - return `${(task.current_attempt?.progress * 100).toFixed(0)}%`; + const progress = task.current_attempt?.progress; + if (task.state == 'ASSIGNED' && progress !== null && progress !== undefined) + return `${(progress * 100).toFixed(0)}%`; else return task.state; } diff --git a/frontend/src/openapi-schema.ts b/frontend/src/openapi-schema.ts index cb3cb59e..bf5fb6bf 100644 --- a/frontend/src/openapi-schema.ts +++ b/frontend/src/openapi-schema.ts @@ -156,7 +156,7 @@ export interface components { /** * Task Type * @default ALIGN - * @enum {string} + * @constant */ task_type?: "ALIGN"; }; @@ -166,7 +166,10 @@ export interface components { changed_at: string; /** Created At */ created_at: string; - /** Id */ + /** + * Id + * Format: uuid + */ id: string; /** Media Files */ media_files: components["schemas"]["DocumentMedia"][]; @@ -177,7 +180,7 @@ export interface components { }; /** AssignedTaskResponse */ AssignedTaskResponse: { - current_attempt?: components["schemas"]["TaskAttemptResponse"]; + current_attempt: components["schemas"]["TaskAttemptResponse"] | null; /** Dependencies */ dependencies: string[]; document: components["schemas"]["Document"]; @@ -220,7 +223,7 @@ export interface components { /** Name */ name: string; /** Number Of Speakers */ - number_of_speakers?: number; + number_of_speakers?: number | null; }; /** Body_import_document_api_v1_documents_import__post */ Body_import_document_api_v1_documents_import__post: { @@ -245,11 +248,8 @@ export interface components { can_write: boolean; /** Name */ name: string; - /** - * Valid Until - * Format: date-time - */ - valid_until?: string; + /** Valid Until */ + valid_until?: string | null; }; /** CreateUser */ CreateUser: { @@ -277,7 +277,10 @@ export interface components { changed_at: string; /** Created At */ created_at: string; - /** Id */ + /** + * Id + * Format: uuid + */ id: string; /** Media Files */ media_files: components["schemas"]["DocumentMedia"][]; @@ -311,16 +314,13 @@ export interface components { name: string; /** Token */ token: string; - /** - * Valid Until - * Format: date-time - */ - valid_until?: string; + /** Valid Until */ + valid_until: string | null; }; /** DocumentUpdateRequest */ DocumentUpdateRequest: { /** Name */ - name?: string; + name?: string | null; }; /** DocumentWithAccessInfo */ DocumentWithAccessInfo: { @@ -332,7 +332,10 @@ export interface components { created_at: string; /** Has Full Access */ has_full_access: boolean; - /** Id */ + /** + * Id + * Format: uuid + */ id: string; /** Media Files */ media_files: components["schemas"]["DocumentMedia"][]; @@ -346,7 +349,6 @@ export interface components { }; /** * ExportFormat - * @description An enumeration. * @enum {string} */ ExportFormat: "VTT" | "SRT"; @@ -366,7 +368,7 @@ export interface components { /** * Task Type * @default EXPORT - * @enum {string} + * @constant */ task_type?: "EXPORT"; }; @@ -378,7 +380,7 @@ export interface components { /** Include Word Timing */ include_word_timing: boolean; /** Max Line Length */ - max_line_length?: number; + max_line_length?: number | null; }; /** HTTPValidationError */ HTTPValidationError: { @@ -388,7 +390,7 @@ export interface components { /** KeepaliveBody */ KeepaliveBody: { /** Progress */ - progress?: number; + progress?: number | null; }; /** LoginResponse */ LoginResponse: { @@ -407,7 +409,7 @@ export interface components { /** PageConfig */ PageConfig: { /** Footer Position */ - footer_position?: number; + footer_position?: number | null; /** Name */ name: string; /** Text */ @@ -416,7 +418,7 @@ export interface components { /** PublicConfig */ PublicConfig: { /** Logged Out Redirect Url */ - logged_out_redirect_url?: string; + logged_out_redirect_url?: string | null; /** Models */ models: { [key: string]: components["schemas"]["ModelConfig"]; @@ -430,7 +432,7 @@ export interface components { /** ShortPageConfig */ ShortPageConfig: { /** Footer Position */ - footer_position?: number; + footer_position?: number | null; /** Name */ name: string; }; @@ -446,14 +448,14 @@ export interface components { /** * Task Type * @default IDENTIFY_SPEAKERS - * @enum {string} + * @constant */ task_type?: "IDENTIFY_SPEAKERS"; }; /** TaskAttemptResponse */ TaskAttemptResponse: { /** Progress */ - progress?: number; + progress: number | null; }; /** TaskQueueInfoResponse */ TaskQueueInfoResponse: { @@ -468,13 +470,13 @@ export interface components { */ id: string; /** Remaining Cost */ - remaining_cost: number; + remaining_cost: number | null; state: components["schemas"]["TaskState"]; task_type: components["schemas"]["TaskType"]; }; /** TaskResponse */ TaskResponse: { - current_attempt?: components["schemas"]["TaskAttemptResponse"]; + current_attempt: components["schemas"]["TaskAttemptResponse"] | null; /** Dependencies */ dependencies: string[]; /** @@ -494,13 +496,11 @@ export interface components { }; /** * TaskState - * @description An enumeration. - * @enum {unknown} + * @enum {string} */ TaskState: "NEW" | "ASSIGNED" | "COMPLETED" | "FAILED"; /** * TaskType - * @description An enumeration. * @enum {string} */ TaskType: "IDENTIFY_SPEAKERS" | "TRANSCRIBE" | "ALIGN" | "REENCODE" | "EXPORT"; @@ -515,7 +515,7 @@ export interface components { /** * Task Type * @default TRANSCRIBE - * @enum {string} + * @constant */ task_type?: "TRANSCRIBE"; }; @@ -554,21 +554,15 @@ export interface components { }; /** Worker */ Worker: { - /** - * Deactivated At - * Format: date-time - */ - deactivated_at?: string; + /** Deactivated At */ + deactivated_at: string | null; /** * Id * Format: uuid */ id?: string; - /** - * Last Seen - * Format: date-time - */ - last_seen?: string; + /** Last Seen */ + last_seen: string | null; /** Name */ name: string; /** Token */ @@ -576,21 +570,15 @@ export interface components { }; /** WorkerWithId */ WorkerWithId: { - /** - * Deactivated At - * Format: date-time - */ - deactivated_at?: string; + /** Deactivated At */ + deactivated_at: string | null; /** * Id * Format: uuid */ id?: string; - /** - * Last Seen - * Format: date-time - */ - last_seen?: string; + /** Last Seen */ + last_seen: string | null; /** Name */ name: string; }; @@ -710,8 +698,8 @@ export interface operations { get_document_api_v1_documents__document_id___get: { parameters: { header?: { - authorization?: string; - "Share-Token"?: string; + authorization?: string | null; + "Share-Token"?: string | null; }; path: { document_id: string; @@ -736,8 +724,8 @@ export interface operations { delete_document_api_v1_documents__document_id___delete: { parameters: { header?: { - authorization?: string; - "Share-Token"?: string; + authorization?: string | null; + "Share-Token"?: string | null; }; path: { document_id: string; @@ -762,8 +750,8 @@ export interface operations { update_document_api_v1_documents__document_id___patch: { parameters: { header?: { - authorization?: string; - "Share-Token"?: string; + authorization?: string | null; + "Share-Token"?: string | null; }; path: { document_id: string; @@ -796,8 +784,8 @@ export interface operations { task_id: string; }; header?: { - authorization?: string; - "Share-Token"?: string; + authorization?: string | null; + "Share-Token"?: string | null; }; path: { document_id: string; @@ -860,11 +848,11 @@ export interface operations { format: components["schemas"]["ExportFormat"]; include_speaker_names: boolean; include_word_timing: boolean; - max_line_length?: number; + max_line_length?: number | null; }; header?: { - authorization?: string; - "Share-Token"?: string; + authorization?: string | null; + "Share-Token"?: string | null; }; path: { document_id: string; @@ -919,8 +907,8 @@ export interface operations { list_share_tokens_api_v1_documents__document_id__share_tokens__get: { parameters: { header?: { - authorization?: string; - "Share-Token"?: string; + authorization?: string | null; + "Share-Token"?: string | null; }; path: { document_id: string; @@ -946,7 +934,7 @@ export interface operations { parameters: { header: { authorization: string; - "Share-Token"?: string; + "Share-Token"?: string | null; }; path: { document_id: string; @@ -976,8 +964,8 @@ export interface operations { delete_share_tokens_api_v1_documents__document_id__share_tokens__token_id___delete: { parameters: { header?: { - authorization?: string; - "Share-Token"?: string; + authorization?: string | null; + "Share-Token"?: string | null; }; path: { token_id: string; @@ -1003,8 +991,8 @@ export interface operations { get_document_tasks_api_v1_documents__document_id__tasks__get: { parameters: { header?: { - authorization?: string; - "Share-Token"?: string; + authorization?: string | null; + "Share-Token"?: string | null; }; path: { document_id: string; @@ -1127,7 +1115,7 @@ export interface operations { /** @description Successful Response */ 200: { content: { - "application/json": components["schemas"]["AssignedTaskResponse"]; + "application/json": components["schemas"]["AssignedTaskResponse"] | null; }; }; /** @description Validation Error */ diff --git a/nix/pkgs/backend.nix b/nix/pkgs/backend.nix index 690fdc3b..bc358690 100644 --- a/nix/pkgs/backend.nix +++ b/nix/pkgs/backend.nix @@ -25,12 +25,6 @@ let pkgs.openssl ]; }); - - sqlalchemy = prev.sqlalchemy.overrideAttrs (old: { - nativeBuildInputs = old.nativeBuildInputs ++ [ - final.setuptools - ]; - }); }; pythonSet = diff --git a/packaging/start_backend.sh b/packaging/start_backend.sh index 028ce956..93f3f0a5 100755 --- a/packaging/start_backend.sh +++ b/packaging/start_backend.sh @@ -8,6 +8,8 @@ if [ ! -z "${TRANSCRIBEE_DYLD_LIBRARY_PATH:-}" ]; then export DYLD_LIBRARY_PATH=$LD_LIBRARY_PATH:${DYLD_LIBRARY_PATH:-} fi +export SQLALCHEMY_WARN_20=1 +export PYTHONWARNINGS=error::DeprecationWarning ./setup_backend.sh poe -C ../backend/ dev diff --git a/proto/pyproject.toml b/proto/pyproject.toml index 7b595d87..f622bc55 100644 --- a/proto/pyproject.toml +++ b/proto/pyproject.toml @@ -10,7 +10,7 @@ authors = [ ] dependencies = [ - "pydantic~=1.10", + "pydantic~=2.2", ] requires-python = ">=3.11" readme = "README.md" diff --git a/proto/transcribee_proto/api.py b/proto/transcribee_proto/api.py index 515114bf..9e6bb691 100644 --- a/proto/transcribee_proto/api.py +++ b/proto/transcribee_proto/api.py @@ -2,6 +2,7 @@ import enum from typing import Any, Dict, List, Literal, Optional +from uuid import UUID from pydantic import BaseModel @@ -21,7 +22,7 @@ class DocumentMedia(BaseModel): class Document(BaseModel): - id: str + id: UUID name: str created_at: str changed_at: str @@ -34,13 +35,13 @@ class DocumentWithAccessInfo(Document): class TaskBase(BaseModel): - id: str + id: UUID document: Document task_type: TaskType class SpeakerIdentificationTaskParameters(BaseModel): - number_of_speakers: int | None + number_of_speakers: int | None = None class SpeakerIdentificationTask(TaskBase): @@ -62,7 +63,7 @@ class ExportTaskParameters(BaseModel): format: ExportFormat include_speaker_names: bool include_word_timing: bool - max_line_length: int | None + max_line_length: int | None = None class TranscribeTask(TaskBase): @@ -95,4 +96,4 @@ class LoginResponse(BaseModel): class KeepaliveBody(BaseModel): - progress: Optional[float] + progress: Optional[float] = None diff --git a/proto/transcribee_proto/document.py b/proto/transcribee_proto/document.py index 4bbd035b..cc6c0677 100644 --- a/proto/transcribee_proto/document.py +++ b/proto/transcribee_proto/document.py @@ -31,7 +31,7 @@ def end(self) -> Optional[float]: class Document(BaseModel): - speaker_names: Optional[Mapping[str, str]] + speaker_names: Optional[Mapping[str, str]] = None children: List[Paragraph] version: int = 1 diff --git a/worker/pyproject.toml b/worker/pyproject.toml index a388ca78..09932297 100644 --- a/worker/pyproject.toml +++ b/worker/pyproject.toml @@ -12,7 +12,7 @@ authors = [ dependencies = [ "spectralcluster~=0.2.21", "numpy~=1.23", - "pydantic[dotenv]~=1.10", + "pydantic~=2.2", "transformers~=4.26", "torchaudio~=2.0", "torch~=2.0", @@ -25,6 +25,7 @@ dependencies = [ "transcribee-proto", "PyICU~=2.11", "faster-whisper~=1.1", + "pydantic-settings>=2.7.1", ] requires-python = ">=3.11" diff --git a/worker/tests/test_transcribe.py b/worker/tests/test_transcribe.py index 1501cd57..fb72f57c 100644 --- a/worker/tests/test_transcribe.py +++ b/worker/tests/test_transcribe.py @@ -3,7 +3,7 @@ from typing import List import pytest -from pydantic import BaseModel, parse_file_as +from pydantic import BaseModel from transcribee_proto.document import Paragraph from transcribee_worker.whisper_transcribe import ( move_space_to_prev_token, @@ -36,7 +36,7 @@ class SpecInput(BaseModel): ), ) def test_strict_sentence_paragraphs(data_file): - test_data = parse_file_as(SpecInput, data_file) + test_data = SpecInput.model_validate_json(Path(data_file).read_text()) output = list(doc_chain_func_to_list(strict_sentence_paragraphs)(test_data.input)) assert [x.text() for x in output] == [x.text() for x in test_data.expected] @@ -50,7 +50,7 @@ def test_strict_sentence_paragraphs(data_file): ), ) def test_move_space_to_prev_token(data_file): - test_data = parse_file_as(SpecInput, data_file) + test_data = SpecInput.model_validate_json(Path(data_file).read_text()) output = doc_chain_func_to_list(move_space_to_prev_token)(test_data.input) assert output == test_data.expected @@ -63,7 +63,7 @@ def test_move_space_to_prev_token(data_file): ), ) def test_space_and_sentences(data_file): - test_data = parse_file_as(SpecInput, data_file) + test_data = SpecInput.model_validate_json(Path(data_file).read_text()) output = doc_chain_func_to_list( move_space_to_prev_token, strict_sentence_paragraphs diff --git a/worker/transcribee_worker/api_client.py b/worker/transcribee_worker/api_client.py index 88e80675..6def3399 100644 --- a/worker/transcribee_worker/api_client.py +++ b/worker/transcribee_worker/api_client.py @@ -3,6 +3,7 @@ import urllib.parse from contextlib import asynccontextmanager from typing import AsyncGenerator +from uuid import UUID import requests from transcribee_worker.document import SyncedDocument @@ -36,7 +37,7 @@ def get(self, url): return req @asynccontextmanager - async def document(self, id: str) -> AsyncGenerator[SyncedDocument, None]: + async def document(self, id: UUID) -> AsyncGenerator[SyncedDocument, None]: params = urllib.parse.urlencode(self._get_headers()) async with connect( f"{self.websocket_base_url}{id}/?{params}", max_size=None diff --git a/worker/transcribee_worker/config.py b/worker/transcribee_worker/config.py index 0cf31bab..158b3ba1 100644 --- a/worker/transcribee_worker/config.py +++ b/worker/transcribee_worker/config.py @@ -2,12 +2,12 @@ from pathlib import Path from typing import Dict, Optional -from pydantic import BaseSettings +from pydantic_settings import BaseSettings, SettingsConfigDict class Settings(BaseSettings): - SAMPLE_RATE = 16_000 # samples per second - MODELS_DIR = Path(__file__).parent / ".data" / "models" + SAMPLE_RATE: int = 16_000 # samples per second + MODELS_DIR: Path = Path(__file__).parent / ".data" / "models" HUGGINGFACE_TOKEN: Optional[str] = None @@ -42,8 +42,7 @@ class Settings(BaseSettings): COMPUTE_TYPE: str = "int8" - class Config(BaseSettings.Config): - env_file = ".env" + model_config = SettingsConfigDict(env_file=".env") def setup_env_vars(self): os.environ["PYANNOTE_CACHE"] = str(self.MODELS_DIR) diff --git a/worker/transcribee_worker/worker.py b/worker/transcribee_worker/worker.py index 9ec23fb6..389b2349 100644 --- a/worker/transcribee_worker/worker.py +++ b/worker/transcribee_worker/worker.py @@ -7,11 +7,12 @@ from contextlib import asynccontextmanager from pathlib import Path from typing import Any, AsyncGenerator, Optional, Tuple +from uuid import UUID import automerge import ffmpeg import numpy.typing as npt -from pydantic import parse_raw_as +from pydantic import TypeAdapter from transcribee_proto.api import ( AlignTask, AssignedTask, @@ -129,7 +130,7 @@ def claim_task(self) -> Optional[AssignedTask]: "tasks/claim_unassigned_task/", params={"task_type": self.task_types} ) - return parse_raw_as(Optional[AssignedTask], req.text) # type: ignore + return TypeAdapter(Optional[AssignedTask]).validate_json(req.text) # type: ignore def _get_tmpfile(self, filename: str) -> Path: if self.tmpdir is None: @@ -166,7 +167,7 @@ def load_document_audio(self, document: ApiDocument) -> npt.NDArray: raise ValueError(f"Document {document} has no audio attached.") return load_audio(document_audio)[0] - def keepalive(self, task_id: str, progress: Optional[float]): + def keepalive(self, task_id: UUID, progress: Optional[float]): body = {} if progress is not None: body["progress"] = progress @@ -249,7 +250,7 @@ async def align(self, task: AlignTask, progress_callback: ProgressCallbackType): async with self.api_client.document(task.document.id) as doc: # TODO(robin): #perf: avoid this copy - document = EditorDocument.parse_obj(automerge.dump(doc.doc)) + document = EditorDocument.model_validate(automerge.dump(doc.doc)) aligned_para_iter = aiter( align( @@ -320,7 +321,7 @@ async def export(self, task: ExportTask, progress_callback: ProgressCallbackType res = None try: vtt = generate_web_vtt( - EditorDocument.parse_obj(automerge.dump(doc.doc)), + EditorDocument.model_validate(automerge.dump(doc.doc)), params.include_speaker_names, params.include_word_timing, params.max_line_length, @@ -360,7 +361,7 @@ def add_document_media_file(self, task: AssignedTask, path: Path, tags: list[str data=[("tags", tag) for tag in tags], ) - def mark_completed(self, task_id: str, additional_data: Optional[dict] = None): + def mark_completed(self, task_id: UUID, additional_data: Optional[dict] = None): extra_data = {**self._result_data} if additional_data: extra_data.update(additional_data) @@ -368,7 +369,7 @@ def mark_completed(self, task_id: str, additional_data: Optional[dict] = None): logging.debug(f"Marking task as completed {task_id=} {body=}") self.api_client.post(f"tasks/{task_id}/mark_completed/", json=body) - def mark_failed(self, task_id: str, additional_data: Optional[dict] = None): + def mark_failed(self, task_id: UUID, additional_data: Optional[dict] = None): extra_data = {**self._result_data} if additional_data: extra_data.update(additional_data) @@ -377,7 +378,11 @@ def mark_failed(self, task_id: str, additional_data: Optional[dict] = None): self.api_client.post(f"tasks/{task_id}/mark_failed/", json=body) def _set_progress( - self, task_id: str, step: str, progress: Optional[float], extra_data: Any = None + self, + _task_id: UUID, + step: str, + progress: Optional[float], + extra_data: Any = None, ): self._result_data["progress"].append( { @@ -391,7 +396,7 @@ def _set_progress( @asynccontextmanager async def keepalive_task( - self, task_id: str, seconds: float + self, task_id: UUID, seconds: float ) -> AsyncGenerator[None, None]: stop_event = asyncio.Event() diff --git a/worker/uv.lock b/worker/uv.lock index 79e519ed..c68e4ad7 100644 --- a/worker/uv.lock +++ b/worker/uv.lock @@ -10,6 +10,15 @@ resolution-markers = [ [manifest] overrides = [{ name = "soundfile", specifier = "==0.11.0" }] +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, +] + [[package]] name = "ansicon" version = "1.89.0" @@ -573,41 +582,83 @@ wheels = [ [[package]] name = "pydantic" -version = "1.10.20" +version = "2.10.5" source = { registry = "https://pypi.org/simple" } dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c3/eb/081e5c6bad10570613df9b769bc8553bab771554cbd71371115e38d5e303/pydantic-1.10.20.tar.gz", hash = "sha256:8dbb8fb6f0ee469c197047e5e69f51677f0bf6297619c98c814a22965a5486d4", size = 356518 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6c/0e/b5c0d7e7950ff6816ba9e2cabcb8655f34137bedf961780fe2254a23b6db/pydantic-1.10.20-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bed9ec215fa46fb4024777d4098816e2779b5c0192c3646aa1766c7527031c7d", size = 2845160 }, - { url = "https://files.pythonhosted.org/packages/7a/f6/e8bba6e8dfa7d17a04463361f8d4442417f15e2fad5da5d219ba9b376d85/pydantic-1.10.20-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e6fbf8fdc5991560709cb24cebab2e3812b7c8e3eac922a8bf51a531046fb494", size = 2552963 }, - { url = "https://files.pythonhosted.org/packages/8c/13/113f132d8638dda224b06a37cad967c84c2e612d0c07480550a55bbda619/pydantic-1.10.20-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f6d485ef36dcd597f7d6226bf8cb222b3b5a1a9191261b7ec5677b08e9230bc", size = 3140364 }, - { url = "https://files.pythonhosted.org/packages/95/51/8c1642cb9c80f84fa05c650395caf19c1fceba335a9960914cda26d33f84/pydantic-1.10.20-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:664110d074bfc14432326d59ec51da0c260050935fe7cdb3579d24b02766dd93", size = 3213620 }, - { url = "https://files.pythonhosted.org/packages/2a/0a/93d1e3825d8a2e687f1025b03251108df86171831bff4b2ff0db58b83eef/pydantic-1.10.20-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d6efdd3653713c1e24a46ec966e296faf28933da319692e2adbe5bb6d980418", size = 3320922 }, - { url = "https://files.pythonhosted.org/packages/ec/ab/2193ce40469409f94e23a6eb859809f91a0513c57ada65037af2d74639e4/pydantic-1.10.20-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:604904991eb38e68674892bfea9e9fce6cea62b4d9b9593a2575080842512edb", size = 3243736 }, - { url = "https://files.pythonhosted.org/packages/94/20/811fd66e7381a8d3005d5fab4b426634ed17505487be242d1f1890f5b13a/pydantic-1.10.20-cp311-cp311-win_amd64.whl", hash = "sha256:875f6b2f99b621c1fcdb3ba947e4a89120c5199ffe873b5c0f81b0668a2b1647", size = 2307225 }, - { url = "https://files.pythonhosted.org/packages/5c/17/0c010d2288843e103c42a04891923b2dd6c548ed3f654f8f6c963feb0c64/pydantic-1.10.20-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ef6feab9add46f970cbe5676da7681700ddade1d9aeeb206d61c3e6e47d39e8f", size = 2794434 }, - { url = "https://files.pythonhosted.org/packages/1c/82/62c1c6e45b7b536a30d6255a56e34c276162417701708d7eb1da17003090/pydantic-1.10.20-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:31e699a67b53c9a07780caacf662d4bc6eed15cb70b3bc30256cbef000ad6874", size = 2533956 }, - { url = "https://files.pythonhosted.org/packages/ff/1c/a50964e655dadfa4a6260bf01ddc6b84554a68024beedda95b6232b0d4e8/pydantic-1.10.20-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:657f0977327dd41b3e25c2adec81cf58a7e62b868876bef80b9deec38d264428", size = 2993534 }, - { url = "https://files.pythonhosted.org/packages/b8/a5/1d0b04786fac9920dcb69a1023777c6176a6fbd9b940920c7432366dec2f/pydantic-1.10.20-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd4726eb30445555fa2e95795af1603af6023d216e9d9f45d940e0f07a1eda7", size = 3036293 }, - { url = "https://files.pythonhosted.org/packages/be/1e/8a56c63d4c4d02ab33585fc3a75af9679540360716e7596058613d81a3b3/pydantic-1.10.20-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:64793e059a9e254a1dd63e70bb946e550ac6ba465e13b9aec40408bc10b038c0", size = 3171156 }, - { url = "https://files.pythonhosted.org/packages/58/fc/6cb5a4b8e924f461b8f8ac7c19ef6b0ab84b46d243977992b4b99a0dcd23/pydantic-1.10.20-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:88f552e84c27b321310c0bdefe90b5279f5c4255867b4fcbbbdfe8bb86831750", size = 3123165 }, - { url = "https://files.pythonhosted.org/packages/53/ea/8bb04a3b3566a3cb76d615bd67d2c4263a9b779b81100f9259e1f3d7e580/pydantic-1.10.20-cp312-cp312-win_amd64.whl", hash = "sha256:0a2351eb63b8c7016d30b1d8fa390f4666488a0ed4071972afb63696810ecc8f", size = 2189333 }, - { url = "https://files.pythonhosted.org/packages/43/93/51030ea841583508154c8626dfde32a8556fd5b1e60dbc292f49d0e7554c/pydantic-1.10.20-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d9041e27b36597a54fbe59c4f8e205748d72d455dfc29e98b5d72d97f7d0c036", size = 2796668 }, - { url = "https://files.pythonhosted.org/packages/73/78/948d20580c07a99ce2468860a42e135e599b171c0f13f6adac78323919ad/pydantic-1.10.20-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:13262acfdc60b8af048ee27483ebbcbdf5ea3217c8b8e4eede94373c8d65f6a5", size = 2538168 }, - { url = "https://files.pythonhosted.org/packages/0c/23/d49b2249eeca644e37f1f60a09c72973070e98e929e993ccdcc4ab15de4c/pydantic-1.10.20-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1a58b0b07a8bce4c8fb368435571a8bcf0457332c503055b1bbff5e4e65c13e", size = 2985709 }, - { url = "https://files.pythonhosted.org/packages/54/9f/83e888afb1133f54076b8af749b5e247330ad45c36b9457831d6fc8d0b7b/pydantic-1.10.20-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b378c2fd51dc0c475a01c46748a5bcf7d41d23e4620d7622a373d7dab6dc2cf", size = 3015169 }, - { url = "https://files.pythonhosted.org/packages/ad/4b/07b9cc10fc0ad1defc6531dc598922929807f12620f4ea515bd47a10e6d7/pydantic-1.10.20-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:61bcdd86348dd5e268f0f306d8d9c00c0e10f158af53538ce270447e67af88b0", size = 3163895 }, - { url = "https://files.pythonhosted.org/packages/cb/92/fde1bd6ba297eeaac12b1454c8c294dd0cdba0e8de71ff2269b3678d08c3/pydantic-1.10.20-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7fb3d37f29cfa83b00eef518187509044626162511da5367ada03ba29647630f", size = 3117174 }, - { url = "https://files.pythonhosted.org/packages/46/81/81764ecb32de8725e5b77fca9c83d871ad1c29664e907eaf77f4344240ea/pydantic-1.10.20-cp313-cp313-win_amd64.whl", hash = "sha256:64aae7ea5c88db33fabeb3017f8d25f2ebff0244a3db3819c38bdcef42e917d6", size = 2172937 }, - { url = "https://files.pythonhosted.org/packages/e8/08/ce63c53094a258983667f1ab22f0fb841310f16a07dba6a550e8006bb2e8/pydantic-1.10.20-py3-none-any.whl", hash = "sha256:cd9c1a6a60d54281defb35292d3f2b70bce1b62fe404fd991688fa146715936a", size = 166366 }, -] - -[package.optional-dependencies] -dotenv = [ +sdist = { url = "https://files.pythonhosted.org/packages/6a/c7/ca334c2ef6f2e046b1144fe4bb2a5da8a4c574e7f2ebf7e16b34a6a2fa92/pydantic-2.10.5.tar.gz", hash = "sha256:278b38dbbaec562011d659ee05f63346951b3a248a6f3642e1bc68894ea2b4ff", size = 761287 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/58/26/82663c79010b28eddf29dcdd0ea723439535fa917fce5905885c0e9ba562/pydantic-2.10.5-py3-none-any.whl", hash = "sha256:4dd4e322dbe55472cb7ca7e73f4b63574eecccf2835ffa2af9021ce113c83c53", size = 431426 }, +] + +[[package]] +name = "pydantic-core" +version = "2.27.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fc/01/f3e5ac5e7c25833db5eb555f7b7ab24cd6f8c322d3a3ad2d67a952dc0abc/pydantic_core-2.27.2.tar.gz", hash = "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39", size = 413443 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/89/f3450af9d09d44eea1f2c369f49e8f181d742f28220f88cc4dfaae91ea6e/pydantic_core-2.27.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:8e10c99ef58cfdf2a66fc15d66b16c4a04f62bca39db589ae8cba08bc55331bc", size = 1893421 }, + { url = "https://files.pythonhosted.org/packages/9e/e3/71fe85af2021f3f386da42d291412e5baf6ce7716bd7101ea49c810eda90/pydantic_core-2.27.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:26f32e0adf166a84d0cb63be85c562ca8a6fa8de28e5f0d92250c6b7e9e2aff7", size = 1814998 }, + { url = "https://files.pythonhosted.org/packages/a6/3c/724039e0d848fd69dbf5806894e26479577316c6f0f112bacaf67aa889ac/pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c19d1ea0673cd13cc2f872f6c9ab42acc4e4f492a7ca9d3795ce2b112dd7e15", size = 1826167 }, + { url = "https://files.pythonhosted.org/packages/2b/5b/1b29e8c1fb5f3199a9a57c1452004ff39f494bbe9bdbe9a81e18172e40d3/pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e68c4446fe0810e959cdff46ab0a41ce2f2c86d227d96dc3847af0ba7def306", size = 1865071 }, + { url = "https://files.pythonhosted.org/packages/89/6c/3985203863d76bb7d7266e36970d7e3b6385148c18a68cc8915fd8c84d57/pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9640b0059ff4f14d1f37321b94061c6db164fbe49b334b31643e0528d100d99", size = 2036244 }, + { url = "https://files.pythonhosted.org/packages/0e/41/f15316858a246b5d723f7d7f599f79e37493b2e84bfc789e58d88c209f8a/pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40d02e7d45c9f8af700f3452f329ead92da4c5f4317ca9b896de7ce7199ea459", size = 2737470 }, + { url = "https://files.pythonhosted.org/packages/a8/7c/b860618c25678bbd6d1d99dbdfdf0510ccb50790099b963ff78a124b754f/pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c1fd185014191700554795c99b347d64f2bb637966c4cfc16998a0ca700d048", size = 1992291 }, + { url = "https://files.pythonhosted.org/packages/bf/73/42c3742a391eccbeab39f15213ecda3104ae8682ba3c0c28069fbcb8c10d/pydantic_core-2.27.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d81d2068e1c1228a565af076598f9e7451712700b673de8f502f0334f281387d", size = 1994613 }, + { url = "https://files.pythonhosted.org/packages/94/7a/941e89096d1175d56f59340f3a8ebaf20762fef222c298ea96d36a6328c5/pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1a4207639fb02ec2dbb76227d7c751a20b1a6b4bc52850568e52260cae64ca3b", size = 2002355 }, + { url = "https://files.pythonhosted.org/packages/6e/95/2359937a73d49e336a5a19848713555605d4d8d6940c3ec6c6c0ca4dcf25/pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:3de3ce3c9ddc8bbd88f6e0e304dea0e66d843ec9de1b0042b0911c1663ffd474", size = 2126661 }, + { url = "https://files.pythonhosted.org/packages/2b/4c/ca02b7bdb6012a1adef21a50625b14f43ed4d11f1fc237f9d7490aa5078c/pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:30c5f68ded0c36466acede341551106821043e9afaad516adfb6e8fa80a4e6a6", size = 2153261 }, + { url = "https://files.pythonhosted.org/packages/72/9d/a241db83f973049a1092a079272ffe2e3e82e98561ef6214ab53fe53b1c7/pydantic_core-2.27.2-cp311-cp311-win32.whl", hash = "sha256:c70c26d2c99f78b125a3459f8afe1aed4d9687c24fd677c6a4436bc042e50d6c", size = 1812361 }, + { url = "https://files.pythonhosted.org/packages/e8/ef/013f07248041b74abd48a385e2110aa3a9bbfef0fbd97d4e6d07d2f5b89a/pydantic_core-2.27.2-cp311-cp311-win_amd64.whl", hash = "sha256:08e125dbdc505fa69ca7d9c499639ab6407cfa909214d500897d02afb816e7cc", size = 1982484 }, + { url = "https://files.pythonhosted.org/packages/10/1c/16b3a3e3398fd29dca77cea0a1d998d6bde3902fa2706985191e2313cc76/pydantic_core-2.27.2-cp311-cp311-win_arm64.whl", hash = "sha256:26f0d68d4b235a2bae0c3fc585c585b4ecc51382db0e3ba402a22cbc440915e4", size = 1867102 }, + { url = "https://files.pythonhosted.org/packages/d6/74/51c8a5482ca447871c93e142d9d4a92ead74de6c8dc5e66733e22c9bba89/pydantic_core-2.27.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9e0c8cfefa0ef83b4da9588448b6d8d2a2bf1a53c3f1ae5fca39eb3061e2f0b0", size = 1893127 }, + { url = "https://files.pythonhosted.org/packages/d3/f3/c97e80721735868313c58b89d2de85fa80fe8dfeeed84dc51598b92a135e/pydantic_core-2.27.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83097677b8e3bd7eaa6775720ec8e0405f1575015a463285a92bfdfe254529ef", size = 1811340 }, + { url = "https://files.pythonhosted.org/packages/9e/91/840ec1375e686dbae1bd80a9e46c26a1e0083e1186abc610efa3d9a36180/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:172fce187655fece0c90d90a678424b013f8fbb0ca8b036ac266749c09438cb7", size = 1822900 }, + { url = "https://files.pythonhosted.org/packages/f6/31/4240bc96025035500c18adc149aa6ffdf1a0062a4b525c932065ceb4d868/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:519f29f5213271eeeeb3093f662ba2fd512b91c5f188f3bb7b27bc5973816934", size = 1869177 }, + { url = "https://files.pythonhosted.org/packages/fa/20/02fbaadb7808be578317015c462655c317a77a7c8f0ef274bc016a784c54/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05e3a55d124407fffba0dd6b0c0cd056d10e983ceb4e5dbd10dda135c31071d6", size = 2038046 }, + { url = "https://files.pythonhosted.org/packages/06/86/7f306b904e6c9eccf0668248b3f272090e49c275bc488a7b88b0823444a4/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c3ed807c7b91de05e63930188f19e921d1fe90de6b4f5cd43ee7fcc3525cb8c", size = 2685386 }, + { url = "https://files.pythonhosted.org/packages/8d/f0/49129b27c43396581a635d8710dae54a791b17dfc50c70164866bbf865e3/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fb4aadc0b9a0c063206846d603b92030eb6f03069151a625667f982887153e2", size = 1997060 }, + { url = "https://files.pythonhosted.org/packages/0d/0f/943b4af7cd416c477fd40b187036c4f89b416a33d3cc0ab7b82708a667aa/pydantic_core-2.27.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28ccb213807e037460326424ceb8b5245acb88f32f3d2777427476e1b32c48c4", size = 2004870 }, + { url = "https://files.pythonhosted.org/packages/35/40/aea70b5b1a63911c53a4c8117c0a828d6790483f858041f47bab0b779f44/pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:de3cd1899e2c279b140adde9357c4495ed9d47131b4a4eaff9052f23398076b3", size = 1999822 }, + { url = "https://files.pythonhosted.org/packages/f2/b3/807b94fd337d58effc5498fd1a7a4d9d59af4133e83e32ae39a96fddec9d/pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:220f892729375e2d736b97d0e51466252ad84c51857d4d15f5e9692f9ef12be4", size = 2130364 }, + { url = "https://files.pythonhosted.org/packages/fc/df/791c827cd4ee6efd59248dca9369fb35e80a9484462c33c6649a8d02b565/pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a0fcd29cd6b4e74fe8ddd2c90330fd8edf2e30cb52acda47f06dd615ae72da57", size = 2158303 }, + { url = "https://files.pythonhosted.org/packages/9b/67/4e197c300976af185b7cef4c02203e175fb127e414125916bf1128b639a9/pydantic_core-2.27.2-cp312-cp312-win32.whl", hash = "sha256:1e2cb691ed9834cd6a8be61228471d0a503731abfb42f82458ff27be7b2186fc", size = 1834064 }, + { url = "https://files.pythonhosted.org/packages/1f/ea/cd7209a889163b8dcca139fe32b9687dd05249161a3edda62860430457a5/pydantic_core-2.27.2-cp312-cp312-win_amd64.whl", hash = "sha256:cc3f1a99a4f4f9dd1de4fe0312c114e740b5ddead65bb4102884b384c15d8bc9", size = 1989046 }, + { url = "https://files.pythonhosted.org/packages/bc/49/c54baab2f4658c26ac633d798dab66b4c3a9bbf47cff5284e9c182f4137a/pydantic_core-2.27.2-cp312-cp312-win_arm64.whl", hash = "sha256:3911ac9284cd8a1792d3cb26a2da18f3ca26c6908cc434a18f730dc0db7bfa3b", size = 1885092 }, + { url = "https://files.pythonhosted.org/packages/41/b1/9bc383f48f8002f99104e3acff6cba1231b29ef76cfa45d1506a5cad1f84/pydantic_core-2.27.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b", size = 1892709 }, + { url = "https://files.pythonhosted.org/packages/10/6c/e62b8657b834f3eb2961b49ec8e301eb99946245e70bf42c8817350cbefc/pydantic_core-2.27.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154", size = 1811273 }, + { url = "https://files.pythonhosted.org/packages/ba/15/52cfe49c8c986e081b863b102d6b859d9defc63446b642ccbbb3742bf371/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9", size = 1823027 }, + { url = "https://files.pythonhosted.org/packages/b1/1c/b6f402cfc18ec0024120602bdbcebc7bdd5b856528c013bd4d13865ca473/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9", size = 1868888 }, + { url = "https://files.pythonhosted.org/packages/bd/7b/8cb75b66ac37bc2975a3b7de99f3c6f355fcc4d89820b61dffa8f1e81677/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1", size = 2037738 }, + { url = "https://files.pythonhosted.org/packages/c8/f1/786d8fe78970a06f61df22cba58e365ce304bf9b9f46cc71c8c424e0c334/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a", size = 2685138 }, + { url = "https://files.pythonhosted.org/packages/a6/74/d12b2cd841d8724dc8ffb13fc5cef86566a53ed358103150209ecd5d1999/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e", size = 1997025 }, + { url = "https://files.pythonhosted.org/packages/a0/6e/940bcd631bc4d9a06c9539b51f070b66e8f370ed0933f392db6ff350d873/pydantic_core-2.27.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4", size = 2004633 }, + { url = "https://files.pythonhosted.org/packages/50/cc/a46b34f1708d82498c227d5d80ce615b2dd502ddcfd8376fc14a36655af1/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27", size = 1999404 }, + { url = "https://files.pythonhosted.org/packages/ca/2d/c365cfa930ed23bc58c41463bae347d1005537dc8db79e998af8ba28d35e/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee", size = 2130130 }, + { url = "https://files.pythonhosted.org/packages/f4/d7/eb64d015c350b7cdb371145b54d96c919d4db516817f31cd1c650cae3b21/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1", size = 2157946 }, + { url = "https://files.pythonhosted.org/packages/a4/99/bddde3ddde76c03b65dfd5a66ab436c4e58ffc42927d4ff1198ffbf96f5f/pydantic_core-2.27.2-cp313-cp313-win32.whl", hash = "sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130", size = 1834387 }, + { url = "https://files.pythonhosted.org/packages/71/47/82b5e846e01b26ac6f1893d3c5f9f3a2eb6ba79be26eef0b759b4fe72946/pydantic_core-2.27.2-cp313-cp313-win_amd64.whl", hash = "sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee", size = 1990453 }, + { url = "https://files.pythonhosted.org/packages/51/b2/b2b50d5ecf21acf870190ae5d093602d95f66c9c31f9d5de6062eb329ad1/pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b", size = 1885186 }, +] + +[[package]] +name = "pydantic-settings" +version = "2.7.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, { name = "python-dotenv" }, ] +sdist = { url = "https://files.pythonhosted.org/packages/73/7b/c58a586cd7d9ac66d2ee4ba60ca2d241fa837c02bca9bea80a9a8c3d22a9/pydantic_settings-2.7.1.tar.gz", hash = "sha256:10c9caad35e64bfb3c2fbf70a078c0e25cc92499782e5200747f942a065dec93", size = 79920 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b4/46/93416fdae86d40879714f72956ac14df9c7b76f7d41a4d68aa9f71a0028b/pydantic_settings-2.7.1-py3-none-any.whl", hash = "sha256:590be9e6e24d06db33a4262829edef682500ef008565a969c73d39d5f8bfb3fd", size = 29718 }, +] [[package]] name = "pyicu" @@ -1164,7 +1215,7 @@ dependencies = [ ] [package.metadata] -requires-dist = [{ name = "pydantic", specifier = "~=1.10" }] +requires-dist = [{ name = "pydantic", specifier = "~=2.2" }] [[package]] name = "transcribee-worker" @@ -1175,7 +1226,8 @@ dependencies = [ { name = "faster-whisper" }, { name = "ffmpeg-python" }, { name = "numpy" }, - { name = "pydantic", extra = ["dotenv"] }, + { name = "pydantic" }, + { name = "pydantic-settings" }, { name = "pyicu" }, { name = "scikit-learn" }, { name = "spectralcluster" }, @@ -1204,7 +1256,8 @@ requires-dist = [ { name = "faster-whisper", specifier = "~=1.1" }, { name = "ffmpeg-python", specifier = "~=0.2.0" }, { name = "numpy", specifier = "~=1.23" }, - { name = "pydantic", extras = ["dotenv"], specifier = "~=1.10" }, + { name = "pydantic", specifier = "~=2.2" }, + { name = "pydantic-settings", specifier = ">=2.7.1" }, { name = "pyicu", specifier = "~=2.11" }, { name = "scikit-learn", specifier = "~=1.2" }, { name = "spectralcluster", specifier = "~=0.2.21" },