Skip to content

Latest commit

 

History

History
102 lines (80 loc) · 17.9 KB

File metadata and controls

102 lines (80 loc) · 17.9 KB

Agents

Fern SDK Regeneration

Overview

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.

Freeze classification rules

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:

Never unfreeze (permanently frozen)

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, reconnect parity flag); no Fern equivalent
  • src/deepgram/_secure_logging.py — hand-written security utility that installs a logging.Filter on the websockets client/server loggers to mask the Authorization header in DEBUG handshake logs; called from client.py; no Fern equivalent
  • src/deepgram/helpers/ — hand-written TextBuilder helpers
  • src/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 renames
  • 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
  • tests/custom/test_query_encoder.py — hand-written regression test that core/query_encoder.py coerces Python bools to lowercase "true"/"false" before urlencode so websocket query strings stay wire-correct
  • tests/custom/test_secure_logging.py — hand-written regression test that the websockets Authorization-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 the speed/expressivity connect params)
  • tests/custom/test_text_builder.py, tests/custom/test_transport.py — hand-written tests
  • tests/typecheck/compat_aliases.py — hand-written mypy assert_type coverage for backward-compatible alias TypedDicts
  • tests/manual/ — manual standalone tests
  • .coveragerc — hand-written coverage configuration (branch coverage on, scoped to deepgram, generated data models and package plumbing excluded, fail_under gate). Deliberately kept out of pyproject.toml, which Fern regenerates: freezing pyproject.toml would also block generator dependency updates (see 5c1e845, where a pydantic-core bound fix for issue #701 shipped as a pyproject.toml-only regen change). No Fern counterpart, so it never needs unfreezing.
  • README.md, CHANGELOG.md, CONTRIBUTING.md, reference.md — docs
  • CLAUDE.md, AGENTS.md, .claude/ — agent files
  • .github/, docs/, examples/ — folders

Unfreeze for regen (temporarily frozen)

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 .fernignore and ran the generator, Fern would produce a version of it
  • Our version is a modified copy of what Fern generates (e.g., changed float to int, 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 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/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
  • 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/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/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 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-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.
  • 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.

Unfrozen files with residual manual concerns

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'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.

Prepare repo for regeneration

  1. Create a new branch off main named lo/sdk-gen-<YYYY-MM-DD>.
  2. Push the branch and create a PR titled chore: SDK regeneration <YYYY-MM-DD> (empty commit if needed).
  3. Read .fernignore and classify each entry using the rules above.
  4. For each temporarily frozen file only:
    • Copy the file to <filename>.bak alongside the original.
    • In .fernignore, replace the original path with the .bak path. This protects our patched version from the generator while allowing Fern to overwrite the original.
  5. Never touch permanently frozen entries. Leave them in .fernignore as-is.
  6. Commit as chore: unfreeze files pending regen and push.
  7. The branch is now ready for the Fern generator to push changes.

After regeneration

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.

  1. Diff each .bak file against the new generated version to understand what changed and whether our patches are still needed.
  2. Re-apply any patches that are still necessary to the newly generated files.
  3. In .fernignore, replace each .bak path back to the original path for any files that still need manual patches.
  4. Remove .fernignore entries entirely for any files where the generator now produces correct output (patches no longer needed).
  5. Delete all .bak files once review is complete.
  6. Run tests (pytest) and linting (ruff check, mypy) to verify.
  7. Commit as chore: re-apply manual patches after regen and push.