feat!: regenerate SDK for API 2026-09-13 with consent-challenge voice cloning - #26
feat!: regenerate SDK for API 2026-09-13 with consent-challenge voice cloning#26luke-speechify wants to merge 3 commits into
Conversation
|
Holding this as draft until the SSE response is modelled upstream. Live verification against production found The response is generated as A raw HTTP control request to the same endpoint returns 94 chunks + 1 done, 19,629 audio bytes, and word marks that reconcile exactly against the input. The transport is fine — the SDK discards the data. Also found:
All of this is in generated code, so the fixes belong in the Fern definition — patching here would be erased on the next regeneration. The release-please wiring in this PR is verified and stays as-is. Everything else passed: 24/30 live cases, all 10 endpoints exercised, all four |
Removes the dangling $.project.version jsonpath ([project] is dynamic), covers .fern/metadata.json, stamps the Fern-generated version strings from the release tag at publish time, restores AGENTS.md and manual-publish.yml behind .fernignore, and gates publish on a version/tag assertion. client_wrapper.py is Fern-generated and cannot be .fernignore'd, so an x-release-please-version marker does not survive there: every regeneration strips it and the generic updater goes back to silently no-opping, leaving User-Agent and X-Fern-SDK-Version reporting a version the SDK is not. That is the same class of failure that shipped 2.0.1 to PyPI under a 3.0.0 tag. The generic extra-files entry and both markers are gone. The publish job now rewrites those two literals from the release tag before the assertion and before poetry publish --build. The stamp fails the build when a target literal is not found, so a regeneration that renames or restructures those lines breaks the release loudly instead of shipping a stale version silently. Order is stamp, assert, publish. The stamp applies to the CI checkout only and is never committed back, so those two strings read stale on main between releases by design; the published artifact is always built from the stamped and asserted tree. AGENTS.md documents this so it does not get "fixed" later.
70c9af5 to
48676ad
Compare
Summary
Full SDK regeneration against API version
2026-09-13. Adds the models endpoint, the consent-challenge flow for voice cloning, and streaming with word-level timestamps. Also repairs the release-please wiring so every version-bearing value in a published release matches the release tag.Breaking changes
voices.create()signatureThe
consent: strparameter is gone. Voice cloning now goes through a two-step consent challenge:Callers pinned to a
Speechify-Versionbefore2026-09-13keep the old flow: no challenge, and aconsentform field carrying the speaker's name and email as a JSON string. That flow is deprecated and will be removed after a sunset window announced in the changelog.GetStreamRequestModelimport pathIt is no longer re-exported from
speechify.audio, so the old import raisesImportError. It is also available from the package root asfrom speechify import GetStreamRequestModel.Default API version
Bumped from
2026-07-07to2026-09-13. Pinversion=explicitly to stay on the old behaviour.New
client.models.list()— available TTS modelsclient.voices.consent_challenges.create()— consent challenge flowclient.audio.stream_with_timestamps()— SSE stream with word-level speech marksvoices.list()filters:type,locale,gender,modelContentTooLargeError(413) and new error codesRelease wiring
The generic release-please updater was silently no-opping — that is what shipped
2.0.1to PyPI under the3.0.0tag. The release commit for 3.0.0 touched only the manifest and the changelog;poetry buildthen read a stale[tool.poetry].version. Fixed here:$.project.versionjsonpath.[project]isdynamicand has noversionkey, so the TOML updater loggedNo entries modifiedand no-opped — it implied coverage that did not exist$.tool.poetry.version, which is load-bearing: the defaultpythonrelease-type resolvesproject || tool.poetry, hits our[project]table first, seesdynamic = ["version"]and skips the file entirely. Without theextra-filesentry nothing bumps the artifact version.fern/metadata.jsontoextra-files(it was stale at2.0.1). Thejsonupdater is jsonpath-based, so it needs no marker and survives regenerationgenericextra-files entry forclient_wrapper.py, and thex-release-please-versionmarkers with it. That file is Fern-generated and cannot be.fernignored, so every regeneration strips the markers and the updater silently stops bumpingUser-AgentandX-Fern-SDK-Version— re-arming the exact trap that caused the 3.0.0 incident. A fix that lives inside a generated file is not a fixpublishjob. Order is stamp → assert → publish. The stamp handles a prerelease suffix (4.0.0-rc.1stays4.0.0-rc.1) and fails the build if a target literal is not found, so a regeneration that restructures those lines breaks the release loudly instead of shipping a stale version quietlyThe stamp rewrites the CI checkout only and is never committed back.
client_wrapper.pyonmaintherefore reads stale between releases by design — the published artifact is always built from the stamped and asserted tree.AGENTS.mddocuments this so nobody "fixes" it later.Verified by running release-please's own updater classes against this config for a simulated bump to
4.0.0:$.tool.poetry.versionand.fern/metadata.jsonboth update, and neither depends on a marker. The same run confirms the generic updater no-ops silently on the marker-free file, which is why it is gone. The stamp and assertion scripts were extracted from the workflow and executed against fixture trees: matching tag, un-stamped tree, restructured literal, empty tag, non-semver tag and prerelease tag all behave as intended, and the stamp is idempotent.Merge with squash so the conventional title and this footer land on
main.BREAKING CHANGE:
voices.create()no longer acceptsconsent; it requiresconsent_challenge_idandconsent_recording.BREAKING CHANGE:
GetStreamRequestModelmoved fromspeechify.audiotospeechify.types.BREAKING CHANGE: default API version is now
2026-09-13.Release-As: 4.0.0