Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .fern/metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
"skip_validation": true
}
},
"originGitCommit": "11cbef1122366ca086b021f14a892414ffcc035d",
"originGitCommit": "460af9f2b10e5a374517a3a9e40e3045aaa59a08",
"originGitCommitIsDirty": true,
"invokedBy": "manual",
"sdkVersion": "7.10.0"
}
}
22 changes: 5 additions & 17 deletions .fernignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,6 @@ src/deepgram/listen/v2/socket_client.py
src/deepgram/speak/v1/socket_client.py
src/deepgram/speak/v2/socket_client.py

# The generated eot_threshold descriptions omit that eot_timeout_ms still ends
# idle turns. The DeepgramListenProviderV2 model below shares this patch with
# its language_hint compatibility shim. Remove these patches after the API
# definition includes the timeout requirement and Fern regenerates them.
# [temporarily frozen]
src/deepgram/types/listen_v2eot_threshold.py
src/deepgram/requests/deepgram_listen_provider_v2.py

# Agent TTS provider expressivity must reject fractional values rather than
# allowing Pydantic v1 to truncate them before the API sees the request.
# [temporarily frozen — remove when Fern emits a strict integer]
Expand Down Expand Up @@ -131,11 +123,8 @@ src/deepgram/requests/deepgram_listen_provider_v2language_hint.py
# 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. This DeepgramListenProviderV2 model also carries the EOT description correction:
# eot_threshold=1.0 suppresses confidence-based detection only; eot_timeout_ms still ends
# idle turns and must be increased for manual ForceEndTurn control. Remove that correction
# when the API definition provides it; remove the shim and unfreeze when the singular alias
# is intentionally retired in a future major release.
# key. Remove and unfreeze these shims 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
Expand All @@ -157,10 +146,9 @@ src/deepgram/agent/v1/types/agent_v1update_listen_listen.py
# 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.
# Re-apply genuine new generator additions to these files manually after each regen.
# Temporarily frozen generated package barrels that carry hand-applied legacy alias
# re-exports. Before each regen, swap these originals to .bak paths so Fern can add new
# exports; then restore the aliases alongside every genuine generated addition.
src/deepgram/__init__.py
src/deepgram/agent/__init__.py
src/deepgram/agent/v1/__init__.py
Expand Down
5 changes: 3 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ Current temporarily frozen files:
- `.gitignore` — Fern generates a baseline version and has regenerated it before (`fdcce88`, `ad93815`, `4bee463`). We hand-add the local coverage artifact ignores (`.coverage`, `htmlcov/`, `coverage.xml`). Before each regen, unfreeze and re-diff so Fern's own additions are picked up, then re-apply the three coverage lines.
- `src/deepgram/speak/v1/socket_client.py` — optional message param defaults, broad exception catch
- `src/deepgram/speak/v2/socket_client.py` — same (optional `send_flush`/`send_close`/`send_interrupt` defaults, broad exception catch); new websocket TTS client added in the 2026-07-08 regen. `send_interrupt` carries no required payload so it takes the same optional-default treatment as the other control sends; `send_configure` deliberately keeps its required argument (a Configure with no settings is meaningless)
- `src/deepgram/types/listen_v2eot_threshold.py`, `src/deepgram/requests/deepgram_listen_provider_v2.py` — complete the generated `eot_threshold=1.0` description: it suppresses confidence-based detection, while `eot_timeout_ms` still ends idle turns. Unfreeze when the API definition provides that qualifier.
- `src/deepgram/types/speak_settings_v1provider.py`, `src/deepgram/types/deepgram.py` — validate Agent TTS `expressivity` as `pydantic.StrictInt` so Pydantic v1 rejects fractional values instead of truncating them before they reach the API. Regression coverage in `tests/custom/test_socket_client_shims.py`. Unfreeze when Fern emits a strict integer.
- `src/deepgram/listen/v1/socket_client.py` — same
- `src/deepgram/listen/v2/socket_client.py` — same (broad except, optional `send_close_stream` default). As of the 2026-08-11 regen the generator properly types `send_configure(ListenV2Configure)` and puts `ListenV2ConfigureSuccess` in the response Union, so those are taken from the generator; the only `send_configure` patch retained is runtime tolerance for a raw dict (sent verbatim) for back-compat with pre-typed-model callers
Expand All @@ -71,7 +70,7 @@ Current temporarily frozen files:
- `src/deepgram/core/api_error.py`, `src/deepgram/core/parse_error.py` — credential redaction. Every websocket `connect()` path raises `ApiError(headers=dict(headers), ...)` with the full request headers, and both error types stringify that dict, so an unredacted `Authorization` reached `str(e)`, tracebacks, log aggregators and error trackers (which serialise attributes as well as the message). Both now mask credential values at construction via `_secure_logging.redact_sensitive_headers`, preserving non-sensitive headers (`dg-request-id`) for debugging. This is the same threat `_secure_logging.py` covers for the `websockets` DEBUG handshake logs, via the other path to it. Regression coverage in `tests/custom/test_api_error_redaction.py`. Unfreeze if the generator starts redacting credentials itself.
- `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.
- `src/deepgram/listen/v2/types/listen_v2connected.py`, `src/deepgram/listen/v2/types/listen_v2turn_info.py`, `src/deepgram/listen/v2/types/listen_v2turn_info_words_item.py`, `src/deepgram/listen/v2/types/listen_v2configure_success.py`, `src/deepgram/listen/v2/types/listen_v2configure_success_thresholds.py`, `src/deepgram/listen/v2/types/listen_v2configure_failure.py`, `src/deepgram/listen/v2/types/listen_v2fatal_error.py` — read-side compatibility for the SDK 7.7 Listen V2 response retype. Through 7.6, `V2SocketClientResponse` contained `typing.Any`, so every response was returned as a raw dict; fixing the union made responses typed models and broke callers using `response["field"]`. These generated response classes inherit the hand-written base above, preserving read-only wire-key subscript access alongside canonical attribute access. Restore direct `UncheckedBaseModel` inheritance and unfreeze these files in the next 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` — behavioural back-compat shim for the `language_hint` -> `language_hints` rename (2026-06-15 regen). The public field was historically (incorrectly) singular and accepted 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). Each carries a hand-added `model_validator(mode='before')` / `root_validator(pre=True)` that remaps a legacy `language_hint=` kwarg and drops the dead singular key. `deepgram_listen_provider_v2.py` also corrects the EOT description: `eot_threshold=1.0` suppresses confidence-based detection only, while `eot_timeout_ms` still ends idle turns and must be increased for manual ForceEndTurn control. Remove the EOT patch when the API definition provides it; remove and unfreeze the language alias when it is retired in a future major.
- `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` — behavioural back-compat shim for the `language_hint` -> `language_hints` rename (2026-06-15 regen). The public field was historically (incorrectly) singular and accepted 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). Each carries a hand-added `model_validator(mode='before')` / `root_validator(pre=True)` that remaps a legacy `language_hint=` kwarg and drops the dead singular key. Remove and unfreeze the language alias when it is retired in a future major.
- `src/deepgram/agent/v1/types/agent_v1update_listen_listen.py` — backward-compat patch for the 2026-07-31 `AgentV1UpdateListen` provider retype. The `provider` field changed from a bare `DeepgramListenProviderV2` to the required discriminated union `AgentV1UpdateListenListenProvider` (`_V1`/`_V2`, discriminant `version`). Carries a hand-added `model_validator(mode='before')` / `root_validator(pre=True)` that coerces a legacy `DeepgramListenProviderV1`/`V2` (or a dict lacking the `version` discriminant) into the new shape so existing callers keep working. Remove and unfreeze when the old provider payloads are retired in a future major. NOTE: this patch was silently lost once (it was absent from `.fernignore`, so a regen overwrote it) — keep it frozen.
- `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
- `tests/wire/test_manage_v1_projects_requests.py` — restored query-parameter coverage for `manage.v1.projects.requests.list`. The 2026-08-11 regen simplified the upstream spec *example*, and Fern derives the wire test from the example, so all ten optional query params (and the `datetime` → ISO-8601 `Z` encoding) lost their assertions while the client signature still forwarded them. Frozen for the same reason as the `_keys.py` entry above.
Expand All @@ -83,6 +82,8 @@ Current temporarily frozen files:

Files Fern now owns outright, but that carry a caveat worth knowing before the next regen:

- `src/deepgram/types/listen_v2eot_threshold.py`, `src/deepgram/requests/deepgram_listen_provider_v2.py` — Fern emits the `eot_threshold=1.0` timeout and ForceEndTurn documentation natively as of the 2026-09-23 regeneration, so the prior temporary documentation patches were removed.

- `src/deepgram/core/client_wrapper.py` — unfrozen in the 2026-08-11 regen. The generator's `runtime_version: true` mode now derives the wire version from `importlib.metadata.version("deepgram-sdk")`, which supersedes the old hand-applied `# x-release-please-version` annotations (its `generic` entry was removed from `.github/release-please-config.json` at the same time, since with the annotations gone it was a no-op). **Caveat:** the `except PackageNotFoundError` fallback is a hardcoded version literal that nothing bumps — release-please's `extra-files` now covers only `pyproject.toml` and `.fern/metadata.json`. It only applies where package metadata is undiscoverable (source checkout on `sys.path`, vendored copy, zipapp, some frozen bundles), and it is refreshed only by the next regen. Accepted deliberately rather than re-freezing the file; if that fallback ever needs to be accurate on those paths, re-add a release-please `generic` entry with an annotation on the fallback line.
- `src/deepgram/agent/v1/types/agent_v1latency_report.py` and its `requests/` twin — unfrozen in the 2026-08-11 regen because the spec restored `stt_latency` natively, making the hand-added read-side field redundant. `tests/custom/test_latency_report_stt_compat.py` stays frozen and still guards the field's presence, so a future spec removal is caught rather than silently re-breaking readers.

Expand Down
7 changes: 5 additions & 2 deletions examples/14-transcription-live-websocket-v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from deepgram.core.events import EventType
from deepgram.listen.v2.types import (
ListenV2CloseStream,
ListenV2Configure,
ListenV2Connected,
ListenV2FatalError,
ListenV2TurnInfo,
Expand All @@ -35,8 +36,7 @@
with client.listen.v2.connect(
model="flux-general-en",
# Flux STT numerals: format spoken numbers as digits (e.g. "one twenty" -> "120").
# Connection-time only — it cannot be toggled mid-stream via Configure.
numerals="true",
numerals="false",
) as connection:

def on_message(message: ListenV2SocketClientResponse) -> None:
Expand All @@ -62,6 +62,9 @@ def on_message(message: ListenV2SocketClientResponse) -> None:
connection.on(EventType.CLOSE, lambda _: print("Connection closed"))
connection.on(EventType.ERROR, lambda error: print(f"Error: {type(error).__name__}: {error}"))

# Update formatting for turns transcribed after this Configure message.
connection.send_configure(ListenV2Configure(numerals=True))

# Send audio in a background thread so start_listening can process responses
def send_audio():
audio_path = Path(__file__).parent / "fixtures" / "audio.wav"
Expand Down
Loading
Loading