This SDK is generated by Fern. Most files under src/deepgram/ are auto-generated and should not be edited directly. Some files have manual patches and are listed in .fernignore to prevent the generator from overwriting them.
When a new Fern generator release is available, we prepare the repo so the generator can overwrite previously-frozen files, then re-apply manual patches after reviewing the diff.
Every entry in .fernignore falls into one of two categories. The comment above each entry in .fernignore indicates which category it belongs to, but when in doubt, apply these rules:
These files are entirely hand-written — they have no Fern-generated counterpart. The generator would delete or replace them with something unrelated. They must stay in .fernignore at all times.
How to identify:
- The file was created by us, not by Fern (e.g.,
src/deepgram/client.py, custom tests, helpers, transport layer) - The file is a doc, config, or folder we maintain independently (README, CHANGELOG, .github, examples, etc.)
- The file lives outside
src/deepgram/in a hand-maintained location (e.g.,.claude/,docs/)
Current permanently frozen files:
src/deepgram/client.py— entirely custom (Bearer auth, session ID,transport_factory,reconnectparity flag); no Fern equivalentsrc/deepgram/_secure_logging.py— hand-written security utility that installs alogging.Filteron thewebsocketsclient/server loggers to mask theAuthorizationheader in DEBUG handshake logs; called fromclient.py; no Fern equivalentsrc/deepgram/helpers/— hand-written TextBuilder helperssrc/deepgram/agent/v1/types/agent_v1history_content.py,src/deepgram/agent/v1/types/agent_v1history_function_calls.py,src/deepgram/agent/v1/types/agent_v1settings_agent_context_messages_item.py,src/deepgram/agent/v1/types/agent_v1settings_agent_context_messages_item_content.py,src/deepgram/agent/v1/types/agent_v1settings_agent_context_messages_item_content_role.py,src/deepgram/agent/v1/types/agent_v1settings_agent_context_messages_item_function_calls.py,src/deepgram/agent/v1/types/agent_v1settings_agent_context_messages_item_function_calls_function_calls_item.py— hand-written compatibility aliases preserving old public Agent History type imports after regen renamessrc/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 renamessrc/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 toCreateKeyV1Requestsrc/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 consolidatedAgentV1SettingsAgent[Context]ListenProviderV{1,2,V2LanguageHint}into top-levelDeepgramListenProvider*typessrc/deepgram/types/deepgram_listen_provider_v2language_hint.py,src/deepgram/requests/deepgram_listen_provider_v2language_hint.py— hand-written shims recreating the top-levelDeepgramListenProviderV2LanguageHint/...Paramstype (Union[str, List[str]]) that Fern removed in the 2026-06-15 regen. The*V2LanguageHintlisten-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 againsrc/deepgram/listen/v2/types/listen_v2close_stream_type.py— hand-written shim recreatingListenV2CloseStreamType, which Fern removed in the 2026-06-15 regen (docs #946). The original generated type wrongly allowedUnion[Literal["Finalize","CloseStream","KeepAlive"], Any](v2 copied v1's control-message enum); a CloseStream message'stypecan only ever be"CloseStream". Recreated as the correctedLiteral["CloseStream"]to preserve the public import path without resurrecting the invalid values. Re-exported from the threelisten__init__.pyfiles (temporarily frozen, below).src/deepgram/transport_interface.py,src/deepgram/transport.py,src/deepgram/transports/— custom transport layertests/custom/test_agent_history.py— hand-written regression test for Agent History websocket payload parsingtests/custom/test_compat_aliases.py— hand-written regression test for backward-compatible alias imports after regen renamestests/custom/test_query_encoder.py— hand-written regression test thatcore/query_encoder.pycoerces Python bools to lowercase"true"/"false"beforeurlencodeso websocket query strings stay wire-correcttests/custom/test_secure_logging.py— hand-written regression test that thewebsocketsAuthorization-header DEBUG logs are redacted (API key never logged in clear text)tests/custom/test_speak_v2_interrupt_configure.py— hand-written coverage for the Speak V2 barge-in / mid-stream reconfigure surface (send_interrupt,send_configure,SpeechInterrupted,ConfigureSuccess/ConfigureFailure, and thespeed/expressivityconnect params)tests/custom/test_text_builder.py,tests/custom/test_transport.py— hand-written teststests/typecheck/compat_aliases.py— hand-written mypyassert_typecoverage for backward-compatible alias TypedDictstests/manual/— manual standalone tests.coveragerc— hand-written coverage configuration (branch coverage on, scoped todeepgram, generated data models and package plumbing excluded,fail_undergate). Deliberately kept out ofpyproject.toml, which Fern regenerates: freezingpyproject.tomlwould also block generator dependency updates (see5c1e845, where apydantic-corebound fix for issue #701 shipped as apyproject.toml-only regen change). No Fern counterpart, so it never needs unfreezing.README.md,CHANGELOG.md,CONTRIBUTING.md,reference.md— docsCLAUDE.md,AGENTS.md,.claude/— agent files.github/,docs/,examples/— folders
These files are Fern-generated but carry manual patches to fix issues in the generator output. We freeze them to protect our patches between regenerations, but unfreeze them before a regen so we can compare the new output against our patches.
How to identify:
- The file exists in Fern's output — if you removed it from
.fernignoreand ran the generator, Fern would produce a version of it - Our version is a modified copy of what Fern generates (e.g., changed
floattoint, added optional defaults, broadened a Union type)
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 catchsrc/deepgram/speak/v2/socket_client.py— same (optionalsend_flush/send_close/send_interruptdefaults, broad exception catch); new websocket TTS client added in the 2026-07-08 regen.send_interruptcarries no required payload so it takes the same optional-default treatment as the other control sends;send_configuredeliberately keeps its required argument (a Configure with no settings is meaningless)src/deepgram/listen/v1/socket_client.py— samesrc/deepgram/listen/v2/socket_client.py— same (broad except, optionalsend_close_streamdefault). As of the 2026-08-11 regen the generator properly typessend_configure(ListenV2Configure)and putsListenV2ConfigureSuccessin the response Union, so those are taken from the generator; the onlysend_configurepatch retained is runtime tolerance for a raw dict (sent verbatim) for back-compat with pre-typed-model callerssrc/deepgram/agent/v1/socket_client.py— same +_sanitize_numeric_typessrc/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 callableAgentV1SettingsAgent(...), keepAgentV1Settings.agentaccepting both that wrapper andagent_idstrings, restore the legacy request TypedDict shapes, remap legacymessages=[...]/ nestedcontext=AgentV1SettingsAgentContext(messages=[...])usage into the newcontext={"messages": [...]}wire shape, and keep read-sideobj.messagesaccess working.src/deepgram/core/api_error.py,src/deepgram/core/parse_error.py— credential redaction. Every websocketconnect()path raisesApiError(headers=dict(headers), ...)with the full request headers, and both error types stringify that dict, so an unredactedAuthorizationreachedstr(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.pycovers for thewebsocketsDEBUG handshake logs, via the other path to it. Regression coverage intests/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 reachurllib.parse.urlencode(which would otherwise produce"True"/"False"viastr()and break websocket query strings). Only the four*/connect()paths callurlencode; 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 asbooleanend-to-end), this can be unfrozen.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 thelanguage_hint->language_hintsrename (2026-06-15 regen). The public field was historically (incorrectly) singular and accepted a str or a list; the API field islanguage_hints(a list, and the server usesdeny_unknown_fieldsso the singular key is rejected on the wire). Each carries a hand-addedmodel_validator(mode='before')/root_validator(pre=True)that remaps a legacylanguage_hint=kwarg and drops the dead singular key. Remove and unfreeze when the singular alias is retired in a future major.src/deepgram/agent/v1/types/agent_v1update_listen_listen.py— backward-compat patch for the 2026-07-31AgentV1UpdateListenprovider retype. Theproviderfield changed from a bareDeepgramListenProviderV2to the required discriminated unionAgentV1UpdateListenListenProvider(_V1/_V2, discriminantversion). Carries a hand-addedmodel_validator(mode='before')/root_validator(pre=True)that coerces a legacyDeepgramListenProviderV1/V2(or a dict lacking theversiondiscriminant) 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 legacyCreateKeyV1RequestOneParamsrequest alias so future regens do not silently drop that compatibility checktests/wire/test_manage_v1_projects_requests.py— restored query-parameter coverage formanage.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 thedatetime→ ISO-8601Zencoding) lost their assertions while the client signature still forwarded them. Frozen for the same reason as the_keys.pyentry above.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__.pyfiles carrying hand-applied legacy alias re-exports forCreateKeyV1RequestOne,AgentV1HistoryContent,AgentV1HistoryFunctionCalls,AgentV1SettingsAgentContextMessagesItemContent,AgentV1SettingsAgentContextMessagesItemFunctionCalls(and their*Paramsvariants). 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__.pyfiles re-exporting the hand-writtenListenV2CloseStreamTypeshim. Frozen so Fern won't strip the re-export on regen. Same handling as the package__init__.pyfiles above: after unfreezing, re-apply theListenV2CloseStreamTypere-export plus any genuine new entries Fern added.
Files Fern now owns outright, but that carry a caveat worth knowing before the next regen:
src/deepgram/core/client_wrapper.py— unfrozen in the 2026-08-11 regen. The generator'sruntime_version: truemode now derives the wire version fromimportlib.metadata.version("deepgram-sdk"), which supersedes the old hand-applied# x-release-please-versionannotations (itsgenericentry was removed from.github/release-please-config.jsonat the same time, since with the annotations gone it was a no-op). Caveat: theexcept PackageNotFoundErrorfallback is a hardcoded version literal that nothing bumps — release-please'sextra-filesnow covers onlypyproject.tomland.fern/metadata.json. It only applies where package metadata is undiscoverable (source checkout onsys.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-pleasegenericentry with an annotation on the fallback line.src/deepgram/agent/v1/types/agent_v1latency_report.pyand itsrequests/twin — unfrozen in the 2026-08-11 regen because the spec restoredstt_latencynatively, making the hand-added read-side field redundant.tests/custom/test_latency_report_stt_compat.pystays frozen and still guards the field's presence, so a future spec removal is caught rather than silently re-breaking readers.
- Create a new branch off
mainnamedlo/sdk-gen-<YYYY-MM-DD>. - Push the branch and create a PR titled
chore: SDK regeneration <YYYY-MM-DD>(empty commit if needed). - Read
.fernignoreand classify each entry using the rules above. - For each temporarily frozen file only:
- Copy the file to
<filename>.bakalongside the original. - In
.fernignore, replace the original path with the.bakpath. This protects our patched version from the generator while allowing Fern to overwrite the original.
- Copy the file to
- Never touch permanently frozen entries. Leave them in
.fernignoreas-is. - Commit as
chore: unfreeze files pending regenand push. - The branch is now ready for the Fern generator to push changes.
The .bak files are our manually-patched versions (protected by .fernignore). The original paths now contain the freshly generated versions. By comparing the two, we can see what the generator now produces vs what we had patched.
- Diff each
.bakfile against the new generated version to understand what changed and whether our patches are still needed. - Re-apply any patches that are still necessary to the newly generated files.
- In
.fernignore, replace each.bakpath back to the original path for any files that still need manual patches. - Remove
.fernignoreentries entirely for any files where the generator now produces correct output (patches no longer needed). - Delete all
.bakfiles once review is complete. - Run tests (
pytest) and linting (ruff check,mypy) to verify. - Commit as
chore: re-apply manual patches after regenand push.