diff --git a/.fern/metadata.json b/.fern/metadata.json index cfc40977..f810fd87 100644 --- a/.fern/metadata.json +++ b/.fern/metadata.json @@ -1,7 +1,7 @@ { - "cliVersion": "5.44.2", + "cliVersion": "5.44.6", "generatorName": "fernapi/fern-python-sdk", - "generatorVersion": "5.14.8", + "generatorVersion": "5.14.18", "generatorConfig": { "client": { "class_name": "BaseClient", @@ -16,8 +16,8 @@ "skip_validation": true } }, - "originGitCommit": "d228f82e93aaa8aa77f978d458cf912f3daaa8c1", + "originGitCommit": "8dd6f48a06eee8c3985894f68ba3b554e5564d21", "originGitCommitIsDirty": true, "invokedBy": "manual", - "sdkVersion": "7.3.1" + "sdkVersion": "7.3.2" } \ No newline at end of file diff --git a/.fernignore b/.fernignore index a8a767c0..3c065285 100644 --- a/.fernignore +++ b/.fernignore @@ -36,7 +36,6 @@ src/deepgram/speak/v1/socket_client.py src/deepgram/agent/v1/types/agent_v1settings_agent_context.py src/deepgram/agent/v1/types/agent_v1settings_agent.py src/deepgram/agent/v1/types/agent_v1settings.py -src/deepgram/agent/v1/types/agent_v1settings_audio_output.py src/deepgram/agent/v1/requests/agent_v1settings_agent_context.py src/deepgram/agent/v1/requests/agent_v1settings_agent.py src/deepgram/agent/v1/requests/agent_v1settings.py @@ -74,6 +73,33 @@ src/deepgram/agent/v1/requests/agent_v1settings_agent_context_listen_provider_v2 src/deepgram/agent/v1/requests/agent_v1settings_agent_listen_provider_v1.py src/deepgram/agent/v1/requests/agent_v1settings_agent_listen_provider_v2.py +# Hand-written compat shims recreating the top-level DeepgramListenProviderV2LanguageHint +# type/param that Fern removed in the 2026-06-15 regen (Union[str, List[str]]). The +# *V2LanguageHint listen-provider aliases above import these, and they remain part of the +# public import surface, so they are recreated by hand and frozen so Fern won't delete them. +src/deepgram/types/deepgram_listen_provider_v2language_hint.py +src/deepgram/requests/deepgram_listen_provider_v2language_hint.py + +# Behavioral back-compat shim for the language_hint -> language_hints rename (2026-06-15 +# regen). The public field was historically (incorrectly) named `language_hint`, accepting +# a str or a list; the API field is `language_hints` (a list, and the server uses +# deny_unknown_fields so the singular key is rejected on the wire). These three generated +# models carry a hand-added model_validator(mode='before') / root_validator(pre=True) that +# remaps a legacy `language_hint=` kwarg to `language_hints` and drops the dead singular +# key. Frozen so Fern won't strip the validator on regen. Remove the shim and unfreeze when +# the singular alias is intentionally retired in a future major release. +src/deepgram/types/deepgram_listen_provider_v2.py +src/deepgram/agent/v1/types/agent_v1settings_agent_listen_provider.py +src/deepgram/agent/v1/types/agent_v1settings_agent_context_listen_provider.py + +# Hand-written compat shim recreating ListenV2CloseStreamType, which Fern removed in the +# 2026-06-15 regen (docs #946). The original generated type wrongly allowed +# Union[Literal["Finalize","CloseStream","KeepAlive"], Any] — v2 copied v1's control-message +# enum, but a CloseStream message's `type` can only ever be "CloseStream". Recreated as the +# corrected single-value Literal["CloseStream"] to preserve the public import path without +# resurrecting the invalid values. Frozen so Fern won't delete it again. +src/deepgram/listen/v2/types/listen_v2close_stream_type.py + # Package __init__.py files that carry hand-applied legacy alias re-exports for the # above shims. Fern would otherwise regenerate these and strip the legacy entries on # every regen. Frozen to preserve the public-import surface for renamed types/params. @@ -85,6 +111,10 @@ src/deepgram/agent/v1/types/__init__.py src/deepgram/agent/v1/requests/__init__.py src/deepgram/types/__init__.py src/deepgram/requests/__init__.py +# listen __init__.py files re-export the ListenV2CloseStreamType shim above. +src/deepgram/listen/__init__.py +src/deepgram/listen/v2/__init__.py +src/deepgram/listen/v2/types/__init__.py # Coerces Python bools to lowercase "true"/"false" before urlencode, which # otherwise stringifies via str() and produces "True"/"False" — rejected by @@ -96,8 +126,12 @@ src/deepgram/core/query_encoder.py # Hand-written custom tests tests/custom/test_agent_history.py tests/custom/test_compat_aliases.py +tests/custom/test_language_hint_compat.py +tests/custom/test_language_hints_feature.py +tests/custom/test_listen_v2_regen_constraints.py tests/custom/test_query_encoder.py tests/custom/test_secure_logging.py +tests/custom/test_socket_client_shims.py tests/custom/test_text_builder.py tests/custom/test_transport.py tests/typecheck/compat_aliases.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2128e6b0..60fc69b4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,6 +25,8 @@ jobs: run: poetry install - name: Compile run: poetry run mypy src/ + - name: Typecheck tests + run: poetry run mypy tests/typecheck test: runs-on: ubuntu-latest strategy: diff --git a/AGENTS.md b/AGENTS.md index e3767c84..fd3febac 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -29,6 +29,8 @@ Current permanently frozen files: - `src/deepgram/agent/v1/requests/agent_v1history_content.py`, `src/deepgram/agent/v1/requests/agent_v1history_function_calls.py`, `src/deepgram/agent/v1/requests/agent_v1settings_agent_context_messages_item.py`, `src/deepgram/agent/v1/requests/agent_v1settings_agent_context_messages_item_content.py`, `src/deepgram/agent/v1/requests/agent_v1settings_agent_context_messages_item_function_calls.py`, `src/deepgram/agent/v1/requests/agent_v1settings_agent_context_messages_item_function_calls_function_calls_item.py` — hand-written compatibility aliases preserving old public Agent History request-param imports after regen renames - `src/deepgram/types/create_key_v1request_one.py`, `src/deepgram/requests/create_key_v1request_one.py` — hand-written compatibility aliases preserving the old public create-key request imports after the regen rename to `CreateKeyV1Request` - `src/deepgram/agent/v1/types/agent_v1settings_agent_context_listen_provider_v1.py`, `src/deepgram/agent/v1/types/agent_v1settings_agent_context_listen_provider_v2.py`, `src/deepgram/agent/v1/types/agent_v1settings_agent_context_listen_provider_v2language_hint.py`, `src/deepgram/agent/v1/types/agent_v1settings_agent_listen_provider_v1.py`, `src/deepgram/agent/v1/types/agent_v1settings_agent_listen_provider_v2.py`, `src/deepgram/agent/v1/requests/agent_v1settings_agent_context_listen_provider_v1.py`, `src/deepgram/agent/v1/requests/agent_v1settings_agent_context_listen_provider_v2.py`, `src/deepgram/agent/v1/requests/agent_v1settings_agent_context_listen_provider_v2language_hint.py`, `src/deepgram/agent/v1/requests/agent_v1settings_agent_listen_provider_v1.py`, `src/deepgram/agent/v1/requests/agent_v1settings_agent_listen_provider_v2.py` — hand-written compatibility aliases for the 2026-05-14 spec dedup that consolidated `AgentV1SettingsAgent[Context]ListenProviderV{1,2,V2LanguageHint}` into top-level `DeepgramListenProvider*` types +- `src/deepgram/types/deepgram_listen_provider_v2language_hint.py`, `src/deepgram/requests/deepgram_listen_provider_v2language_hint.py` — hand-written shims recreating the top-level `DeepgramListenProviderV2LanguageHint`/`...Params` type (`Union[str, List[str]]`) that Fern removed in the 2026-06-15 regen. The `*V2LanguageHint` listen-provider aliases above import from these and they remain part of the public import surface, so they are recreated by hand and frozen so Fern won't delete them again +- `src/deepgram/listen/v2/types/listen_v2close_stream_type.py` — hand-written shim recreating `ListenV2CloseStreamType`, which Fern removed in the 2026-06-15 regen (docs #946). The original generated type wrongly allowed `Union[Literal["Finalize","CloseStream","KeepAlive"], Any]` (v2 copied v1's control-message enum); a CloseStream message's `type` can only ever be `"CloseStream"`. Recreated as the corrected `Literal["CloseStream"]` to preserve the public import path without resurrecting the invalid values. Re-exported from the three `listen` `__init__.py` files (temporarily frozen, below). - `src/deepgram/transport_interface.py`, `src/deepgram/transport.py`, `src/deepgram/transports/` — custom transport layer - `tests/custom/test_agent_history.py` — hand-written regression test for Agent History websocket payload parsing - `tests/custom/test_compat_aliases.py` — hand-written regression test for backward-compatible alias imports after regen renames @@ -55,10 +57,10 @@ Current temporarily frozen files: - `src/deepgram/listen/v2/socket_client.py` — same + `send_configure` typing.Any/raw shim, response Union uses typing.Any instead of `ListenV2ConfigureSuccess` - `src/deepgram/agent/v1/socket_client.py` — same + `_sanitize_numeric_types` - `src/deepgram/agent/v1/types/agent_v1settings_agent_context.py`, `src/deepgram/agent/v1/types/agent_v1settings_agent.py`, `src/deepgram/agent/v1/types/agent_v1settings.py`, `src/deepgram/agent/v1/requests/agent_v1settings_agent_context.py`, `src/deepgram/agent/v1/requests/agent_v1settings_agent.py`, `src/deepgram/agent/v1/requests/agent_v1settings.py` — backward-compat patches for the 2026-05-05 Agent Settings schema restructure. These preserve callable `AgentV1SettingsAgent(...)`, keep `AgentV1Settings.agent` accepting both that wrapper and `agent_id` strings, restore the legacy request TypedDict shapes, remap legacy `messages=[...]` / nested `context=AgentV1SettingsAgentContext(messages=[...])` usage into the new `context={"messages": [...]}` wire shape, and keep read-side `obj.messages` access working. -- `src/deepgram/agent/v1/types/agent_v1settings_audio_output.py` — keeps `audio.output.container` typed as `str` for backward compatibility instead of the regenerated enum alias - `src/deepgram/core/query_encoder.py` — coerces Python bools to lowercase `"true"`/`"false"` before they reach `urllib.parse.urlencode` (which would otherwise produce `"True"`/`"False"` via `str()` and break websocket query strings). Only the four `*/connect()` paths call `urlencode`; HTTP raw clients hand params to httpx, which lowercases bools itself, so the patch is a no-op for the HTTP path. Once Fern's websocket codegen normalizes bools (or the spec types these as `boolean` end-to-end), this can be unfrozen. - `tests/wire/test_manage_v1_projects_keys.py` — restored wire coverage for the legacy `CreateKeyV1RequestOneParams` request alias so future regens do not silently drop that compatibility check - `src/deepgram/__init__.py`, `src/deepgram/agent/__init__.py`, `src/deepgram/agent/v1/__init__.py`, `src/deepgram/agent/v1/types/__init__.py`, `src/deepgram/agent/v1/requests/__init__.py`, `src/deepgram/types/__init__.py`, `src/deepgram/requests/__init__.py` — package `__init__.py` files carrying hand-applied legacy alias re-exports for `CreateKeyV1RequestOne`, `AgentV1HistoryContent`, `AgentV1HistoryFunctionCalls`, `AgentV1SettingsAgentContextMessagesItemContent`, `AgentV1SettingsAgentContextMessagesItemFunctionCalls` (and their `*Params` variants). Fern would otherwise regenerate these and strip the legacy entries. After unfreezing for the next regen and reviewing the new generated content, re-apply the legacy re-exports plus any genuine new entries Fern added. +- `src/deepgram/listen/__init__.py`, `src/deepgram/listen/v2/__init__.py`, `src/deepgram/listen/v2/types/__init__.py` — `__init__.py` files re-exporting the hand-written `ListenV2CloseStreamType` shim. Frozen so Fern won't strip the re-export on regen. Same handling as the package `__init__.py` files above: after unfreezing, re-apply the `ListenV2CloseStreamType` re-export plus any genuine new entries Fern added. ### Prepare repo for regeneration diff --git a/poetry.lock b/poetry.lock index 1ae8548a..8f46bcc6 100644 --- a/poetry.lock +++ b/poetry.lock @@ -15,132 +15,132 @@ files = [ [[package]] name = "aiohttp" -version = "3.14.0" +version = "3.14.1" description = "Async http client/server framework (asyncio)" optional = true python-versions = ">=3.10" groups = ["main"] markers = "extra == \"aiohttp\"" files = [ - {file = "aiohttp-3.14.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:692e409052e7436029bbb32977cd7c5bf806ac5fa4085b973996785ffadad33c"}, - {file = "aiohttp-3.14.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:40af7ebe53c7990e110dc4ad03566b12c3ac996254298a3d39046dd69cfcb2c2"}, - {file = "aiohttp-3.14.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02cb2ffbb7da32f82e21ad9952669c45bd88a80e0878264c2f59fe1c6fb2badd"}, - {file = "aiohttp-3.14.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2e2514cb7195f6d7c219339635bea71ae47d1569b051300d32df9dcfabcdb869"}, - {file = "aiohttp-3.14.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:30e8b7eeb42d02c120ca90d6c6e076a221a16b70a6dac9ae44c7ab5104cc7fe4"}, - {file = "aiohttp-3.14.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:63e38be0d75a654deaa06be32fb4cab883a4222940be1d05861b6717679cbadb"}, - {file = "aiohttp-3.14.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1210d4c87cc00128160c7384ab41877a701295b97cffa6362f908a49b6e8a7ca"}, - {file = "aiohttp-3.14.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1a78a77366ed158a0a54b076990e575d7b7cdb728cbfd02711eadab150f2269f"}, - {file = "aiohttp-3.14.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f4d2038c64f36df96cfd3fa0937910e231eafbf897e70a06c155a817bb632fa6"}, - {file = "aiohttp-3.14.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4714c70067a08b604d0bf3bc4dfdf82e52944afab41d0428d460862763d2f79b"}, - {file = "aiohttp-3.14.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:f79bfd2847513a7ac801bbafd1de02348a37926ac439eeb4bfe96fcff4eada15"}, - {file = "aiohttp-3.14.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:25e9f1d2465a210d60edb64d7b204a147e85d4c194eecef3d1604fb5ace678ce"}, - {file = "aiohttp-3.14.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:b5314743ebe926c2fda35d0a298c565c885505f6635c2a30936363404cf274a7"}, - {file = "aiohttp-3.14.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:28eee8de1d69711c53116df8202f1c2aa0e3f80ef912a88fc18d159d53e7110b"}, - {file = "aiohttp-3.14.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:89ed35666c95d3efe1955056afcde09e62a57a34e2a4398b17f9f6c1564f0b25"}, - {file = "aiohttp-3.14.0-cp310-cp310-win32.whl", hash = "sha256:5e4646e9a6af29af354204011bf5769cb0276ec5b64653e42f90b3e13845169f"}, - {file = "aiohttp-3.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:22a8d06f204e0518a586d770032db3c7043c9ba3693081b3e3ad425e1458d594"}, - {file = "aiohttp-3.14.0-cp310-cp310-win_arm64.whl", hash = "sha256:4acfc34bd4d3c58754fc9f22ff1b5e92aabce68f3d4bf7b71a0b732d9bceb78a"}, - {file = "aiohttp-3.14.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:54bf3522d6f7351e55f89a62d5c2bf138ad557b031670266c5df604ae88e0b5a"}, - {file = "aiohttp-3.14.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0746d9fb0ac4fdef643a84494efe3f06d50335dd8c7a530228b86448aae0a803"}, - {file = "aiohttp-3.14.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9f3a96b6d39a4872222beee72e1df41d2ff886ae96152cf3e757ef8c5673ef0e"}, - {file = "aiohttp-3.14.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d336820adbb914debbc90a1d8c1bfc4bea55996aecf64866a989d35d1f9fd903"}, - {file = "aiohttp-3.14.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:71b2604c9bfc1b115547d63a094d5244b3f02799833513a99a68aaa7b167c4cb"}, - {file = "aiohttp-3.14.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:610d68800435903e303ca0542b9d3e4eb72a12ff33a6d471a070c1d81eebd3c2"}, - {file = "aiohttp-3.14.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:514db9a79337068981ee2137310283a07b4b885c584991097a91a4da419bcb81"}, - {file = "aiohttp-3.14.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c452d17eeb95d563fc8b936f3050301dbd1d268126c4632d8b70ede9696202ee"}, - {file = "aiohttp-3.14.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ed94a81506e3d1bdbad5108f497a58f2a2354aedb4ca314d5326f07d1fd1ac2d"}, - {file = "aiohttp-3.14.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1394dce36e0f0d260ac0b555a654de19cb989f3c1b8bdd24f505314dfea18a00"}, - {file = "aiohttp-3.14.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:d1467d1e7b48a73ca7237e0ee4335f3d02b923dbc27b82fd254bc301c97d4026"}, - {file = "aiohttp-3.14.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:6a5f3532125233c261cf61f32df4059cfcf482eb793c7d3db8452e3142028b86"}, - {file = "aiohttp-3.14.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:3ea81eb518a2ecb319d8ec6d1424a37c773f6634bd87d6985eb606b2faac419f"}, - {file = "aiohttp-3.14.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:32e735c3182de7b64f6941a4ede48b38c7f47d9437bd615dd30b5bda8fa1bc93"}, - {file = "aiohttp-3.14.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c21ca9a1c63d4509158f478aeb9d02914dcc52adc68d1bc9dee2452284ee5996"}, - {file = "aiohttp-3.14.0-cp311-cp311-win32.whl", hash = "sha256:19ca5fc84130675ba11c6ca5c7da5cb65f7bf8a32cdd2b616bf49cd334688aae"}, - {file = "aiohttp-3.14.0-cp311-cp311-win_amd64.whl", hash = "sha256:d488e6e9d3bb8ba5ae7066d5be885ae9670eba021b8c6ccb9a3a568e6b19d6e5"}, - {file = "aiohttp-3.14.0-cp311-cp311-win_arm64.whl", hash = "sha256:8b93618102caf12801638a01a2b478a55410ddd71bd41cfaf6f707953a49ac43"}, - {file = "aiohttp-3.14.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b29518c9c2ec7e373e68259206a137c7f4f5439c58baaec4b5ab3ab799850a4e"}, - {file = "aiohttp-3.14.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:dbec68ce61b64cb73cab4d33df9433427b1713c8bcccb181dce695c1b6f8e87c"}, - {file = "aiohttp-3.14.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3cdf534aa455593e589302990c5097aa5c92c06c4262a20da22934f9186a5fff"}, - {file = "aiohttp-3.14.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cb6c657104393b5fbff01a5f59b2023db74058a8077d94475d6c25d03882a108"}, - {file = "aiohttp-3.14.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:46fbbec4e4fab7428d4396a3823f9320e4560aa3113b89eeebce712c27c9ed5a"}, - {file = "aiohttp-3.14.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2c2c7e05dd5335b298085abf45ddf98673934c3ee1c083d0b9ea13d4186ad500"}, - {file = "aiohttp-3.14.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3c7139100fbaae76515b73051d8f0aa3a3ff02e415eec8a8eee8e2223d9ba955"}, - {file = "aiohttp-3.14.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:78d6f9286a629ce52728430afe18f8ed2b6c39a1fddb3802d7244b9983910ad2"}, - {file = "aiohttp-3.14.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cc3c3e12cdaeb92d7dcf13db00e9f6b1956b910e47256e696df1cfa946d02159"}, - {file = "aiohttp-3.14.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4d6a998191f5ebe3b8c28463ff72bc030250008b3193c402464efadd08b5ca02"}, - {file = "aiohttp-3.14.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:0fc2b75ae8d169d853be2862d960be8550da6c5c65711d5476407eb3fdb006bd"}, - {file = "aiohttp-3.14.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:16eee56bcc72d04600bc56c1759982c2385ec0b41d3fd3521f836bf64a0957ef"}, - {file = "aiohttp-3.14.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:5a2e7ca615c3ddc15b82687e05a624e5f5cba3f1d6c20cb81172d70ea498451e"}, - {file = "aiohttp-3.14.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:f0b7b8bbbec3ce9467ee0ebe334622fd90624f593edd3136c567811453fc4fae"}, - {file = "aiohttp-3.14.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5ba10966d4f03dd96a14365be4b8e37c327c76f11c3ca867116966cdd9f98066"}, - {file = "aiohttp-3.14.0-cp312-cp312-win32.whl", hash = "sha256:101df7779c80c0636014a6b2c6642acd3efb5b355d48347c9d7dfb720aee9430"}, - {file = "aiohttp-3.14.0-cp312-cp312-win_amd64.whl", hash = "sha256:b0a5747586d4467efd1f932710b269131c9717a872dce082cd92a00c1c13123a"}, - {file = "aiohttp-3.14.0-cp312-cp312-win_arm64.whl", hash = "sha256:5f1c5be60add78fabb4aacd13c5a348ae79d2fcbfc7fa78da8f1eb192273b370"}, - {file = "aiohttp-3.14.0-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:25400d710641a8040bf022a8a99f579e581ffa1c5bd42c33255d7d6f3957c127"}, - {file = "aiohttp-3.14.0-cp313-cp313-android_21_x86_64.whl", hash = "sha256:c5492b9929826e07cc3fcb9739ae87aab05dff6b5e67a9b73fd1700c6d008981"}, - {file = "aiohttp-3.14.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:3366751d68d237c621264233a32f3078bbc21b7904ab90a77e03d21390c742c6"}, - {file = "aiohttp-3.14.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:57ea07d28695a7a40304d42251892a8df765e5588c10ee32afeddcd5df33c0a2"}, - {file = "aiohttp-3.14.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:076cb014191ae2e65d949e1ad01f1dcfe33e32789b5172510f3e79c79fc04d50"}, - {file = "aiohttp-3.14.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2f3fc37054564dee64a855b5b092d87ec35dcddfaabf7dacb1c8a2b1f83dc0a9"}, - {file = "aiohttp-3.14.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8fcaef74d2ab0f607d7ff85a0d15e21bb5a258c4a58df1908396eb50d7f4ed3c"}, - {file = "aiohttp-3.14.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e4c01b0bfc6209590960e68eac083cd22d5d87c21f974dd6208cafa5d3542bc8"}, - {file = "aiohttp-3.14.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f12eb7896e81caf403a2b18c9406426f1207361e7239c057ab29c076d4257e83"}, - {file = "aiohttp-3.14.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6c79a044cacf360ec46738d863d2f41c9300d2a06ef4a7402ea0df306a350e61"}, - {file = "aiohttp-3.14.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:85e0675f47be4eff0636bf88c02140ea89168ae0df3ff1f3f464e9de9610d277"}, - {file = "aiohttp-3.14.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7b33e751cab03fdc960095b1e326cb5a03f5ee577d6ded59f3d1c100f8668882"}, - {file = "aiohttp-3.14.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:26d9224c6dd7f5c749aba4f61315a894601448b28d94d12f4dea0903e26d2096"}, - {file = "aiohttp-3.14.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6281aecdf2732940f4fe06bd6adec5ae4d59b78b080b8e3a6b81467301010988"}, - {file = "aiohttp-3.14.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:23e8314e7aed8576fbe33314d218bd81447a3adbc91dc36f1163bf583cd3084c"}, - {file = "aiohttp-3.14.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:3b54fbff46127aeafdd764cecd0d99fa2f24a0e37ea5c18a7c3a4ac450df1db3"}, - {file = "aiohttp-3.14.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b27d89af91a555f58e08e4902dbcbc48862fd40095720ca705990476bd93b7ac"}, - {file = "aiohttp-3.14.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:25d2326a4967bf705a9f9913a13005e93b6020ad8a9f6bd6bd78850d5171332e"}, - {file = "aiohttp-3.14.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:a1d209375c503472b3c0a340cdf3c55fcd82e84b46dda7caeaced59faba373ec"}, - {file = "aiohttp-3.14.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:666c7c5036df57b693026398b69b41874a1931ac5b3485fd910e57bfac253869"}, - {file = "aiohttp-3.14.0-cp313-cp313-win32.whl", hash = "sha256:23f094a1ef64823fd35854ddf5c7a80a078162f37f9d2f7c6142b51a6affa456"}, - {file = "aiohttp-3.14.0-cp313-cp313-win_amd64.whl", hash = "sha256:e03abdaa17d553f17e1d1d06bb266b3970106c78051d06795723e748d8e49d11"}, - {file = "aiohttp-3.14.0-cp313-cp313-win_arm64.whl", hash = "sha256:acdb400538cf4769543548bb5d1eb23d39bed4f96554a6078cb728c7cb2c268b"}, - {file = "aiohttp-3.14.0-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:363ef9e91014e7891679bfb2ac0a7c6ea93435dbbfd10ecf41b9f06fcf506c5f"}, - {file = "aiohttp-3.14.0-cp314-cp314-android_24_x86_64.whl", hash = "sha256:884a4edbdad77be9d0ef36142c8b504351b170df0bf62b51e784fadabf311c42"}, - {file = "aiohttp-3.14.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:70ea956f6cc4a37620966b56c2e205d88ca3e6d85ec063277e414b1035cddad3"}, - {file = "aiohttp-3.14.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:ea3b9806c89f61da22fddf1f12dd524fb368e5e28f1261fbdafe5c3cd8ce893b"}, - {file = "aiohttp-3.14.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:a071be341c2bd9b0188e62d173509f024e0a35b1c342c53c50f8daaeda8c3bd8"}, - {file = "aiohttp-3.14.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:198cfe61bf253b19da1fb3e0fa122249dc4f14c12709493fed8054aa0411cc76"}, - {file = "aiohttp-3.14.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:9dc203d6ce6b9106d54e2a93f41dfdfebfbca2d99962ba503bfd3e5921a6549e"}, - {file = "aiohttp-3.14.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9e19d17ab02bf16832a2c8c0d55a486792c5b1645665652ee9531aebcc30cb72"}, - {file = "aiohttp-3.14.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d925fba0c14d5b498a8028b0107beebdfd16c5d48d702ff54f879cb017aaaca3"}, - {file = "aiohttp-3.14.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d33e61021222ce7f9792bcac870d6f58d8adfceda33ab857b01264f4560f2c5f"}, - {file = "aiohttp-3.14.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:44eca38755d0105bb32f47d085f5dd449846a449e1245fc105889e3279dcf8e3"}, - {file = "aiohttp-3.14.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f13087e06f68fea4941c21a0c541c00553aa16e4f8fd7bbe2b198df761e964d6"}, - {file = "aiohttp-3.14.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ff82be7f1ef73634cb77890a770743239bc3d487b848669be1c599889336dc0a"}, - {file = "aiohttp-3.14.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a150c0875ac8fd87f1c398650841308a30d65facf7416b12dbdb9cfdcbe5a48c"}, - {file = "aiohttp-3.14.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:edc01ea4e1ec5a1649a28866262bf24195889ff7b27bdd947029a6086741de9b"}, - {file = "aiohttp-3.14.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:540632bf882ff8fc88f2e1697be0761578e89e0d79fb4a8a6d65dc5da7e729d4"}, - {file = "aiohttp-3.14.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:860a86bc2c80237f5dff52edcf427e10a8d8352271fd84845429a3e60199e02c"}, - {file = "aiohttp-3.14.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:5cbd50e6a50d6b99283a826b18cbdebf65b0797689a7535cb0e9dd37be0f63c3"}, - {file = "aiohttp-3.14.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:20144819e99db593e22bbd2f3f2691a5e149f879142d6b8670254708853ff4fb"}, - {file = "aiohttp-3.14.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:26b6d79aa54cb4ed50cc7d41ed14e99e0f1fc8e7c2d42f2e05b37aea897b2b52"}, - {file = "aiohttp-3.14.0-cp314-cp314-win32.whl", hash = "sha256:106ed074a856f3e21d186b8579e2c8afb6da598e267cdaab01059e13db2fc44d"}, - {file = "aiohttp-3.14.0-cp314-cp314-win_amd64.whl", hash = "sha256:4f770846edae8f00ecc57af825bce811f787f87a7dcf0e90d191790efe5b31f7"}, - {file = "aiohttp-3.14.0-cp314-cp314-win_arm64.whl", hash = "sha256:acf1581c4f21ed4b80a2dded504d87b055a071a84d5737ea966435f768275ac6"}, - {file = "aiohttp-3.14.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:6aa1a40f9cbb3da9f80714c5966b8946c21e6a2530d809b9498b33161e3c8733"}, - {file = "aiohttp-3.14.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:b62af5a8cc96a194eaa01a9ed7b34a3ffa58d3d8daaa1a0d7a749353ad12d228"}, - {file = "aiohttp-3.14.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6eb63b1417efaf7d1002a6ad034a40d44376afcc16508a57f8e74b49ad26a095"}, - {file = "aiohttp-3.14.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c20b9ad156a79eb97be5cf9e069eec01d2f0dc8472ffbd75299a8b2d4c2cbbde"}, - {file = "aiohttp-3.14.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:40ae7b0642c25632c7eabc4a04754012691864d2a1b93becf7cddb76027b838a"}, - {file = "aiohttp-3.14.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:95f5217e76a046b9f228a101717ef8d42b1eb3d9d196d15202db5bf41df88936"}, - {file = "aiohttp-3.14.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1a4a9f17e85b80878c176695c1998c790e83731d8271881e5d356488652a1f9e"}, - {file = "aiohttp-3.14.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:145262119b07d7f95abc1839add35ba2bfc84551d4b4660ca11542c0b215455b"}, - {file = "aiohttp-3.14.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:49a33ded29b0b2fa7a367a02cf0fb89af602bb87542a16177ec8ce1c9c51d12a"}, - {file = "aiohttp-3.14.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:2cc736a9c9fc2bc4dd71fd404815741b6573df27c3f985948ec4076989ac57de"}, - {file = "aiohttp-3.14.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:b4141a3e5342ee3053a9cab54d25b64ed28289c1041e4c54b3d99839314d90ce"}, - {file = "aiohttp-3.14.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:e30871b2d58996cb81aac52d2b1d15ac05257131ef0f90f18c2115a380fbfe7c"}, - {file = "aiohttp-3.14.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:667b881d083ccae3900ea5a241e17e5007ca78844c53ed389bb63d48f729d9c7"}, - {file = "aiohttp-3.14.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:b584dfe615d151e9b8f0a8ecb3aee6147f2927ec5b95ba25fe621f5377510928"}, - {file = "aiohttp-3.14.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6199707cc40e0e9cd39c36fbc97bec416c704e1d0ddce03412bb3b3e6a90ccd0"}, - {file = "aiohttp-3.14.0-cp314-cp314t-win32.whl", hash = "sha256:a8d93334d4961c9d566b1f046c81dee475b7c21eb730728d38237bfa70d1c8e6"}, - {file = "aiohttp-3.14.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2d2ffe9b614f50f069068b3b52e73414e4107fc10b7efc939a76acff9251fdd2"}, - {file = "aiohttp-3.14.0-cp314-cp314t-win_arm64.whl", hash = "sha256:7a3fc4358e65826c515350f199c210de747cf669998211b1ee6c2e46de364b24"}, - {file = "aiohttp-3.14.0.tar.gz", hash = "sha256:2882de819734c715fd1b9c11c97e09fa020d14438203d1d354d8ed1702791c9b"}, + {file = "aiohttp-3.14.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8f6bb621e5863cfe8fe5ff5468002d200ec31f30f1280b259dc505b02595099e"}, + {file = "aiohttp-3.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4f7215cb3933784f79ed20e5f050e15984f390424339b22375d5a53c933a0491"}, + {file = "aiohttp-3.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d9d4e294455b23a68c9b8f042d0e8e377a265bcb15332753695f6e5b6819e0ce"}, + {file = "aiohttp-3.14.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b238af795833d5731d049d82bc84b768ae6f8f97f0495963b3ed9935c5901cc3"}, + {file = "aiohttp-3.14.1-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e4e5e0ae56914ecdbf446493addefc0159053dd53962cef37d7839f37f73d505"}, + {file = "aiohttp-3.14.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:092e4ce3619a7c6dee52a6bdabda973d9b34b66781f840ce93c7e0cec30cf521"}, + {file = "aiohttp-3.14.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bb33777ea21e8b7ecde0e6fc84f598be0a1192eab1a63bc746d75aa75d38e7bd"}, + {file = "aiohttp-3.14.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:23119f8fd4f5d16902ed459b63b100bcd269628075162bddac56cc7b5273b3fb"}, + {file = "aiohttp-3.14.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:57fc6745a4b7d0f5a9eb4f40a69718be6c0bc1b8368cc9fe89e90118719f4f42"}, + {file = "aiohttp-3.14.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6fd35beba67c4183b09375c5fff9accb47524191a244a99f95fd4472f5402c2b"}, + {file = "aiohttp-3.14.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:672b9d65f42eb877f5c3f234a4547e4e1a226ca8c2eed879bb34670a0ce51192"}, + {file = "aiohttp-3.14.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:24ba13339fed9251d9b1a1bec8c7ab84c0d1675d79d33501e11f94f8b9a84e05"}, + {file = "aiohttp-3.14.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:94da27378da0610e341c4d30de29a191672683cc82b8f9556e8f7c7212a020fe"}, + {file = "aiohttp-3.14.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:52cdac9432d8b4a719f35094a818d95adcae0f0b4fe9b9b921909e0c87de9e7d"}, + {file = "aiohttp-3.14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:672ac254412a24d0d0cf00a9e6c238877e4be5e5fa2d188832c1244f45f31966"}, + {file = "aiohttp-3.14.1-cp310-cp310-win32.whl", hash = "sha256:2fe3607e71acc6ebb0ec8e492a247bf7a291226192dc0084236dfc12478916f6"}, + {file = "aiohttp-3.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:30099eda75a53c32efb0920e9c33c195314d2cc1c680fbfd30894932ac5f27df"}, + {file = "aiohttp-3.14.1-cp310-cp310-win_arm64.whl", hash = "sha256:5a837f49d901f9e368651b676912bff1104ed8c1a83b280bcd7b29adccef5c9c"}, + {file = "aiohttp-3.14.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:aa00140699487bd435fde4342d85c94cb256b7cd3a5b9c3396c67f19922afda2"}, + {file = "aiohttp-3.14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1c1af67559445498b502030c35c59db59966f47041ca9de5b4e707f86bd10b5f"}, + {file = "aiohttp-3.14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d44ec478e713ee7f29b439f7eb8dc2b9d4079e11ae114d2c2ac3d5daf30516c8"}, + {file = "aiohttp-3.14.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d3b1a184a9a8f548a6b73f1e26b96b052193e4b3175ed7342aaf1151a1f00a04"}, + {file = "aiohttp-3.14.1-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5f2504bc0322437c9a1ff6d3333ca56c7477b727c995f036b976ae17b98372c8"}, + {file = "aiohttp-3.14.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:73f05ea02013e02512c3bf42714f1208c57168c779cc6fe23516e4543089d0a6"}, + {file = "aiohttp-3.14.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:797457503c2d426bee06eef808d07b31ede30b65e054444e7de64cad0061b7af"}, + {file = "aiohttp-3.14.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b821a1f7dedf7e37450654e620038ac3b2e81e8fa6ea269337e97101978ec730"}, + {file = "aiohttp-3.14.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4cd96b5ba05d67ed0cf00b5b405c8cd99586d8e3481e8ee0a831057591af7621"}, + {file = "aiohttp-3.14.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d459b98a932296c6f0e94f87511a0b1b90a8a02c30a50e60a297619cd5a58ee"}, + {file = "aiohttp-3.14.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:764457a7be60825fb770a644852ff717bcbb5042f189f2bd16df61a81b3f6573"}, + {file = "aiohttp-3.14.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f7a16ef45b081454ef844502d87a848876c490c4cb5c650c230f6ec79ed2c1e7"}, + {file = "aiohttp-3.14.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:2fbc3ed048b3475b9f0cbcb9978e9d2d3511acd91ead203af26ed9f0056004cf"}, + {file = "aiohttp-3.14.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:bedb0cd073cc2dc035e30aeb99444389d3cd2113afe4ef9fcd23d439f5bade85"}, + {file = "aiohttp-3.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b6feea921016eb3d4e04d65fc4e9ca402d1a3801f562aef94989f54694917af3"}, + {file = "aiohttp-3.14.1-cp311-cp311-win32.whl", hash = "sha256:313701e488100074ce99850404ee36e741abf6330179fec908a1944ecf570126"}, + {file = "aiohttp-3.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:03ab4530fdcb3a543a122ba4b65ac9919da9fe9f78a03d328a6e38ff962f7aa5"}, + {file = "aiohttp-3.14.1-cp311-cp311-win_arm64.whl", hash = "sha256:486f7d16ed54c39c2cbd7ca71fd8ba2b8bb7860df65bd7b6ed640bab96a38a8b"}, + {file = "aiohttp-3.14.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d35143e27778b4bb0fb189562d7f275bff79c62ab8e98459717c0ea617ff2480"}, + {file = "aiohttp-3.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bcfb80a2cc36fba2534e5e5b5264dc7ae6fcd9bf15256da3e53d2f499e6fa29d"}, + {file = "aiohttp-3.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:27fd7c91e51729b4f7e1577865fa6d34c9adccbc39aabe9000285b48af9f0ec2"}, + {file = "aiohttp-3.14.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:64c567bf9eaf664280116a8688f63016e6b32db2505908e2bdaca1b6438142f2"}, + {file = "aiohttp-3.14.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f5e6ff2bdbb8f4cd3fbe41f99e25bbcd58e3bf9f13d3dd31a11e7917251cc77a"}, + {file = "aiohttp-3.14.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2f73e01dc37122325caf079982621262f96d74823c179038a82fddfc50359264"}, + {file = "aiohttp-3.14.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bb2c0c80d431c0d03f2c7dbf125150fedd4f0de17366a7ca33f7ccb822391842"}, + {file = "aiohttp-3.14.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3e6fc1a85fa7194a1a7d19f44e8609180f4a8eb5fa4c7ed8b4355f080fad235c"}, + {file = "aiohttp-3.14.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:686b6c0d3911ec387b444ddf5dc62fb7f7c0a7d5186a7861626496a5ab4aff95"}, + {file = "aiohttp-3.14.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c6fa4dc7ad6f8109c70bb1499e589f76b0b792baf39f9b017eb92c8a81d0a199"}, + {file = "aiohttp-3.14.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:87a5eea1b2a5e21e1ebdbb33ad4165359189327e63fc4e4894693e7f821ac817"}, + {file = "aiohttp-3.14.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1c1421eb01d4fd608d88cc8290211d177a58532b55ad94076fb349c5bf467f0a"}, + {file = "aiohttp-3.14.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:34b257ec41345c1e8f2df68fa908a7952f5de932723871eb633ecbbff396c9a4"}, + {file = "aiohttp-3.14.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:de538791a80e5d862addbc183f70f0158ac9b9bb872bb147f1fd2a683691e087"}, + {file = "aiohttp-3.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6f71173be42d3241d428f760122febb748de0623f44308a6f120d0dd9ec572e3"}, + {file = "aiohttp-3.14.1-cp312-cp312-win32.whl", hash = "sha256:ec8dc383ee57ea3e883477dcca3f11b65d58199f1080acaf4cd6ad9a99698be4"}, + {file = "aiohttp-3.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:2aa92c87868cd13674989f9ee83e5f9f7ea4237589b728048e1f0c8f6caa3271"}, + {file = "aiohttp-3.14.1-cp312-cp312-win_arm64.whl", hash = "sha256:2c840c90759922cb5e6dda94596e079a30fb5a5ba548e7e0dc00574703940847"}, + {file = "aiohttp-3.14.1-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:b3a03285a7f9c7b016324574a6d92a1c895da6b978cb8f1deee3ac72bc6da178"}, + {file = "aiohttp-3.14.1-cp313-cp313-android_21_x86_64.whl", hash = "sha256:2a73f487ab8ef5abbb24b7aa9b73e98eaba9e9e031804ff2416f02eca315ccaf"}, + {file = "aiohttp-3.14.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:915fbb7b41b115192259f8c9ae58f3ddc444d2b5579917270211858e606a4afd"}, + {file = "aiohttp-3.14.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:7fb4bdf95b0561a79f259f9d28fbc109728c5ee7f27aff6391f0ca703a329abe"}, + {file = "aiohttp-3.14.1-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:1b9748363260121d2927704f5d4fc498150669ca3ae93625986ee89c8f80dcd4"}, + {file = "aiohttp-3.14.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:86a6dab78b0e43e2897a3bbe15745aa60dc5423ca437b7b0b164c069bf91b876"}, + {file = "aiohttp-3.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4dfd6e47d3c44c2279907607f73a4240b88c69eb8b90da7e2441a8045dfd21da"}, + {file = "aiohttp-3.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:317acd9f8602858dc7d59679812c376c7f0b97bcbbf16e0d6237f54141d8a8a6"}, + {file = "aiohttp-3.14.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bd869c427324e5cb15195793de951295710db28be7d818247f3097b4ab5d4b96"}, + {file = "aiohttp-3.14.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:93b032b5ec3255473c143627d21a69ac74ae12f7f33974cb587c564d11b1066f"}, + {file = "aiohttp-3.14.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f234b4deb12f3ad59127e037bc57c40c21e45b45282df7d3a55a0f409f595296"}, + {file = "aiohttp-3.14.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9af6779bfb46abf124068327abcdf9ce95c9ef8287a3e8da76ccf2d0f16c28fa"}, + {file = "aiohttp-3.14.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:faccab372e66bc76d5731525e7f1143c922271725b9d38c9f97edcc66266b451"}, + {file = "aiohttp-3.14.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f380468b09d2a81633ee863b0ec5648d364bd17bb8ecfb8c2f387f7ac1faf42c"}, + {file = "aiohttp-3.14.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:97e704dcd26271f5bda3fa07c3ce0fb76d6d3f8659f4baa1a24442cc9ba177ca"}, + {file = "aiohttp-3.14.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:269b76ac5394092b95bc4a098f4fc6c191c083c3bd12775d1e30e663132f6a09"}, + {file = "aiohttp-3.14.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5c0b3e614340c889d575451696374c9d17affd54cd607ca0babed8f8c37b9397"}, + {file = "aiohttp-3.14.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:5663ee9257cfa1add7253a7da3035a02f31b6600ec48261585e1800a81533080"}, + {file = "aiohttp-3.14.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:603a2c834142172ffddc054067f5ec0ca65d57a0aa98a71bc81952573208e345"}, + {file = "aiohttp-3.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:cb21957bb8aca671c1765e32f58164cf0c50e6bf41c0bbbd16da20732ecaf588"}, + {file = "aiohttp-3.14.1-cp313-cp313-win32.whl", hash = "sha256:e509a55f681e6158c20f70f102f9cf61fb20fbc382272bc6d94b7343f2582780"}, + {file = "aiohttp-3.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:1ac8531b638959718e18c2207fbfe297819875da46a740b29dfa29beba64355a"}, + {file = "aiohttp-3.14.1-cp313-cp313-win_arm64.whl", hash = "sha256:250d14af67f6b6a1a4a811049b1afa69d61d617fca6bf33149b3ab1a6dbcf7b8"}, + {file = "aiohttp-3.14.1-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:7c106c26852ca1c2047c6b80384f17100b4e439af276f21ef3d4e2f450ae7e15"}, + {file = "aiohttp-3.14.1-cp314-cp314-android_24_x86_64.whl", hash = "sha256:20205f7f5ade7aaec9f4b500549bbc071b046453aed72f9c06dcab87896a83e8"}, + {file = "aiohttp-3.14.1-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:62a759436b29e677181a9e76bab8b8f689a29cb9c535f45f7c48c9c830d3f8c3"}, + {file = "aiohttp-3.14.1-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:2964cbf553df4d7a57348da44d961d871895fc1ee4e8c322b2a95612c7b17fba"}, + {file = "aiohttp-3.14.1-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:237651caadc3a59badd39319c54642b5299e9cc98a3a194310e55d5bb9f5e397"}, + {file = "aiohttp-3.14.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:896e12dfdbbab9d8f7e16d2b28c6769a60126fa92095d1ebf9473d02593a2448"}, + {file = "aiohttp-3.14.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d03f281ed22579314ba00821ce20115a7c0ac430660b4cc05704a3f818b3e004"}, + {file = "aiohttp-3.14.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:07eabb979d236335fed927e137a928c9adfb7df3b9ec7aa31726f133a62be983"}, + {file = "aiohttp-3.14.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4fe1f1087cbadb280b5e1bb054a4f00d1423c74d6626c5e48400d871d34ecefe"}, + {file = "aiohttp-3.14.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:367a9314fdc79dab0fac96e216cb41dd73c85bdca85306ce8999118ba7e0f333"}, + {file = "aiohttp-3.14.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a24f677ebe83749039e7bdf862ff0bbb16818ae4193d4ef96505e269375bcce0"}, + {file = "aiohttp-3.14.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c83afe0ba876be7e943d2e0ba645809ad441575d2840c895c21ee5de93b9377a"}, + {file = "aiohttp-3.14.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:634e385930fb6d2d479cf3aa66515955863b77a5e3c2b5894ca259a25b308602"}, + {file = "aiohttp-3.14.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:eeea07c4397bbc57719c4eed8f9c284874d4f175f9b6d57f7a1546b976d455ca"}, + {file = "aiohttp-3.14.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:335c0cc3e3545ce98dcb9cfcb836f40c3411f43fa03dab757597d80c89af8a35"}, + {file = "aiohttp-3.14.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:ae6be797afdef264e8a84864a85b196ca06045586481b3df8a967322fd2fa844"}, + {file = "aiohttp-3.14.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:8560b4d712474335d08907db7973f71912d3a9a8f1dee992ec06b5d2fe359496"}, + {file = "aiohttp-3.14.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7edd08e0a5deb1e8564a2fcd8f4561014a3f05252334671bbf55ddd47db0e5"}, + {file = "aiohttp-3.14.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:b6ff7fcee63287ae57b5df3e4f5957ce032122802509246dec1a5bcc55904c95"}, + {file = "aiohttp-3.14.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6ffbb2f4ec1ceaff7e07d43922954da26b223d188bf30658e561b98e23089444"}, + {file = "aiohttp-3.14.1-cp314-cp314-win32.whl", hash = "sha256:a9875b46d910cff3ea2f5962f9d266b465459fe634e22556ab9bd6fc1192eea0"}, + {file = "aiohttp-3.14.1-cp314-cp314-win_amd64.whl", hash = "sha256:af8b4b81a960eeaf1234971ac3cd0ba5901f3cd42eae42a46b4d089a8b492719"}, + {file = "aiohttp-3.14.1-cp314-cp314-win_arm64.whl", hash = "sha256:cf4491381b1b57425c315a56a439251b1bdac07b2275f19a8c44bc57744532ec"}, + {file = "aiohttp-3.14.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:819c054312f1af92947e6a55883d1b66feefab11531a7fc45e0fb9b63880b5c2"}, + {file = "aiohttp-3.14.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:10ee9c1753a8f706345b22496c79fbddb5be0599e0823f3738b1534058e25340"}, + {file = "aiohttp-3.14.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1601cc37baf5750ccacae618ec2daf020769581695550e3b654a911f859c563d"}, + {file = "aiohttp-3.14.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4d6e0ac9da31c9c04c84e1c0182ad8d6df35965a85cae29cd71d089621b3ae94"}, + {file = "aiohttp-3.14.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9e8f2d660c350b3d0e259c7a7e3d9b7fc8b41210cbcc3d4a7076ff0a5e5c2fdc"}, + {file = "aiohttp-3.14.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4691802dda97be727f79d86818acaad7eb8e9252626a1d6b519fedbb92d5e251"}, + {file = "aiohttp-3.14.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c389c482a7e9b9dc3ee2701ac46c4125297a3818875b9c305ddb603c04828fd1"}, + {file = "aiohttp-3.14.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fc0cacab7ba4e56f0f81c82a98c09bed2f39c940107b03a34b168bdf7597edd3"}, + {file = "aiohttp-3.14.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:979ed4717f59b8bb12e3963378fa285d93d367e15bcd66c721311826d3c44a6c"}, + {file = "aiohttp-3.14.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:38e1e7daaea81df51c952e18483f323d878499a1e2bfe564790e0f9701d6f203"}, + {file = "aiohttp-3.14.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:4132e72c608fe9fecb8f409113567605915b83e9bdd3ea56538d2f9cd35002f1"}, + {file = "aiohttp-3.14.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:eefd9cc9b6d4a2db5f00a26bc3e4f9acf71926a6ec557cd56c9c6f27c290b665"}, + {file = "aiohttp-3.14.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:b165790117eea512d7f3fb22f1f6dad3d55a7189571993eb015591c1401276d1"}, + {file = "aiohttp-3.14.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:ed09c7eb1c391271c2ed0314a51903e72a3acb653d5ccfc264cdf3ef11f8269d"}, + {file = "aiohttp-3.14.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:99abd37084b82f5830c635fddd0b4993b9742a66eb746dacf433c8590e8f9e3c"}, + {file = "aiohttp-3.14.1-cp314-cp314t-win32.whl", hash = "sha256:47ddf841cdecc810749921d25606dee45857d12d2ad5ddb7b5bd7eab12e4b365"}, + {file = "aiohttp-3.14.1-cp314-cp314t-win_amd64.whl", hash = "sha256:5e78b522b7a6e27e0b25d19b247b75039ac4c94f99823e3c9e53ae1603a9f7e9"}, + {file = "aiohttp-3.14.1-cp314-cp314t-win_arm64.whl", hash = "sha256:90d53f1609c29ccc2193945ef732428382a28f78d0456ae4d3daf0d48b74f0f6"}, + {file = "aiohttp-3.14.1.tar.gz", hash = "sha256:307f2cff90a764d329e77040603fa032db89c5c24fdad50c4c15334cba744035"}, ] [package.dependencies] @@ -188,14 +188,14 @@ files = [ [[package]] name = "anyio" -version = "4.13.0" +version = "4.14.0" description = "High-level concurrency and networking framework on top of asyncio or Trio" optional = false python-versions = ">=3.10" groups = ["main"] files = [ - {file = "anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708"}, - {file = "anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc"}, + {file = "anyio-4.14.0-py3-none-any.whl", hash = "sha256:dd9b7a2a9799ed6552fde617b2c5df02b7fdd7d88392fc48101e51bae46164d9"}, + {file = "anyio-4.14.0.tar.gz", hash = "sha256:b47c1f9ccf73e67021df785332508f99379c68fa7d0684e8e3492cb1d4b23f89"}, ] [package.dependencies] @@ -213,7 +213,7 @@ description = "Timeout context manager for asyncio programs" optional = true python-versions = ">=3.8" groups = ["main"] -markers = "extra == \"aiohttp\" and python_version == \"3.10\"" +markers = "python_version == \"3.10\" and extra == \"aiohttp\"" files = [ {file = "async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c"}, {file = "async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3"}, @@ -247,14 +247,14 @@ files = [ [[package]] name = "certifi" -version = "2026.5.20" +version = "2026.6.17" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.7" groups = ["main", "dev"] files = [ - {file = "certifi-2026.5.20-py3-none-any.whl", hash = "sha256:3c52e209ba0a4ad7aebe60436a4ab349c39e1e602e8c134221e546902ad25897"}, - {file = "certifi-2026.5.20.tar.gz", hash = "sha256:69dea482ab64caa7b9f6aba1c6bf48bb6a5448d1c0f1b17ab42ad8c763a5344d"}, + {file = "certifi-2026.6.17-py3-none-any.whl", hash = "sha256:2227dcbaafe0d2f59279d1762ddddc37783ed4354594f194ffc31d20f41fc3db"}, + {file = "certifi-2026.6.17.tar.gz", hash = "sha256:024c88eeec92ca068db80f02b8b07c9cef7b9fe261d1d535abfd5abd6f6af432"}, ] [[package]] @@ -662,14 +662,14 @@ httpx = ">=0.27.0" [[package]] name = "idna" -version = "3.17" +version = "3.18" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.9" groups = ["main", "dev"] files = [ - {file = "idna-3.17-py3-none-any.whl", hash = "sha256:466e48829084efe2548012b855df21540b96f2e20e51bd124c851536556a592c"}, - {file = "idna-3.17.tar.gz", hash = "sha256:5eb0cb53bc467c12eadcf6de83163ad8527cec9416f44b9b61b19caedad2b87f"}, + {file = "idna-3.18-py3-none-any.whl", hash = "sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2"}, + {file = "idna-3.18.tar.gz", hash = "sha256:ffb385a7e039654cef1ab9ef32c6fafe283c0c0467bba1d9029738ce4a14a848"}, ] [package.extras] @@ -1245,14 +1245,14 @@ windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pytest" -version = "9.0.3" +version = "9.1.0" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9"}, - {file = "pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c"}, + {file = "pytest-9.1.0-py3-none-any.whl", hash = "sha256:8ebb0e7888bdf2bdfc602ec51f8f62d50200af37356c74e503c79a94f5c81f32"}, + {file = "pytest-9.1.0.tar.gz", hash = "sha256:41dd9148c08072446394cefd3d79701701335a9f4cae69ba92e39f6c7f5c061c"}, ] [package.dependencies] @@ -1713,4 +1713,4 @@ aiohttp = ["aiohttp", "httpx-aiohttp"] [metadata] lock-version = "2.1" python-versions = "^3.10" -content-hash = "0eff19b2d3dd9b284e02282d753969f4688b017184f65e3296bd869c5a836e1d" +content-hash = "9720594db9f54f592921cb5f95880967c27258ad9fc3ddde5f3f9f174cfb0044" diff --git a/pyproject.toml b/pyproject.toml index 3b5e7639..f18c31ad 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ dynamic = ["version"] [tool.poetry] name = "deepgram-sdk" -version = "7.3.1" +version = "7.3.2" description = "" readme = "README.md" authors = [] @@ -38,9 +38,9 @@ Repository = 'https://github.com/deepgram/deepgram-python-sdk' [tool.poetry.dependencies] python = "^3.10" -aiohttp = { version = ">=3.13.4,<4", optional = true, python = ">=3.9"} +aiohttp = { version = ">=3.14.0,<4", optional = true, python = ">=3.10"} httpx = ">=0.21.2" -httpx-aiohttp = { version = "0.1.8", optional = true, python = ">=3.9"} +httpx-aiohttp = { version = "0.1.8", optional = true, python = ">=3.10"} pydantic = ">= 1.9.2" pydantic-core = ">=2.18.2,<3.0.0" typing_extensions = ">= 4.0.0" @@ -61,6 +61,7 @@ ruff = "==0.11.5" [tool.pytest.ini_options] testpaths = [ "tests" ] asyncio_mode = "auto" +norecursedirs = [ "src" ] markers = [ "aiohttp: tests that require httpx_aiohttp to be installed", ] diff --git a/src/deepgram/__init__.py b/src/deepgram/__init__.py index bab6a21c..3b9390d0 100644 --- a/src/deepgram/__init__.py +++ b/src/deepgram/__init__.py @@ -171,6 +171,7 @@ ListenV2LanguageHint, ListenV2MipOptOut, ListenV2Model, + ListenV2ProfanityFilter, ListenV2SampleRate, ListenV2Tag, OpenAiSpeakProvider, @@ -701,6 +702,7 @@ "ListenV2LanguageHintParams": ".requests", "ListenV2MipOptOut": ".types", "ListenV2Model": ".types", + "ListenV2ProfanityFilter": ".types", "ListenV2SampleRate": ".types", "ListenV2Tag": ".types", "OpenAiSpeakProvider": ".types", @@ -1145,6 +1147,7 @@ def __dir__(): "ListenV2LanguageHintParams", "ListenV2MipOptOut", "ListenV2Model", + "ListenV2ProfanityFilter", "ListenV2SampleRate", "ListenV2Tag", "OpenAiSpeakProvider", diff --git a/src/deepgram/agent/v1/requests/agent_v1settings_agent_context_listen_provider.py b/src/deepgram/agent/v1/requests/agent_v1settings_agent_context_listen_provider.py index 9f63b22a..98381492 100644 --- a/src/deepgram/agent/v1/requests/agent_v1settings_agent_context_listen_provider.py +++ b/src/deepgram/agent/v1/requests/agent_v1settings_agent_context_listen_provider.py @@ -5,7 +5,6 @@ import typing import typing_extensions -from ....requests.deepgram_listen_provider_v2language_hint import DeepgramListenProviderV2LanguageHintParams class AgentV1SettingsAgentContextListenProvider_V1Params(typing_extensions.TypedDict): @@ -21,7 +20,7 @@ class AgentV1SettingsAgentContextListenProvider_V2Params(typing_extensions.Typed version: typing.Literal["v2"] type: typing.Literal["deepgram"] model: str - language_hint: typing_extensions.NotRequired[DeepgramListenProviderV2LanguageHintParams] + language_hints: typing_extensions.NotRequired[typing.Sequence[str]] keyterms: typing_extensions.NotRequired[typing.Sequence[str]] diff --git a/src/deepgram/agent/v1/requests/agent_v1settings_agent_listen_provider.py b/src/deepgram/agent/v1/requests/agent_v1settings_agent_listen_provider.py index 4f16ce88..a0ddf06b 100644 --- a/src/deepgram/agent/v1/requests/agent_v1settings_agent_listen_provider.py +++ b/src/deepgram/agent/v1/requests/agent_v1settings_agent_listen_provider.py @@ -5,7 +5,6 @@ import typing import typing_extensions -from ....requests.deepgram_listen_provider_v2language_hint import DeepgramListenProviderV2LanguageHintParams class AgentV1SettingsAgentListenProvider_V1Params(typing_extensions.TypedDict): @@ -21,7 +20,7 @@ class AgentV1SettingsAgentListenProvider_V2Params(typing_extensions.TypedDict): version: typing.Literal["v2"] type: typing.Literal["deepgram"] model: str - language_hint: typing_extensions.NotRequired[DeepgramListenProviderV2LanguageHintParams] + language_hints: typing_extensions.NotRequired[typing.Sequence[str]] keyterms: typing_extensions.NotRequired[typing.Sequence[str]] diff --git a/src/deepgram/agent/v1/types/agent_v1settings_agent_context_listen_provider.py b/src/deepgram/agent/v1/types/agent_v1settings_agent_context_listen_provider.py index 0f8de520..229a54dd 100644 --- a/src/deepgram/agent/v1/types/agent_v1settings_agent_context_listen_provider.py +++ b/src/deepgram/agent/v1/types/agent_v1settings_agent_context_listen_provider.py @@ -8,7 +8,6 @@ import typing_extensions from ....core.pydantic_utilities import IS_PYDANTIC_V2 from ....core.unchecked_base_model import UncheckedBaseModel, UnionMetadata -from ....types.deepgram_listen_provider_v2language_hint import DeepgramListenProviderV2LanguageHint class AgentV1SettingsAgentContextListenProvider_V1(UncheckedBaseModel): @@ -33,9 +32,41 @@ class AgentV1SettingsAgentContextListenProvider_V2(UncheckedBaseModel): version: typing.Literal["v2"] = "v2" type: typing.Literal["deepgram"] = "deepgram" model: str - language_hint: typing.Optional[DeepgramListenProviderV2LanguageHint] = None + language_hints: typing.Optional[typing.List[str]] = None + language_hint: typing.Optional[typing.Union[str, typing.List[str]]] = pydantic.Field(default=None, exclude=True) keyterms: typing.Optional[typing.List[str]] = None + # Backward-compat: the public field was historically named `language_hint` + # and accepted a string or a list. The API field is `language_hints` (a + # list). Translate the legacy kwarg so existing callers keep working, and + # drop the dead singular key so it is not rejected by the API + # (deny_unknown_fields). Hand-maintained. + if IS_PYDANTIC_V2: + + @pydantic.model_validator(mode="before") + @classmethod + def _migrate_language_hint(cls, values: typing.Any) -> typing.Any: + if not isinstance(values, dict): + return values + if "language_hint" in values: + values = dict(values) + hint = values.pop("language_hint") + if hint is not None and values.get("language_hints") is None: + values["language_hints"] = [hint] if isinstance(hint, str) else list(hint) + return values + else: + + @pydantic.root_validator(pre=True) # type: ignore[deprecated] + def _migrate_language_hint(cls, values: typing.Any) -> typing.Any: # type: ignore[no-redef] + if not isinstance(values, dict): + return values + if "language_hint" in values: + values = dict(values) + hint = values.pop("language_hint") + if hint is not None and values.get("language_hints") is None: + values["language_hints"] = [hint] if isinstance(hint, str) else list(hint) + return values + if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 else: diff --git a/src/deepgram/agent/v1/types/agent_v1settings_agent_listen_provider.py b/src/deepgram/agent/v1/types/agent_v1settings_agent_listen_provider.py index 02878ddd..7fe885cb 100644 --- a/src/deepgram/agent/v1/types/agent_v1settings_agent_listen_provider.py +++ b/src/deepgram/agent/v1/types/agent_v1settings_agent_listen_provider.py @@ -8,7 +8,6 @@ import typing_extensions from ....core.pydantic_utilities import IS_PYDANTIC_V2 from ....core.unchecked_base_model import UncheckedBaseModel, UnionMetadata -from ....types.deepgram_listen_provider_v2language_hint import DeepgramListenProviderV2LanguageHint class AgentV1SettingsAgentListenProvider_V1(UncheckedBaseModel): @@ -33,9 +32,41 @@ class AgentV1SettingsAgentListenProvider_V2(UncheckedBaseModel): version: typing.Literal["v2"] = "v2" type: typing.Literal["deepgram"] = "deepgram" model: str - language_hint: typing.Optional[DeepgramListenProviderV2LanguageHint] = None + language_hints: typing.Optional[typing.List[str]] = None + language_hint: typing.Optional[typing.Union[str, typing.List[str]]] = pydantic.Field(default=None, exclude=True) keyterms: typing.Optional[typing.List[str]] = None + # Backward-compat: the public field was historically named `language_hint` + # and accepted a string or a list. The API field is `language_hints` (a + # list). Translate the legacy kwarg so existing callers keep working, and + # drop the dead singular key so it is not rejected by the API + # (deny_unknown_fields). Hand-maintained. + if IS_PYDANTIC_V2: + + @pydantic.model_validator(mode="before") + @classmethod + def _migrate_language_hint(cls, values: typing.Any) -> typing.Any: + if not isinstance(values, dict): + return values + if "language_hint" in values: + values = dict(values) + hint = values.pop("language_hint") + if hint is not None and values.get("language_hints") is None: + values["language_hints"] = [hint] if isinstance(hint, str) else list(hint) + return values + else: + + @pydantic.root_validator(pre=True) # type: ignore[deprecated] + def _migrate_language_hint(cls, values: typing.Any) -> typing.Any: # type: ignore[no-redef] + if not isinstance(values, dict): + return values + if "language_hint" in values: + values = dict(values) + hint = values.pop("language_hint") + if hint is not None and values.get("language_hints") is None: + values["language_hints"] = [hint] if isinstance(hint, str) else list(hint) + return values + if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 else: diff --git a/src/deepgram/agent/v1/types/agent_v1settings_audio_output.py b/src/deepgram/agent/v1/types/agent_v1settings_audio_output.py index bfc06608..ac6d0892 100644 --- a/src/deepgram/agent/v1/types/agent_v1settings_audio_output.py +++ b/src/deepgram/agent/v1/types/agent_v1settings_audio_output.py @@ -5,6 +5,7 @@ import pydantic from ....core.pydantic_utilities import IS_PYDANTIC_V2 from ....core.unchecked_base_model import UncheckedBaseModel +from .agent_v1settings_audio_output_container import AgentV1SettingsAudioOutputContainer from .agent_v1settings_audio_output_encoding import AgentV1SettingsAudioOutputEncoding @@ -28,9 +29,9 @@ class AgentV1SettingsAudioOutput(UncheckedBaseModel): Audio bitrate in bits per second """ - container: typing.Optional[str] = pydantic.Field(default=None) + container: typing.Optional[AgentV1SettingsAudioOutputContainer] = pydantic.Field(default=None) """ - Audio container format. If omitted, defaults to 'none' + Audio container format. """ if IS_PYDANTIC_V2: diff --git a/src/deepgram/core/client_wrapper.py b/src/deepgram/core/client_wrapper.py index 8892372e..5136a051 100644 --- a/src/deepgram/core/client_wrapper.py +++ b/src/deepgram/core/client_wrapper.py @@ -30,12 +30,12 @@ def get_headers(self) -> typing.Dict[str, str]: import platform headers: typing.Dict[str, str] = { - "User-Agent": "deepgram-sdk/7.3.1", + "User-Agent": "deepgram-sdk/7.3.2", "X-Fern-Language": "Python", "X-Fern-Runtime": f"python/{platform.python_version()}", "X-Fern-Platform": f"{platform.system().lower()}/{platform.release()}", "X-Fern-SDK-Name": "deepgram-sdk", - "X-Fern-SDK-Version": "7.3.1", + "X-Fern-SDK-Version": "7.3.2", **(self.get_custom_headers() or {}), } headers["Authorization"] = f"Token {self.api_key}" diff --git a/src/deepgram/core/http_sse/_api.py b/src/deepgram/core/http_sse/_api.py index b3753991..fd137301 100644 --- a/src/deepgram/core/http_sse/_api.py +++ b/src/deepgram/core/http_sse/_api.py @@ -10,6 +10,8 @@ from ._exceptions import SSEError from ._models import ServerSentEvent +MAX_LINE_SIZE: int = 1_048_576 # 1 MiB + class EventSource: def __init__(self, response: httpx.Response) -> None: @@ -75,10 +77,20 @@ def iter_sse(self) -> Iterator[ServerSentEvent]: if sse is not None: yield sse + if len(buf) > MAX_LINE_SIZE: + raise SSEError( + f"SSE line exceeded maximum size of {MAX_LINE_SIZE} characters without encountering a newline" + ) + # Flush any remaining bytes from the incremental decoder buf += text_decoder.decode(b"", final=True) buf = buf.replace("\r\n", "\n").replace("\r", "\n") + if len(buf) > MAX_LINE_SIZE: + raise SSEError( + f"SSE line exceeded maximum size of {MAX_LINE_SIZE} characters without encountering a newline" + ) + while "\n" in buf: line, buf = buf.split("\n", 1) sse = decoder.decode(line) @@ -107,10 +119,20 @@ async def aiter_sse(self) -> AsyncGenerator[ServerSentEvent, None]: if sse is not None: yield sse + if len(buf) > MAX_LINE_SIZE: + raise SSEError( + f"SSE line exceeded maximum size of {MAX_LINE_SIZE} characters without encountering a newline" + ) + # Flush any remaining bytes from the incremental decoder buf += text_decoder.decode(b"", final=True) buf = buf.replace("\r\n", "\n").replace("\r", "\n") + if len(buf) > MAX_LINE_SIZE: + raise SSEError( + f"SSE line exceeded maximum size of {MAX_LINE_SIZE} characters without encountering a newline" + ) + while "\n" in buf: line, buf = buf.split("\n", 1) sse = decoder.decode(line) diff --git a/src/deepgram/listen/__init__.py b/src/deepgram/listen/__init__.py index 6a178489..b3bccf31 100644 --- a/src/deepgram/listen/__init__.py +++ b/src/deepgram/listen/__init__.py @@ -8,6 +8,7 @@ if typing.TYPE_CHECKING: from . import v1, v2 from .v1 import ( + DiarizeModel, ListenV1CloseStream, ListenV1CloseStreamParams, ListenV1CloseStreamType, @@ -63,6 +64,7 @@ ListenV2TurnInfoWordsItemParams, ) _dynamic_imports: typing.Dict[str, str] = { + "DiarizeModel": ".v1", "ListenV1CloseStream": ".v1", "ListenV1CloseStreamParams": ".v1", "ListenV1CloseStreamType": ".v1", @@ -141,6 +143,7 @@ def __dir__(): __all__ = [ + "DiarizeModel", "ListenV1CloseStream", "ListenV1CloseStreamParams", "ListenV1CloseStreamType", diff --git a/src/deepgram/listen/v1/__init__.py b/src/deepgram/listen/v1/__init__.py index c8e01a2f..9311daf3 100644 --- a/src/deepgram/listen/v1/__init__.py +++ b/src/deepgram/listen/v1/__init__.py @@ -7,6 +7,7 @@ if typing.TYPE_CHECKING: from .types import ( + DiarizeModel, ListenV1CloseStream, ListenV1CloseStreamType, ListenV1Finalize, @@ -53,6 +54,7 @@ ListenV1UtteranceEndParams, ) _dynamic_imports: typing.Dict[str, str] = { + "DiarizeModel": ".types", "ListenV1CloseStream": ".types", "ListenV1CloseStreamParams": ".requests", "ListenV1CloseStreamType": ".types", @@ -118,6 +120,7 @@ def __dir__(): __all__ = [ + "DiarizeModel", "ListenV1CloseStream", "ListenV1CloseStreamParams", "ListenV1CloseStreamType", diff --git a/src/deepgram/listen/v1/client.py b/src/deepgram/listen/v1/client.py index 0a4ac15e..c0be4754 100644 --- a/src/deepgram/listen/v1/client.py +++ b/src/deepgram/listen/v1/client.py @@ -44,6 +44,7 @@ from ...types.listen_v1version import ListenV1Version from .raw_client import AsyncRawV1Client, RawV1Client from .socket_client import AsyncV1SocketClient, V1SocketClient +from .types.diarize_model import DiarizeModel if typing.TYPE_CHECKING: from .media.client import AsyncMediaClient, MediaClient @@ -80,6 +81,7 @@ def connect( channels: typing.Optional[ListenV1Channels] = None, detect_entities: typing.Optional[ListenV1DetectEntities] = None, diarize: typing.Optional[ListenV1Diarize] = None, + diarize_model: typing.Optional[DiarizeModel] = None, dictation: typing.Optional[ListenV1Dictation] = None, encoding: typing.Optional[ListenV1Encoding] = None, endpointing: typing.Optional[ListenV1Endpointing] = None, @@ -120,6 +122,10 @@ def connect( detect_entities : typing.Optional[ListenV1DetectEntities] diarize : typing.Optional[ListenV1Diarize] + Deprecated: use `diarize_model` instead. Recognize speaker changes. Each word in the transcript will be assigned a speaker number starting at 0. + + diarize_model : typing.Optional[DiarizeModel] + Select and enable a specific diarization model version. Specifying this parameter enables diarization and selects the model — you do not need to also set `diarize=true`. Supported values for streaming: `v1`, `latest`. The `v2` value is not supported on streaming and returns a validation error. dictation : typing.Optional[ListenV1Dictation] @@ -190,6 +196,7 @@ def connect( "channels": channels, "detect_entities": detect_entities, "diarize": diarize, + "diarize_model": diarize_model, "dictation": dictation, "encoding": encoding, "endpointing": endpointing, @@ -281,6 +288,7 @@ async def connect( channels: typing.Optional[ListenV1Channels] = None, detect_entities: typing.Optional[ListenV1DetectEntities] = None, diarize: typing.Optional[ListenV1Diarize] = None, + diarize_model: typing.Optional[DiarizeModel] = None, dictation: typing.Optional[ListenV1Dictation] = None, encoding: typing.Optional[ListenV1Encoding] = None, endpointing: typing.Optional[ListenV1Endpointing] = None, @@ -321,6 +329,10 @@ async def connect( detect_entities : typing.Optional[ListenV1DetectEntities] diarize : typing.Optional[ListenV1Diarize] + Deprecated: use `diarize_model` instead. Recognize speaker changes. Each word in the transcript will be assigned a speaker number starting at 0. + + diarize_model : typing.Optional[DiarizeModel] + Select and enable a specific diarization model version. Specifying this parameter enables diarization and selects the model — you do not need to also set `diarize=true`. Supported values for streaming: `v1`, `latest`. The `v2` value is not supported on streaming and returns a validation error. dictation : typing.Optional[ListenV1Dictation] @@ -391,6 +403,7 @@ async def connect( "channels": channels, "detect_entities": detect_entities, "diarize": diarize, + "diarize_model": diarize_model, "dictation": dictation, "encoding": encoding, "endpointing": endpointing, diff --git a/src/deepgram/listen/v1/media/client.py b/src/deepgram/listen/v1/media/client.py index 2fb36847..866110b9 100644 --- a/src/deepgram/listen/v1/media/client.py +++ b/src/deepgram/listen/v1/media/client.py @@ -127,10 +127,10 @@ def transcribe_url( Identifies the dominant language spoken in submitted audio diarize : typing.Optional[bool] - Recognize speaker changes. Each word in the transcript will be assigned a speaker number starting at 0 + Deprecated: use `diarize_model` instead. Recognize speaker changes. Each word in the transcript will be assigned a speaker number starting at 0. diarize_model : typing.Optional[MediaTranscribeRequestDiarizeModel] - Select and enable a specific batch diarization model version. If specifying this parameter, you should not set the deprecated `diarize=true` parameter. Not accepted on streaming requests. + Select and enable a specific diarization model version. Specifying this parameter enables diarization and selects the model — you do not need to also set the deprecated `diarize=true` parameter. For batch, supported values are `latest` (currently v2), `v1`, and `v2`. For streaming, supported values are `latest` (currently v1) and `v1`; `v2` returns a validation error on streaming requests. dictation : typing.Optional[bool] Dictation mode for controlling formatting with dictated speech @@ -387,10 +387,10 @@ def transcribe_file( Identifies the dominant language spoken in submitted audio diarize : typing.Optional[bool] - Recognize speaker changes. Each word in the transcript will be assigned a speaker number starting at 0 + Deprecated: use `diarize_model` instead. Recognize speaker changes. Each word in the transcript will be assigned a speaker number starting at 0. diarize_model : typing.Optional[MediaTranscribeRequestDiarizeModel] - Select and enable a specific batch diarization model version. If specifying this parameter, you should not set the deprecated `diarize=true` parameter. Not accepted on streaming requests. + Select and enable a specific diarization model version. Specifying this parameter enables diarization and selects the model — you do not need to also set the deprecated `diarize=true` parameter. For batch, supported values are `latest` (currently v2), `v1`, and `v2`. For streaming, supported values are `latest` (currently v1) and `v1`; `v2` returns a validation error on streaming requests. dictation : typing.Optional[bool] Dictation mode for controlling formatting with dictated speech @@ -624,10 +624,10 @@ async def transcribe_url( Identifies the dominant language spoken in submitted audio diarize : typing.Optional[bool] - Recognize speaker changes. Each word in the transcript will be assigned a speaker number starting at 0 + Deprecated: use `diarize_model` instead. Recognize speaker changes. Each word in the transcript will be assigned a speaker number starting at 0. diarize_model : typing.Optional[MediaTranscribeRequestDiarizeModel] - Select and enable a specific batch diarization model version. If specifying this parameter, you should not set the deprecated `diarize=true` parameter. Not accepted on streaming requests. + Select and enable a specific diarization model version. Specifying this parameter enables diarization and selects the model — you do not need to also set the deprecated `diarize=true` parameter. For batch, supported values are `latest` (currently v2), `v1`, and `v2`. For streaming, supported values are `latest` (currently v1) and `v1`; `v2` returns a validation error on streaming requests. dictation : typing.Optional[bool] Dictation mode for controlling formatting with dictated speech @@ -892,10 +892,10 @@ async def transcribe_file( Identifies the dominant language spoken in submitted audio diarize : typing.Optional[bool] - Recognize speaker changes. Each word in the transcript will be assigned a speaker number starting at 0 + Deprecated: use `diarize_model` instead. Recognize speaker changes. Each word in the transcript will be assigned a speaker number starting at 0. diarize_model : typing.Optional[MediaTranscribeRequestDiarizeModel] - Select and enable a specific batch diarization model version. If specifying this parameter, you should not set the deprecated `diarize=true` parameter. Not accepted on streaming requests. + Select and enable a specific diarization model version. Specifying this parameter enables diarization and selects the model — you do not need to also set the deprecated `diarize=true` parameter. For batch, supported values are `latest` (currently v2), `v1`, and `v2`. For streaming, supported values are `latest` (currently v1) and `v1`; `v2` returns a validation error on streaming requests. dictation : typing.Optional[bool] Dictation mode for controlling formatting with dictated speech diff --git a/src/deepgram/listen/v1/media/raw_client.py b/src/deepgram/listen/v1/media/raw_client.py index 9a89052a..d21a900a 100644 --- a/src/deepgram/listen/v1/media/raw_client.py +++ b/src/deepgram/listen/v1/media/raw_client.py @@ -122,10 +122,10 @@ def transcribe_url( Identifies the dominant language spoken in submitted audio diarize : typing.Optional[bool] - Recognize speaker changes. Each word in the transcript will be assigned a speaker number starting at 0 + Deprecated: use `diarize_model` instead. Recognize speaker changes. Each word in the transcript will be assigned a speaker number starting at 0. diarize_model : typing.Optional[MediaTranscribeRequestDiarizeModel] - Select and enable a specific batch diarization model version. If specifying this parameter, you should not set the deprecated `diarize=true` parameter. Not accepted on streaming requests. + Select and enable a specific diarization model version. Specifying this parameter enables diarization and selects the model — you do not need to also set the deprecated `diarize=true` parameter. For batch, supported values are `latest` (currently v2), `v1`, and `v2`. For streaming, supported values are `latest` (currently v1) and `v1`; `v2` returns a validation error on streaming requests. dictation : typing.Optional[bool] Dictation mode for controlling formatting with dictated speech @@ -373,10 +373,10 @@ def transcribe_file( Identifies the dominant language spoken in submitted audio diarize : typing.Optional[bool] - Recognize speaker changes. Each word in the transcript will be assigned a speaker number starting at 0 + Deprecated: use `diarize_model` instead. Recognize speaker changes. Each word in the transcript will be assigned a speaker number starting at 0. diarize_model : typing.Optional[MediaTranscribeRequestDiarizeModel] - Select and enable a specific batch diarization model version. If specifying this parameter, you should not set the deprecated `diarize=true` parameter. Not accepted on streaming requests. + Select and enable a specific diarization model version. Specifying this parameter enables diarization and selects the model — you do not need to also set the deprecated `diarize=true` parameter. For batch, supported values are `latest` (currently v2), `v1`, and `v2`. For streaming, supported values are `latest` (currently v1) and `v1`; `v2` returns a validation error on streaming requests. dictation : typing.Optional[bool] Dictation mode for controlling formatting with dictated speech @@ -627,10 +627,10 @@ async def transcribe_url( Identifies the dominant language spoken in submitted audio diarize : typing.Optional[bool] - Recognize speaker changes. Each word in the transcript will be assigned a speaker number starting at 0 + Deprecated: use `diarize_model` instead. Recognize speaker changes. Each word in the transcript will be assigned a speaker number starting at 0. diarize_model : typing.Optional[MediaTranscribeRequestDiarizeModel] - Select and enable a specific batch diarization model version. If specifying this parameter, you should not set the deprecated `diarize=true` parameter. Not accepted on streaming requests. + Select and enable a specific diarization model version. Specifying this parameter enables diarization and selects the model — you do not need to also set the deprecated `diarize=true` parameter. For batch, supported values are `latest` (currently v2), `v1`, and `v2`. For streaming, supported values are `latest` (currently v1) and `v1`; `v2` returns a validation error on streaming requests. dictation : typing.Optional[bool] Dictation mode for controlling formatting with dictated speech @@ -878,10 +878,10 @@ async def transcribe_file( Identifies the dominant language spoken in submitted audio diarize : typing.Optional[bool] - Recognize speaker changes. Each word in the transcript will be assigned a speaker number starting at 0 + Deprecated: use `diarize_model` instead. Recognize speaker changes. Each word in the transcript will be assigned a speaker number starting at 0. diarize_model : typing.Optional[MediaTranscribeRequestDiarizeModel] - Select and enable a specific batch diarization model version. If specifying this parameter, you should not set the deprecated `diarize=true` parameter. Not accepted on streaming requests. + Select and enable a specific diarization model version. Specifying this parameter enables diarization and selects the model — you do not need to also set the deprecated `diarize=true` parameter. For batch, supported values are `latest` (currently v2), `v1`, and `v2`. For streaming, supported values are `latest` (currently v1) and `v1`; `v2` returns a validation error on streaming requests. dictation : typing.Optional[bool] Dictation mode for controlling formatting with dictated speech diff --git a/src/deepgram/listen/v1/raw_client.py b/src/deepgram/listen/v1/raw_client.py index f85fe623..dc4bdecd 100644 --- a/src/deepgram/listen/v1/raw_client.py +++ b/src/deepgram/listen/v1/raw_client.py @@ -41,6 +41,7 @@ from ...types.listen_v1vad_events import ListenV1VadEvents from ...types.listen_v1version import ListenV1Version from .socket_client import AsyncV1SocketClient, V1SocketClient +from .types.diarize_model import DiarizeModel try: from websockets.legacy.client import connect as websockets_client_connect # type: ignore @@ -61,6 +62,7 @@ def connect( channels: typing.Optional[ListenV1Channels] = None, detect_entities: typing.Optional[ListenV1DetectEntities] = None, diarize: typing.Optional[ListenV1Diarize] = None, + diarize_model: typing.Optional[DiarizeModel] = None, dictation: typing.Optional[ListenV1Dictation] = None, encoding: typing.Optional[ListenV1Encoding] = None, endpointing: typing.Optional[ListenV1Endpointing] = None, @@ -101,6 +103,10 @@ def connect( detect_entities : typing.Optional[ListenV1DetectEntities] diarize : typing.Optional[ListenV1Diarize] + Deprecated: use `diarize_model` instead. Recognize speaker changes. Each word in the transcript will be assigned a speaker number starting at 0. + + diarize_model : typing.Optional[DiarizeModel] + Select and enable a specific diarization model version. Specifying this parameter enables diarization and selects the model — you do not need to also set `diarize=true`. Supported values for streaming: `v1`, `latest`. The `v2` value is not supported on streaming and returns a validation error. dictation : typing.Optional[ListenV1Dictation] @@ -171,6 +177,7 @@ def connect( "channels": channels, "detect_entities": detect_entities, "diarize": diarize, + "diarize_model": diarize_model, "dictation": dictation, "encoding": encoding, "endpointing": endpointing, @@ -241,6 +248,7 @@ async def connect( channels: typing.Optional[ListenV1Channels] = None, detect_entities: typing.Optional[ListenV1DetectEntities] = None, diarize: typing.Optional[ListenV1Diarize] = None, + diarize_model: typing.Optional[DiarizeModel] = None, dictation: typing.Optional[ListenV1Dictation] = None, encoding: typing.Optional[ListenV1Encoding] = None, endpointing: typing.Optional[ListenV1Endpointing] = None, @@ -281,6 +289,10 @@ async def connect( detect_entities : typing.Optional[ListenV1DetectEntities] diarize : typing.Optional[ListenV1Diarize] + Deprecated: use `diarize_model` instead. Recognize speaker changes. Each word in the transcript will be assigned a speaker number starting at 0. + + diarize_model : typing.Optional[DiarizeModel] + Select and enable a specific diarization model version. Specifying this parameter enables diarization and selects the model — you do not need to also set `diarize=true`. Supported values for streaming: `v1`, `latest`. The `v2` value is not supported on streaming and returns a validation error. dictation : typing.Optional[ListenV1Dictation] @@ -351,6 +363,7 @@ async def connect( "channels": channels, "detect_entities": detect_entities, "diarize": diarize, + "diarize_model": diarize_model, "dictation": dictation, "encoding": encoding, "endpointing": endpointing, diff --git a/src/deepgram/listen/v1/types/__init__.py b/src/deepgram/listen/v1/types/__init__.py index 30c6849a..81de9477 100644 --- a/src/deepgram/listen/v1/types/__init__.py +++ b/src/deepgram/listen/v1/types/__init__.py @@ -6,6 +6,7 @@ from importlib import import_module if typing.TYPE_CHECKING: + from .diarize_model import DiarizeModel from .listen_v1close_stream import ListenV1CloseStream from .listen_v1close_stream_type import ListenV1CloseStreamType from .listen_v1finalize import ListenV1Finalize @@ -23,6 +24,7 @@ from .listen_v1speech_started import ListenV1SpeechStarted from .listen_v1utterance_end import ListenV1UtteranceEnd _dynamic_imports: typing.Dict[str, str] = { + "DiarizeModel": ".diarize_model", "ListenV1CloseStream": ".listen_v1close_stream", "ListenV1CloseStreamType": ".listen_v1close_stream_type", "ListenV1Finalize": ".listen_v1finalize", @@ -64,6 +66,7 @@ def __dir__(): __all__ = [ + "DiarizeModel", "ListenV1CloseStream", "ListenV1CloseStreamType", "ListenV1Finalize", diff --git a/src/deepgram/listen/v1/types/diarize_model.py b/src/deepgram/listen/v1/types/diarize_model.py new file mode 100644 index 00000000..b48780b4 --- /dev/null +++ b/src/deepgram/listen/v1/types/diarize_model.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +DiarizeModel = typing.Union[typing.Literal["latest", "v1"], typing.Any] diff --git a/src/deepgram/listen/v2/client.py b/src/deepgram/listen/v2/client.py index 934deb0d..651fb276 100644 --- a/src/deepgram/listen/v2/client.py +++ b/src/deepgram/listen/v2/client.py @@ -21,6 +21,7 @@ from ...types.listen_v2eot_timeout_ms import ListenV2EotTimeoutMs from ...types.listen_v2mip_opt_out import ListenV2MipOptOut from ...types.listen_v2model import ListenV2Model +from ...types.listen_v2profanity_filter import ListenV2ProfanityFilter from ...types.listen_v2sample_rate import ListenV2SampleRate from ...types.listen_v2tag import ListenV2Tag from .raw_client import AsyncRawV2Client, RawV2Client @@ -59,6 +60,7 @@ def connect( eot_timeout_ms: typing.Optional[ListenV2EotTimeoutMs] = None, keyterm: typing.Optional[ListenV2KeytermParams] = None, language_hint: typing.Optional[ListenV2LanguageHintParams] = None, + profanity_filter: typing.Optional[ListenV2ProfanityFilter] = None, mip_opt_out: typing.Optional[ListenV2MipOptOut] = None, tag: typing.Optional[ListenV2Tag] = None, authorization: typing.Optional[str] = None, @@ -86,6 +88,8 @@ def connect( language_hint : typing.Optional[ListenV2LanguageHintParams] + profanity_filter : typing.Optional[ListenV2ProfanityFilter] + mip_opt_out : typing.Optional[ListenV2MipOptOut] tag : typing.Optional[ListenV2Tag] @@ -127,6 +131,7 @@ def connect( annotation=ListenV2LanguageHintParams, direction="write", ), + "profanity_filter": profanity_filter, "mip_opt_out": mip_opt_out, "tag": tag, **( @@ -190,6 +195,7 @@ async def connect( eot_timeout_ms: typing.Optional[ListenV2EotTimeoutMs] = None, keyterm: typing.Optional[ListenV2KeytermParams] = None, language_hint: typing.Optional[ListenV2LanguageHintParams] = None, + profanity_filter: typing.Optional[ListenV2ProfanityFilter] = None, mip_opt_out: typing.Optional[ListenV2MipOptOut] = None, tag: typing.Optional[ListenV2Tag] = None, authorization: typing.Optional[str] = None, @@ -217,6 +223,8 @@ async def connect( language_hint : typing.Optional[ListenV2LanguageHintParams] + profanity_filter : typing.Optional[ListenV2ProfanityFilter] + mip_opt_out : typing.Optional[ListenV2MipOptOut] tag : typing.Optional[ListenV2Tag] @@ -258,6 +266,7 @@ async def connect( annotation=ListenV2LanguageHintParams, direction="write", ), + "profanity_filter": profanity_filter, "mip_opt_out": mip_opt_out, "tag": tag, **( diff --git a/src/deepgram/listen/v2/raw_client.py b/src/deepgram/listen/v2/raw_client.py index 366feb04..887a6460 100644 --- a/src/deepgram/listen/v2/raw_client.py +++ b/src/deepgram/listen/v2/raw_client.py @@ -21,6 +21,7 @@ from ...types.listen_v2eot_timeout_ms import ListenV2EotTimeoutMs from ...types.listen_v2mip_opt_out import ListenV2MipOptOut from ...types.listen_v2model import ListenV2Model +from ...types.listen_v2profanity_filter import ListenV2ProfanityFilter from ...types.listen_v2sample_rate import ListenV2SampleRate from ...types.listen_v2tag import ListenV2Tag from .socket_client import AsyncV2SocketClient, V2SocketClient @@ -47,6 +48,7 @@ def connect( eot_timeout_ms: typing.Optional[ListenV2EotTimeoutMs] = None, keyterm: typing.Optional[ListenV2KeytermParams] = None, language_hint: typing.Optional[ListenV2LanguageHintParams] = None, + profanity_filter: typing.Optional[ListenV2ProfanityFilter] = None, mip_opt_out: typing.Optional[ListenV2MipOptOut] = None, tag: typing.Optional[ListenV2Tag] = None, authorization: typing.Optional[str] = None, @@ -74,6 +76,8 @@ def connect( language_hint : typing.Optional[ListenV2LanguageHintParams] + profanity_filter : typing.Optional[ListenV2ProfanityFilter] + mip_opt_out : typing.Optional[ListenV2MipOptOut] tag : typing.Optional[ListenV2Tag] @@ -115,6 +119,7 @@ def connect( annotation=ListenV2LanguageHintParams, direction="write", ), + "profanity_filter": profanity_filter, "mip_opt_out": mip_opt_out, "tag": tag, **( @@ -167,6 +172,7 @@ async def connect( eot_timeout_ms: typing.Optional[ListenV2EotTimeoutMs] = None, keyterm: typing.Optional[ListenV2KeytermParams] = None, language_hint: typing.Optional[ListenV2LanguageHintParams] = None, + profanity_filter: typing.Optional[ListenV2ProfanityFilter] = None, mip_opt_out: typing.Optional[ListenV2MipOptOut] = None, tag: typing.Optional[ListenV2Tag] = None, authorization: typing.Optional[str] = None, @@ -194,6 +200,8 @@ async def connect( language_hint : typing.Optional[ListenV2LanguageHintParams] + profanity_filter : typing.Optional[ListenV2ProfanityFilter] + mip_opt_out : typing.Optional[ListenV2MipOptOut] tag : typing.Optional[ListenV2Tag] @@ -235,6 +243,7 @@ async def connect( annotation=ListenV2LanguageHintParams, direction="write", ), + "profanity_filter": profanity_filter, "mip_opt_out": mip_opt_out, "tag": tag, **( diff --git a/src/deepgram/listen/v2/requests/listen_v2close_stream.py b/src/deepgram/listen/v2/requests/listen_v2close_stream.py index 70e4f760..b1956a98 100644 --- a/src/deepgram/listen/v2/requests/listen_v2close_stream.py +++ b/src/deepgram/listen/v2/requests/listen_v2close_stream.py @@ -1,11 +1,12 @@ # This file was auto-generated by Fern from our API Definition. +import typing + import typing_extensions -from ..types.listen_v2close_stream_type import ListenV2CloseStreamType class ListenV2CloseStreamParams(typing_extensions.TypedDict): - type: ListenV2CloseStreamType + type: typing.Literal["CloseStream"] """ Message type identifier """ diff --git a/src/deepgram/listen/v2/requests/listen_v2turn_info_words_item.py b/src/deepgram/listen/v2/requests/listen_v2turn_info_words_item.py index 397157f5..7708cd27 100644 --- a/src/deepgram/listen/v2/requests/listen_v2turn_info_words_item.py +++ b/src/deepgram/listen/v2/requests/listen_v2turn_info_words_item.py @@ -13,3 +13,13 @@ class ListenV2TurnInfoWordsItemParams(typing_extensions.TypedDict): """ Confidence that this word was transcribed correctly """ + + start: typing_extensions.NotRequired[float] + """ + The start time of the word + """ + + end: typing_extensions.NotRequired[float] + """ + The end time of the word + """ diff --git a/src/deepgram/listen/v2/types/listen_v2close_stream.py b/src/deepgram/listen/v2/types/listen_v2close_stream.py index 1c95ec7a..f7724426 100644 --- a/src/deepgram/listen/v2/types/listen_v2close_stream.py +++ b/src/deepgram/listen/v2/types/listen_v2close_stream.py @@ -5,11 +5,10 @@ import pydantic from ....core.pydantic_utilities import IS_PYDANTIC_V2 from ....core.unchecked_base_model import UncheckedBaseModel -from .listen_v2close_stream_type import ListenV2CloseStreamType class ListenV2CloseStream(UncheckedBaseModel): - type: ListenV2CloseStreamType = pydantic.Field() + type: typing.Literal["CloseStream"] = pydantic.Field(default="CloseStream") """ Message type identifier """ diff --git a/src/deepgram/listen/v2/types/listen_v2close_stream_type.py b/src/deepgram/listen/v2/types/listen_v2close_stream_type.py index 2ac3484e..8463d35e 100644 --- a/src/deepgram/listen/v2/types/listen_v2close_stream_type.py +++ b/src/deepgram/listen/v2/types/listen_v2close_stream_type.py @@ -1,5 +1,15 @@ -# This file was auto-generated by Fern from our API Definition. +# This file is NOT auto-generated by Fern — it is a hand-written compatibility shim. +# +# Background: the original Fern-generated ListenV2CloseStreamType erroneously allowed +# Union[Literal["Finalize", "CloseStream", "KeepAlive"], Any] — the v2 spec copied v1's +# control-message enum, but a v2 CloseStream message's `type` can only ever be +# "CloseStream" ("Finalize"/"KeepAlive" are separate messages). The 2026-06-15 spec fix +# narrowed the enum and Fern removed this type entirely. +# +# To preserve the public import path `ListenV2CloseStreamType` without re-introducing the +# bogus values, we recreate it here as the corrected single-value literal and freeze it in +# .fernignore so the generator won't delete it again. import typing -ListenV2CloseStreamType = typing.Union[typing.Literal["Finalize", "CloseStream", "KeepAlive"], typing.Any] +ListenV2CloseStreamType = typing.Literal["CloseStream"] diff --git a/src/deepgram/listen/v2/types/listen_v2turn_info_words_item.py b/src/deepgram/listen/v2/types/listen_v2turn_info_words_item.py index b61097a0..07ada673 100644 --- a/src/deepgram/listen/v2/types/listen_v2turn_info_words_item.py +++ b/src/deepgram/listen/v2/types/listen_v2turn_info_words_item.py @@ -18,6 +18,16 @@ class ListenV2TurnInfoWordsItem(UncheckedBaseModel): Confidence that this word was transcribed correctly """ + start: typing.Optional[float] = pydantic.Field(default=None) + """ + The start time of the word + """ + + end: typing.Optional[float] = pydantic.Field(default=None) + """ + The end time of the word + """ + if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 else: diff --git a/src/deepgram/requests/deepgram_listen_provider_v2.py b/src/deepgram/requests/deepgram_listen_provider_v2.py index be90ac69..0f094f5a 100644 --- a/src/deepgram/requests/deepgram_listen_provider_v2.py +++ b/src/deepgram/requests/deepgram_listen_provider_v2.py @@ -3,7 +3,6 @@ import typing import typing_extensions -from .deepgram_listen_provider_v2language_hint import DeepgramListenProviderV2LanguageHintParams class DeepgramListenProviderV2Params(typing_extensions.TypedDict): @@ -22,9 +21,9 @@ class DeepgramListenProviderV2Params(typing_extensions.TypedDict): Model to use for speech to text using the V2 API (e.g. flux-general-en, flux-general-multi) """ - language_hint: typing_extensions.NotRequired[DeepgramListenProviderV2LanguageHintParams] + language_hints: typing_extensions.NotRequired[typing.Sequence[str]] """ - One or more BCP-47 language codes to bias the model toward specific languages. Only supported when model is flux-general-multi. Without hints, the model auto-detects the spoken language. See the Language Prompting guide for details. + An array of one or more BCP-47 language codes to bias the model toward specific languages. Only supported when model is flux-general-multi. Without hints, the model auto-detects the spoken language. See the Language Prompting guide for details. """ keyterms: typing_extensions.NotRequired[typing.Sequence[str]] diff --git a/src/deepgram/requests/deepgram_listen_provider_v2language_hint.py b/src/deepgram/requests/deepgram_listen_provider_v2language_hint.py index b48d2a89..22726241 100644 --- a/src/deepgram/requests/deepgram_listen_provider_v2language_hint.py +++ b/src/deepgram/requests/deepgram_listen_provider_v2language_hint.py @@ -1,4 +1,7 @@ -# This file was auto-generated by Fern from our API Definition. +# Backward-compatibility shim. Fern removed the generated +# DeepgramListenProviderV2LanguageHintParams type in the 2026-06-15 regen, but the +# public import path (and the AgentV1SettingsAgent[Context]ListenProviderV2LanguageHint +# compat aliases that import from here) must keep working. Hand-maintained. import typing diff --git a/src/deepgram/types/__init__.py b/src/deepgram/types/__init__.py index 34ae8481..f927e513 100644 --- a/src/deepgram/types/__init__.py +++ b/src/deepgram/types/__init__.py @@ -202,6 +202,7 @@ from .listen_v2language_hint import ListenV2LanguageHint from .listen_v2mip_opt_out import ListenV2MipOptOut from .listen_v2model import ListenV2Model + from .listen_v2profanity_filter import ListenV2ProfanityFilter from .listen_v2sample_rate import ListenV2SampleRate from .listen_v2tag import ListenV2Tag from .open_ai_speak_provider import OpenAiSpeakProvider @@ -444,6 +445,7 @@ "ListenV2LanguageHint": ".listen_v2language_hint", "ListenV2MipOptOut": ".listen_v2mip_opt_out", "ListenV2Model": ".listen_v2model", + "ListenV2ProfanityFilter": ".listen_v2profanity_filter", "ListenV2SampleRate": ".listen_v2sample_rate", "ListenV2Tag": ".listen_v2tag", "OpenAiSpeakProvider": ".open_ai_speak_provider", @@ -704,6 +706,7 @@ def __dir__(): "ListenV2LanguageHint", "ListenV2MipOptOut", "ListenV2Model", + "ListenV2ProfanityFilter", "ListenV2SampleRate", "ListenV2Tag", "OpenAiSpeakProvider", diff --git a/src/deepgram/types/deepgram_listen_provider_v2.py b/src/deepgram/types/deepgram_listen_provider_v2.py index 83cf6f7e..7c29c6f1 100644 --- a/src/deepgram/types/deepgram_listen_provider_v2.py +++ b/src/deepgram/types/deepgram_listen_provider_v2.py @@ -5,7 +5,6 @@ import pydantic from ..core.pydantic_utilities import IS_PYDANTIC_V2 from ..core.unchecked_base_model import UncheckedBaseModel -from .deepgram_listen_provider_v2language_hint import DeepgramListenProviderV2LanguageHint class DeepgramListenProviderV2(UncheckedBaseModel): @@ -24,9 +23,16 @@ class DeepgramListenProviderV2(UncheckedBaseModel): Model to use for speech to text using the V2 API (e.g. flux-general-en, flux-general-multi) """ - language_hint: typing.Optional[DeepgramListenProviderV2LanguageHint] = pydantic.Field(default=None) + language_hints: typing.Optional[typing.List[str]] = pydantic.Field(default=None) """ - One or more BCP-47 language codes to bias the model toward specific languages. Only supported when model is flux-general-multi. Without hints, the model auto-detects the spoken language. See the Language Prompting guide for details. + An array of one or more BCP-47 language codes to bias the model toward specific languages. Only supported when model is flux-general-multi. Without hints, the model auto-detects the spoken language. See the Language Prompting guide for details. + """ + + language_hint: typing.Optional[typing.Union[str, typing.List[str]]] = pydantic.Field(default=None, exclude=True) + """ + Deprecated. Use `language_hints`. Accepted (str or list) for backward + compatibility and remapped to `language_hints` by the validator below; + `exclude=True` keeps it off the wire (the API rejects unknown fields). """ keyterms: typing.Optional[typing.List[str]] = pydantic.Field(default=None) @@ -34,6 +40,37 @@ class DeepgramListenProviderV2(UncheckedBaseModel): Prompt keyterm recognition to improve Keyword Recall Rate """ + # Backward-compat: the public field was historically named `language_hint` + # and accepted a string or a list. The API field is `language_hints` (a + # list). Translate the legacy kwarg so existing callers keep working, and + # drop the dead singular key so it is not rejected by the API + # (deny_unknown_fields). Hand-maintained. + if IS_PYDANTIC_V2: + + @pydantic.model_validator(mode="before") + @classmethod + def _migrate_language_hint(cls, values: typing.Any) -> typing.Any: + if not isinstance(values, dict): + return values + if "language_hint" in values: + values = dict(values) + hint = values.pop("language_hint") + if hint is not None and values.get("language_hints") is None: + values["language_hints"] = [hint] if isinstance(hint, str) else list(hint) + return values + else: + + @pydantic.root_validator(pre=True) # type: ignore[deprecated] + def _migrate_language_hint(cls, values: typing.Any) -> typing.Any: # type: ignore[no-redef] + if not isinstance(values, dict): + return values + if "language_hint" in values: + values = dict(values) + hint = values.pop("language_hint") + if hint is not None and values.get("language_hints") is None: + values["language_hints"] = [hint] if isinstance(hint, str) else list(hint) + return values + if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 else: diff --git a/src/deepgram/types/deepgram_listen_provider_v2language_hint.py b/src/deepgram/types/deepgram_listen_provider_v2language_hint.py index 184371b6..32155a24 100644 --- a/src/deepgram/types/deepgram_listen_provider_v2language_hint.py +++ b/src/deepgram/types/deepgram_listen_provider_v2language_hint.py @@ -1,4 +1,7 @@ -# This file was auto-generated by Fern from our API Definition. +# Backward-compatibility shim. Fern removed the generated +# DeepgramListenProviderV2LanguageHint type in the 2026-06-15 regen, but the +# public import path (and the AgentV1SettingsAgent[Context]ListenProviderV2LanguageHint +# compat aliases that import from here) must keep working. Hand-maintained. import typing diff --git a/src/deepgram/types/listen_v2profanity_filter.py b/src/deepgram/types/listen_v2profanity_filter.py new file mode 100644 index 00000000..8607133e --- /dev/null +++ b/src/deepgram/types/listen_v2profanity_filter.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ListenV2ProfanityFilter = typing.Union[typing.Literal["true", "false"], typing.Any] diff --git a/tests/custom/test_language_hint_compat.py b/tests/custom/test_language_hint_compat.py new file mode 100644 index 00000000..47931b3e --- /dev/null +++ b/tests/custom/test_language_hint_compat.py @@ -0,0 +1,147 @@ +""" +Backward-compatibility coverage for the ``language_hint`` -> ``language_hints`` +rename on the V2 Deepgram listen provider (Voice Agent settings). + +Context: the public field was historically (and incorrectly) named +``language_hint`` and accepted a ``str`` or a ``list``. The API field is +``language_hints`` (a list), and the server rejects unknown fields +(``deny_unknown_fields``), so the legacy singular field never worked +end-to-end. The SDK now: + + * declares ``language_hints: Optional[List[str]]`` (the real field), + * keeps a deprecated ``language_hint: Optional[Union[str, List[str]]]`` field + (``exclude=True``) so legacy call sites still type-check, and + * a ``model_validator(mode="before")`` remaps ``language_hint`` -> + ``language_hints`` and drops the singular key so it never reaches the wire. + +These tests assert all of that, on every public surface and both construction +paths (kwargs + dict), and verify ``language_hint`` is never serialized. +""" + +import pydantic +import pytest + +from deepgram.agent.v1.types.agent_v1settings_agent_context_listen_provider import ( + AgentV1SettingsAgentContextListenProvider, + AgentV1SettingsAgentContextListenProvider_V2, +) +from deepgram.agent.v1.types.agent_v1settings_agent_context_listen_provider_v2 import ( + AgentV1SettingsAgentContextListenProviderV2, +) +from deepgram.agent.v1.types.agent_v1settings_agent_listen_provider import ( + AgentV1SettingsAgentListenProvider, + AgentV1SettingsAgentListenProvider_V2, +) + +# Public renamed-alias import paths (both resolve to DeepgramListenProviderV2). +from deepgram.agent.v1.types.agent_v1settings_agent_listen_provider_v2 import ( + AgentV1SettingsAgentListenProviderV2, +) +from deepgram.core.pydantic_utilities import IS_PYDANTIC_V2 +from deepgram.types.deepgram_listen_provider_v2 import DeepgramListenProviderV2 + +# Every public surface that carries the ``language_hints`` field and the shim. +DIRECT_MODELS = [ + pytest.param(DeepgramListenProviderV2, id="DeepgramListenProviderV2"), + pytest.param(AgentV1SettingsAgentListenProvider_V2, id="AgentListenProvider_V2"), + pytest.param(AgentV1SettingsAgentContextListenProvider_V2, id="AgentContextListenProvider_V2"), + pytest.param(AgentV1SettingsAgentListenProviderV2, id="AgentListenProviderV2_alias"), + pytest.param(AgentV1SettingsAgentContextListenProviderV2, id="AgentContextListenProviderV2_alias"), +] + +UNION_TYPES = [ + pytest.param(AgentV1SettingsAgentListenProvider, id="AgentListenProvider_union"), + pytest.param(AgentV1SettingsAgentContextListenProvider, id="AgentContextListenProvider_union"), +] + + +def _dump(model): + # ``.dict()`` is the method the SDK uses on the wire (the socket send path), + # so it is the faithful check for "did language_hint leak into the payload?". + return model.dict() + + +def _from_dict(cls, payload): + return cls.model_validate(payload) if IS_PYDANTIC_V2 else cls.parse_obj(payload) + + +def _from_union(union_type, payload): + if IS_PYDANTIC_V2: + return pydantic.TypeAdapter(union_type).validate_python(payload) + return pydantic.parse_obj_as(union_type, payload) + + +class TestLanguageHintMapping: + @pytest.mark.parametrize("cls", DIRECT_MODELS) + def test_singular_str_kwarg_maps_to_list(self, cls): + dumped = _dump(cls(model="flux-general-multi", language_hint="en")) + assert dumped.get("language_hints") == ["en"] + assert "language_hint" not in dumped + + @pytest.mark.parametrize("cls", DIRECT_MODELS) + def test_singular_list_kwarg_passthrough(self, cls): + dumped = _dump(cls(model="flux-general-multi", language_hint=["en", "de"])) + assert dumped.get("language_hints") == ["en", "de"] + assert "language_hint" not in dumped + + @pytest.mark.parametrize("cls", DIRECT_MODELS) + def test_singular_str_from_dict_maps(self, cls): + # Mirrors nested construction (e.g. settings.agent.listen.provider as a dict). + dumped = _dump(_from_dict(cls, {"model": "flux-general-multi", "language_hint": "en"})) + assert dumped.get("language_hints") == ["en"] + assert "language_hint" not in dumped + + @pytest.mark.parametrize("cls", DIRECT_MODELS) + def test_singular_list_from_dict_maps(self, cls): + dumped = _dump(_from_dict(cls, {"model": "flux-general-multi", "language_hint": ["en", "de"]})) + assert dumped.get("language_hints") == ["en", "de"] + assert "language_hint" not in dumped + + @pytest.mark.parametrize("cls", DIRECT_MODELS) + def test_plural_kwarg_unchanged(self, cls): + dumped = _dump(cls(model="flux-general-multi", language_hints=["fr"])) + assert dumped.get("language_hints") == ["fr"] + assert "language_hint" not in dumped + + @pytest.mark.parametrize("cls", DIRECT_MODELS) + def test_explicit_plural_wins_over_singular(self, cls): + dumped = _dump(cls(model="flux-general-multi", language_hint="en", language_hints=["de"])) + assert dumped.get("language_hints") == ["de"] + assert "language_hint" not in dumped + + @pytest.mark.parametrize("cls", DIRECT_MODELS) + def test_neither_is_none(self, cls): + dumped = _dump(cls(model="flux-general-multi")) + assert dumped.get("language_hints") is None + assert "language_hint" not in dumped + + @pytest.mark.parametrize("cls", DIRECT_MODELS) + def test_singular_none_is_noop(self, cls): + dumped = _dump(cls(model="flux-general-multi", language_hint=None)) + assert dumped.get("language_hints") is None + assert "language_hint" not in dumped + + +class TestLanguageHintUnionRouting: + @pytest.mark.parametrize("union_type", UNION_TYPES) + def test_union_routes_to_v2_and_maps(self, union_type): + model = _from_union( + union_type, + {"version": "v2", "type": "deepgram", "model": "flux-general-multi", "language_hint": "en"}, + ) + dumped = model.dict() + assert dumped.get("language_hints") == ["en"] + assert "language_hint" not in dumped + + @pytest.mark.parametrize("union_type", UNION_TYPES) + def test_v1_member_unaffected_by_shim(self, union_type): + # The v1 provider uses singular ``language`` and has no language_hints; + # routing to it must not be perturbed by the v2 shim. + model = _from_union( + union_type, + {"version": "v1", "type": "deepgram", "model": "nova-3", "language": "en"}, + ) + dumped = model.dict() + assert dumped.get("language") == "en" + assert dumped.get("language_hints") is None + assert "language_hint" not in dumped diff --git a/tests/custom/test_language_hints_feature.py b/tests/custom/test_language_hints_feature.py new file mode 100644 index 00000000..e38b25c8 --- /dev/null +++ b/tests/custom/test_language_hints_feature.py @@ -0,0 +1,52 @@ +""" +Forward-feature coverage for ``language_hints`` (the current, correct field) — +distinct from the deprecated ``language_hint`` back-compat shim covered in +``test_language_hint_compat.py``. + +Unlike ``language_hint`` (which is ``exclude=True`` and never serialized), +``language_hints`` is a real wire field and MUST serialize. Covered here on the +Flux STT reconfigure control message (``ListenV2Configure``, client-sent) and +the Voice Agent listen provider, with single and multiple codes, via kwargs and +dict, asserting the value lands on the wire. +""" + +from deepgram.core.pydantic_utilities import IS_PYDANTIC_V2 +from deepgram.listen.v2.types.listen_v2configure import ListenV2Configure +from deepgram.types.deepgram_listen_provider_v2 import DeepgramListenProviderV2 + + +def _from_dict(cls, payload): + return cls.model_validate(payload) if IS_PYDANTIC_V2 else cls.parse_obj(payload) + + +class TestLanguageHintsFluxConfigure: + """ListenV2Configure — Flux STT mid-stream reconfigure message (client-sent).""" + + def test_multiple_codes_serialize_on_the_wire(self): + dumped = ListenV2Configure(language_hints=["en", "es", "fr"]).dict() + assert dumped["language_hints"] == ["en", "es", "fr"] + + def test_single_code(self): + assert ListenV2Configure(language_hints=["en"]).dict()["language_hints"] == ["en"] + + def test_from_dict(self): + cfg = _from_dict(ListenV2Configure, {"language_hints": ["en", "es"]}) + assert cfg.language_hints == ["en", "es"] + + def test_omitted_when_absent(self): + assert ListenV2Configure().dict().get("language_hints") is None + + +class TestLanguageHintsProviderForward: + """DeepgramListenProviderV2 — ``language_hints`` (plural) is the real wire field.""" + + def test_multiple_codes_serialize(self): + dumped = DeepgramListenProviderV2(model="flux-general-multi", language_hints=["en", "es", "fr"]).dict() + assert dumped["language_hints"] == ["en", "es", "fr"] + + def test_from_dict(self): + provider = _from_dict( + DeepgramListenProviderV2, + {"model": "flux-general-multi", "language_hints": ["en", "es"]}, + ) + assert provider.language_hints == ["en", "es"] diff --git a/tests/custom/test_listen_v2_regen_constraints.py b/tests/custom/test_listen_v2_regen_constraints.py new file mode 100644 index 00000000..af0476e5 --- /dev/null +++ b/tests/custom/test_listen_v2_regen_constraints.py @@ -0,0 +1,57 @@ +""" +Coverage for V2 / Flux listen constraints introduced or corrected in the regen. + + * TurnInfo word ``start`` / ``end`` are OPTIONAL — the API models them as + ``Option`` (it may omit them on some words/paths), so the SDK must + accept a word with neither. Modelling them as required would mis-type + omitted timings on a response model. + * ``ListenV2CloseStreamType`` is the corrected single value ``"CloseStream"`` + — the v2 spec wrongly copied v1's ``Finalize`` / ``CloseStream`` / + ``KeepAlive`` control-message enum; only ``"CloseStream"`` is valid for a + CloseStream message. + * v2 ``profanity_filter`` is exposed (``"true"`` / ``"false"``). +""" + +import typing + +from deepgram.listen.v2.types.listen_v2close_stream import ListenV2CloseStream +from deepgram.listen.v2.types.listen_v2close_stream_type import ListenV2CloseStreamType +from deepgram.listen.v2.types.listen_v2turn_info_words_item import ListenV2TurnInfoWordsItem +from deepgram.types.listen_v2profanity_filter import ListenV2ProfanityFilter + + +class TestWordTimingsOptional: + def test_word_without_timings_is_valid(self): + # The server may omit start/end; a word with neither must validate. + word = ListenV2TurnInfoWordsItem(word="hello", confidence=0.99) + assert word.start is None + assert word.end is None + + def test_word_without_timings_from_dict(self): + word = ListenV2TurnInfoWordsItem.model_validate({"word": "hello", "confidence": 0.99}) \ + if hasattr(ListenV2TurnInfoWordsItem, "model_validate") \ + else ListenV2TurnInfoWordsItem.parse_obj({"word": "hello", "confidence": 0.99}) + assert word.start is None and word.end is None + + def test_word_with_timings(self): + word = ListenV2TurnInfoWordsItem(word="hello", confidence=0.99, start=0.0, end=0.42) + assert word.start == 0.0 + assert word.end == 0.42 + + +class TestCloseStreamTypeShim: + def test_only_close_stream_value(self): + # The shim narrows the type to the single valid literal; the bogus + # Finalize / KeepAlive values (copied from v1) are gone. + assert typing.get_args(ListenV2CloseStreamType) == ("CloseStream",) + + def test_close_stream_message_default_and_roundtrip(self): + assert ListenV2CloseStream().dict().get("type") == "CloseStream" + assert ListenV2CloseStream(type="CloseStream").dict().get("type") == "CloseStream" + + +class TestProfanityFilterV2: + def test_literal_values(self): + # Union[Literal["true", "false"], Any] — assert the documented literals. + literal = typing.get_args(ListenV2ProfanityFilter)[0] + assert set(typing.get_args(literal)) == {"true", "false"} diff --git a/tests/custom/test_socket_client_shims.py b/tests/custom/test_socket_client_shims.py new file mode 100644 index 00000000..7670e9f3 --- /dev/null +++ b/tests/custom/test_socket_client_shims.py @@ -0,0 +1,95 @@ +""" +Coverage for the hand-maintained socket-client shims (frozen in .fernignore), +which previously had no automated tests. A future regen could silently revert +any of these; these tests gate them. + + * ``_sanitize_numeric_types`` — the agent socket client coerces whole-number + floats to ``int`` before send (the API rejects e.g. ``sample_rate=44100.0``), + and ``_send_model`` pipes payloads through it. + * Optional ``message`` param on no-payload control sends — ``send_close_stream()`` + / ``send_keep_alive()`` are callable with no argument and emit the correct + default control message. + * listen/v2 ``send_configure(message: typing.Any)`` — raw passthrough shim + (the generated ``ListenV2Configure`` model is intentionally bypassed). + +The sync socket clients are driven with a fake websocket that captures whatever +is handed to ``.send()`` — the same payload that goes on the wire. +""" + +import json + +from deepgram.agent.v1.socket_client import V1SocketClient, _sanitize_numeric_types +from deepgram.listen.v2.socket_client import V2SocketClient +from deepgram.listen.v2.types.listen_v2close_stream import ListenV2CloseStream + + +class _FakeWebSocket: + """Captures whatever the socket client hands to ``.send()``.""" + + def __init__(self): + self.sent = [] + + def send(self, data): + self.sent.append(data) + + +def _sent_json(ws): + assert len(ws.sent) == 1 + payload = ws.sent[0] + return json.loads(payload) if isinstance(payload, str) else payload + + +class TestSanitizeNumericTypes: + def test_whole_float_becomes_int(self): + result = _sanitize_numeric_types(44100.0) + assert result == 44100 + assert isinstance(result, int) + + def test_non_whole_float_unchanged(self): + result = _sanitize_numeric_types(0.5) + assert result == 0.5 + assert isinstance(result, float) + + def test_recurses_through_dicts_and_lists(self): + out = _sanitize_numeric_types({"sample_rate": 44100.0, "nested": [1.0, 2.5, {"x": 3.0}]}) + assert out == {"sample_rate": 44100, "nested": [1, 2.5, {"x": 3}]} + + def test_passthrough_other_types(self): + assert _sanitize_numeric_types("44100.0") == "44100.0" + assert _sanitize_numeric_types(7) == 7 + assert _sanitize_numeric_types(None) is None + + def test_wired_into_agent_send_model(self): + # _send_model must pipe the serialized payload through _sanitize_numeric_types. + class _Stub: + def dict(self): + return {"sample_rate": 16000.0} + + ws = _FakeWebSocket() + V1SocketClient(websocket=ws)._send_model(_Stub()) + assert _sent_json(ws) == {"sample_rate": 16000} + + +class TestOptionalMessageControlSends: + def test_listen_v2_close_stream_no_arg(self): + ws = _FakeWebSocket() + V2SocketClient(websocket=ws).send_close_stream() + assert _sent_json(ws)["type"] == "CloseStream" + + def test_listen_v2_close_stream_explicit_message(self): + ws = _FakeWebSocket() + V2SocketClient(websocket=ws).send_close_stream(ListenV2CloseStream(type="CloseStream")) + assert _sent_json(ws)["type"] == "CloseStream" + + def test_agent_keep_alive_no_arg(self): + ws = _FakeWebSocket() + V1SocketClient(websocket=ws).send_keep_alive() + assert _sent_json(ws)["type"] == "KeepAlive" + + +class TestSendConfigureRawShim: + def test_passthrough_dict_is_sent_verbatim(self): + ws = _FakeWebSocket() + body = {"type": "Configure", "language_hints": ["en", "es"]} + V2SocketClient(websocket=ws).send_configure(body) + assert _sent_json(ws) == body diff --git a/tests/typecheck/compat_aliases.py b/tests/typecheck/compat_aliases.py index a2db4059..58f626e7 100644 --- a/tests/typecheck/compat_aliases.py +++ b/tests/typecheck/compat_aliases.py @@ -1,4 +1,4 @@ -from typing import assert_type +from typing_extensions import assert_type # 3.10-compatible backport (typing.assert_type is 3.11+) from deepgram.agent.v1.requests import ( AgentV1HistoryContentParams, @@ -30,3 +30,13 @@ assert_type(create_key_request, CreateKeyV1RequestParams) assert_type(history_content, ConversationHistoryMessageParams) assert_type(history_function_calls, FunctionCallHistoryMessageParams) + +# language_hint backward-compat: the deprecated singular kwarg (str or list) must +# keep type-checking on the V2 listen provider, alongside the canonical plural. +# If the deprecated `language_hint` field is ever dropped, these fail type-checking +# and gate the regression (this file is type-checked by CI, not executed). +from deepgram.types.deepgram_listen_provider_v2 import DeepgramListenProviderV2 + +_language_hint_str = DeepgramListenProviderV2(model="flux-general-multi", language_hint="en") +_language_hint_list = DeepgramListenProviderV2(model="flux-general-multi", language_hint=["en", "de"]) +_language_hints_plural = DeepgramListenProviderV2(model="flux-general-multi", language_hints=["fr"])