docs(api-ref): rewrite Lightning v3.1 + Pulse endpoint reference pages#137
Conversation
EntelligenceAI PR SummaryThis PR delivers a comprehensive API documentation rewrite for Lightning v3.1 and Pulse endpoints alongside several doc and CI fixes.
Confidence Score: 3/5 - Review RecommendedNot safe to merge without fixes — this PR delivers a substantial documentation rewrite for Lightning v3.1 and Pulse endpoints with good breadth of coverage, but contains two unresolved functional correctness issues that would ship broken documentation to users. The Python code example in Key Findings:
Files requiring special attention
|
| audio = client.waves.synthesize_lightning_v31( | ||
| text="Hello from Lightning v3.1.", | ||
| voice_id="magnus", | ||
| sample_rate=24000, | ||
| output_format="wav", | ||
| # Optional: cloned voice support | ||
| # voice_id="voice_FlPKRWI7DX", | ||
| # Optional: pin pronunciations for specific words | ||
| # pronunciation_dicts=["<your dict id>"], | ||
| ) |
There was a problem hiding this comment.
v31 but SDK defines v3_1 (underscore) — fern/apis/waves-v4/overrides/lightning-v3.1-openapi-overrides.yaml calls client.waves.synthesize_lightning_v31(...) (line 51) and client.waves.synthesize_sse_lightning_v31(...) (line 137). However, the SDK method names are defined in fern/apis/waves/openapi/lightning-v3.1-openapi-overrides.yaml (lines 5 and 15) as synthesize_lightning_v3_1 and synthesize_sse_lightning_v3_1 respectively — with an underscore between 3 and 1. The v4 overrides file applies to the same base spec (../waves/openapi/lightning-v3.1-openapi.yaml, per generators.yml lines 19–20) and never redefines x-fern-sdk-method-name, so the inherited name has the underscore. Any reader copy-pasting these examples will get AttributeError: 'WavesClient' object has no attribute 'synthesize_lightning_v31'.
🤖 AI Agent Prompt for Cursor/Windsurf
📋 Copy this prompt to your AI coding assistant (Cursor, Windsurf, etc.) to get help fixing this issue
In `fern/apis/waves-v4/overrides/lightning-v3.1-openapi-overrides.yaml`, change `synthesize_lightning_v31` → `synthesize_lightning_v3_1` (line 51) and `synthesize_sse_lightning_v31` → `synthesize_sse_lightning_v3_1` (line 137) to match the method names defined in `fern/apis/waves/openapi/lightning-v3.1-openapi-overrides.yaml`.
| ```python | ||
| from smallestai.waves import WavesStreamingTTS, TTSConfig | ||
|
|
||
| config = TTSConfig(voice_id="magnus", api_key="YOUR_API_KEY", sample_rate=24000) | ||
| tts = WavesStreamingTTS(config) | ||
|
|
||
| def text_chunks(): | ||
| # Pretend this is your LLM streaming tokens. | ||
| for word in ["Hello,", " I am", " streaming", " speech."]: | ||
| yield word | ||
|
|
||
| with open("speech.pcm", "wb") as out: | ||
| for audio_chunk in tts.synthesize_streaming(text_chunks(), continue_stream=True, auto_flush=True): | ||
| out.write(audio_chunk) |
There was a problem hiding this comment.
WavesStreamingTTS promoted for v3.1 WS but documented as broken (hardcodes v2 URL) — lightning-v3.1-ws-overrides.yml (lines 44–57) presents WavesStreamingTTS as the primary Python example for the v3.1 WebSocket endpoint: from smallestai.waves import WavesStreamingTTS, TTSConfig / tts.synthesize_streaming(...). However, fern/products/waves/versions/v4.0.0/text-to-speech/stream-tts.mdx (lines 128–131) contains an explicit ci:skip comment stating: "WavesStreamingTTS hardcodes the deprecated lightning-v2 WS URL and a non-canonical host. Fix ships in the next SDK release (see PR #128 coordination). Use the cURL / Node.js examples above as the canonical pattern until then." The new WS override page therefore advertises a class known to connect to the wrong endpoint (v2 URL, not v3.1), contradicting the warning already in the repo and misleading users who try the example.
🤖 AI Agent Prompt for Cursor/Windsurf
📋 Copy this prompt to your AI coding assistant (Cursor, Windsurf, etc.) to get help fixing this issue
In `lightning-v3.1-ws-overrides.yml`, replace the `WavesStreamingTTS` Python example with a direct `websockets` example (matching the `ws` JavaScript example already on the page), or add the same `ci:skip` warning present in `stream-tts.mdx` lines 128–131 so users know the class targets the v2 URL until the SDK fix ships.
|
|
||
| client = SmallestAI(token="YOUR_API_KEY") | ||
| with open("./call.wav", "rb") as f: | ||
| result = client.waves.transcribe_pulse( |
There was a problem hiding this comment.
transcribe_pulse but SDK method name is pulse — The v4 override sets x-fern-sdk-method-name: pulse (line 8), so the Fern-generated SDK exposes client.waves.pulse(...). The example calls client.waves.transcribe_pulse(...) — the old name from the non-v4 waves override — which will raise AttributeError for any user running this snippet.
🤖 AI Agent Prompt for Cursor/Windsurf
📋 Copy this prompt to your AI coding assistant (Cursor, Windsurf, etc.) to get help fixing this issue
In file `fern/apis/waves-v4/overrides/pulse-stt-openapi-overrides.yaml`, line 53, the Python code example calls `client.waves.transcribe_pulse(`. The `x-fern-sdk-method-name` for this endpoint is `pulse` (line 8), not `transcribe_pulse`. Change line 53 from `result = client.waves.transcribe_pulse(` to `result = client.waves.pulse(`.
|
File:
Note: This comment was posted as a general PR comment because the specific line could not be resolved in the diff. |
| Identify and label different speakers in the audio | ||
| </Card> | ||
| <Card title="Gender Detection" icon="user" href="/waves/documentation/speech-to-text-pulse/features/age-and-gender-detection"> | ||
| <Card title="Gender Detection" icon="user" href="/waves/documentation/speech-to-text-pulse/features/gender-detection"> |
There was a problem hiding this comment.
/waves/documentation/speech-to-text-pulse/features/gender-detection has no corresponding file in the repo — glob finds only fern/products/waves/pages/v4.0.0/speech-to-text/features/age-and-gender-detection.mdx and its sibling in versions/. Clicking this card will result in a 404 for all users.
🤖 AI Agent Prompt for Cursor/Windsurf
📋 Copy this prompt to your AI coding assistant (Cursor, Windsurf, etc.) to get help fixing this issue
In fern/products/waves/pages/v4.0.0/speech-to-text/pre-recorded/features.mdx at line 20, the href was changed to `/waves/documentation/speech-to-text-pulse/features/gender-detection` but no `gender-detection.mdx` page exists in the repo. Either: (a) revert the href back to `/waves/documentation/speech-to-text-pulse/features/age-and-gender-detection` to match the existing page file, OR (b) create the missing `gender-detection.mdx` page (and its counterpart under `versions/`) and ensure the Fern routing resolves it. The existing file is at `fern/products/waves/pages/v4.0.0/speech-to-text/features/age-and-gender-detection.mdx`.
| Identify and label different speakers in the audio | ||
| </Card> | ||
| <Card title="Gender Detection" icon="user" href="/waves/documentation/speech-to-text-pulse/features/age-and-gender-detection"> | ||
| <Card title="Gender Detection" icon="user" href="/waves/documentation/speech-to-text-pulse/features/gender-detection"> |
There was a problem hiding this comment.
/waves/documentation/speech-to-text-pulse/features/gender-detection has no corresponding file — glob finds only age-and-gender-detection.mdx; no gender-detection.mdx exists anywhere in the repo. The age-detection removal changelog explicitly preserved the old path, stating "The file path is unchanged so existing /features/age-and-gender-detection links keep resolving" — this change breaks that guarantee.
🤖 AI Agent Prompt for Cursor/Windsurf
📋 Copy this prompt to your AI coding assistant (Cursor, Windsurf, etc.) to get help fixing this issue
In file `fern/products/waves/versions/v4.0.0/speech-to-text/pre-recorded/features.mdx`, line 20, revert the href back to the existing page path. Change: `href="/waves/documentation/speech-to-text-pulse/features/gender-detection"` → `href="/waves/documentation/speech-to-text-pulse/features/age-and-gender-detection"`. The file `age-and-gender-detection.mdx` was intentionally kept at its original path (confirmed by the changelog entry at `fern/products/waves/pages/v4.0.0/changelog-entries/pulse-stt/2026-05-06-pulse-stt-age-detection-removed.mdx`), and no `gender-detection.mdx` file exists in the repo.
Rewrites the v4 docs overrides for all 5 endpoints: - POST /waves/v1/lightning-v3.1/get_speech (sync REST) - POST /waves/v1/lightning-v3.1/stream (SSE) - WSS /waves/v1/lightning-v3.1/get_speech/stream - POST /waves/v1/pulse/get_text (pre-recorded REST) - WSS /waves/v1/pulse/get_text (realtime) Each description now follows the same shape: one-paragraph "what", "when to use this" comparison against alternatives, how-it-works walkthrough, copy-paste cURL/Python/JS examples, and gotchas drawn from real customer reports. All examples were live-tested against api.smallest.ai before commit. JS samples use fetch / ws directly — the official smallestai npm package (v1.0.1) predates Lightning v3.1 + Pulse, so the SDK route only covers legacy v1/v2/large endpoints. Each gotcha section calls this out for JS users.
- Lightning v3.1 output_format: drop bogus "mulaw" (not in enum), add "ulaw" and "alaw" which are the real spec values - SSE Python example: add missing import sys (snippet calls sys.stdout.buffer.write but never imported it) - Drop all Lightning v2 mentions — v2 is deprecated per the existing changelog entry 2026-05-02-lightning-v2-deprecated-in-docs.mdx, so the new comparison sections no longer promote it as an alternative - Replace risky guessed-slug internal links (./synthesize-lightning-v3-1, ./streaming-tts-stream, ./pulse-pre-recorded, etc.) with plain-text descriptions of the alternative endpoints. Avoids 404s if Fern's slug generation differs from what was guessed - Move changelog entry into changelog-entries/general/ so it registers in the nav (was an orphan and failed CI) Note: the Python SDK method names used in examples (synthesize_lightning_v31, synthesize_sse_lightning_v31, transcribe_pulse) were live-verified against the installed SDK at HEAD of fern-demo/smallest-ai-python-sdk main — those are the real generated names. The bot's suggestion that they should be v3_1 / pulse was based on the override spec, but Fern's actual generation collapses the dots into "v31" and uses operationId-derived names.
The v4 docs override at fern/apis/waves-v4/overrides/pulse-stt-openapi-overrides.yaml was renaming the SDK method to `pulse` for the docs API-ref render, while the SDK-gen override at fern/apis/waves/openapi/pulse-stt-openapi-overrides.yaml correctly defines the method as `transcribe_pulse`. The shipped Python SDK exposes `client.waves.transcribe_pulse(...)` — the v4 override was making the docs page advertise a method name (`client.waves.pulse(...)`) that doesn't exist in the SDK. Dropping the v4 override lets the docs inherit `transcribe_pulse` from the SDK-gen layer, so the auto-generated Fern API-ref method block matches the hand-written prose in this PR and the actual customer-facing SDK method. No SDK change — `transcribe_pulse` was and remains the shipped method.
…om PyPI 4.4.0
A fresh `pip install smallestai==4.4.0` + customer-pattern run surfaced
three places where the prose said one thing but the SDK actually returned
something else. A copy-paster would have hit TypeError on day one. Fixing
now from observed shapes:
- synthesize_lightning_v31: returns a generator of bytes chunks (Fern
yields the HTTP response in pieces, not one buffer). Old doc:
`audio = client.waves.synthesize_lightning_v31(...); f.write(audio)`
→ TypeError. Corrected to iterate: `for chunk in ...: f.write(chunk)`.
- synthesize_sse_lightning_v31: returns a generator of dicts shaped
`{"audio": "<base64-pcm>"}`, not decoded bytes. Old doc piped chunks
to stdout assuming bytes. Corrected to base64.b64decode(chunk["audio"]).
Also clarifies the dict shape so customers know what `chunk` is.
- transcribe_pulse: result has both `.status` and `.transcription`.
Old doc only mentioned `.transcription`. Added the status read so
customers can detect server-side errors before reading the transcript.
All three corrected patterns were re-run verbatim against api.smallest.ai
from a clean venv and produced expected bytes/transcripts.
… notices
Three concrete fixes after running every PR-affected snippet against the
live API from a fresh PyPI 4.4.0 install:
1. **JS SSE example in lightning-v3.1-openapi-overrides.yaml** was
parsing the legacy nested wire shape `{status, data:{audio}}` and
checking `status === "complete"`. The Lightning v3.1 SSE endpoint
emits the flat shape `{audio: "<base64>"}` per chunk and `{done: true}`
as the terminator (verified live + see scripts/probes/lightning_tts.py
lines 152-159). Rewrote the loop to parse the data line, check
`payload.done`, and decode `payload.audio` directly. Python sample
in the same file already had the correct shape (verified live).
2. **Python SSE example in stream-tts.mdx (pages + versions)** had the
same nested-shape bug. The block was producing 0 audio bytes silently
(b"".join([]) writes 0 frames to wave, no exception, CI passed but
customers got nothing). Live test confirms: server emits 168 flat
chunks for a 4s sample. Corrected to `data.get("audio")` /
`data.get("done")` — now produces 161 KB of audio.
3. **Stale "WavesStreamingTTS is broken" ci:skip comments removed**
from stream-tts.mdx (4 places: pages + versions, 2 each). They said
the shim hardcoded the v2 URL — true on 4.3.9, fixed by PR #42, and
v4.4.0 is now live on PyPI. The blocks now run in CI; live-tested
the WavesStreamingTTS Python SDK example end-to-end (215 KB of audio,
28 chunks).
Plus one CI workflow fix: test-quickstarts.yml installs requests, websockets,
smallestai, aiofiles, but not aiohttp — even though how-to-tts.mdx has used
aiohttp for its concurrent example for months. Latent failure that any PR
touching pages/v4.0.0/*.mdx triggers (because this PR's changelog entry
matched the workflow's paths filter). Added aiohttp to the install line.
… URLs
- stream-tts.mdx Response Format section (pages + versions): the prior
block showed only the nested envelope and claimed it applied to both
WS and SSE. That contradicts the SSE Python and JS samples in this
same PR which (correctly) parse the flat shape. Split into two
explicitly labelled blocks: WS = nested `{status, data:{audio}}` +
`status:"complete"` terminator; SSE = flat `{audio}` + `{done:true}`
terminator. Both shapes verified live against the v3.1 endpoint
(probe in scripts/probes/lightning_tts.py:152-159).
- waves features.mdx (pages + versions): Card href for Gender Detection
was pointing at `/features/age-and-gender-detection` which Fern slugs
from the page title and now resolves to /features/gender-detection
(verified: old URL → 404, new URL → 200). The page was renamed in
the 2026-05-06 age-detection-removed changelog but the file path
stayed; Fern's slug follows the title, not the path.
- atoms byom.mdx: OpenAI link upgraded from the section landing page
/docs/api-reference/chat to the specific endpoint reference
/docs/api-reference/chat/create — more stable, matches the prose
about implementing the /chat/completions endpoint.
Note for reviewer bot: two of the 2/5-rating findings are reading
stale state already fixed earlier in this branch — `# ci:skip`
"WavesStreamingTTS hardcodes v2 URL" comments were removed in 83988d5
(grep confirms zero remaining hits), and `x-fern-sdk-method-name: pulse`
was dropped from the v4 override in 3bc8ba9 (so the SDK exposes
`transcribe_pulse` as the Python example uses). Only the Response
Format contradiction in this commit is the new fix.
Reviewer-hat pass on my own PR caught two issues:
1. Lightning v3.1 SSE "How it works" said the terminator event uses
`status: "complete"`. That contradicts both the live wire shape
(verified via scripts/probes/lightning_tts.py — terminator is
`data: {"done": true}`) and the JS code 70 lines below in the
same file (`if (payload.done) break`). Rewrote step 2 + step 4 to
describe the actual SSE frame format (`event: audio\ndata: {...}`)
instead of the legacy nested envelope wording.
2. Gotcha line claimed SSE first-chunk-latency is "typical 150–300 ms
p50". I made that number up — not measured. Replaced with prose
that names the actual factors (model warm-up + network distance)
and the controllable knob (`output_format=pcm`).
…able snippets Previous paths filter triggered on every MDX change under fern/products/waves/pages/v4.0.0/** and fern/products/atoms/pages/**. The workflow only extracts snippets from 6 specific files. Changes to model cards, benchmarks, changelog entries, mobile guides etc have no runnable code and have no business triggering the snippet runner — they just absorb transient-API flake risk and waste 60-90s per PR. This was the source of CI noise on PR #140 (Pulse model-card benchmark update) and would recur on every future model-card/benchmark/changelog PR. Tighten paths to match exactly the snippet-bearing files plus the workflow + runner script.
9048253 to
ed26d8e
Compare
| client = SmallestAI(token="YOUR_API_KEY") | ||
| with open("./call.wav", "rb") as f: | ||
| result = client.waves.transcribe_pulse( | ||
| request=f.read(), | ||
| language="en", | ||
| word_timestamps=True, | ||
| diarize=True, | ||
| ) | ||
| print(result.status) # "success" | ||
| print(result.transcription) # the transcript string |
There was a problem hiding this comment.
x-fern-sdk-method-name: pulse from fern/apis/waves-v4/overrides/pulse-stt-openapi-overrides.yaml (was the only explicit SDK method name in the waves-v4 config) and set operationId: pulseSpeechToText (line 8). Fern derives SDK method names from operationId when no x-fern-sdk-method-name is present, so the generated Python SDK method for waves-v4 will be pulse_speech_to_text (snake_case of pulseSpeechToText). The new Python example at line 52 calls client.waves.transcribe_pulse(...), which does not match that name. The only file that defines transcribe_pulse as an SDK method name is fern/apis/waves/openapi/pulse-stt-openapi-overrides.yaml (lines 15–16) — a separate legacy API config, not the waves-v4 config. A developer copy-pasting the Python example from the v4 API reference page will get an AttributeError: 'WavesClient' object has no attribute 'transcribe_pulse'.
🤖 AI Agent Prompt for Cursor/Windsurf
📋 Copy this prompt to your AI coding assistant (Cursor, Windsurf, etc.) to get help fixing this issue
In fern/apis/waves-v4/overrides/pulse-stt-openapi-overrides.yaml, add `x-fern-sdk-method-name: transcribe_pulse` on the line after `x-fern-sdk-group-name: Speech to Text` (before operationId). This pins the waves-v4 SDK method name to `transcribe_pulse`, matching the Python example at line 52 and aligning with the naming convention used in the legacy waves config. Alternatively, if the intended method name is different, update the Python example to call `client.waves.pulse_speech_to_text(...)` and grep all docs pages for `transcribe_pulse` to update them consistently.
| Identify and label different speakers in the audio | ||
| </Card> | ||
| <Card title="Gender Detection" icon="user" href="/waves/documentation/speech-to-text-pulse/features/age-and-gender-detection"> | ||
| <Card title="Gender Detection" icon="user" href="/waves/documentation/speech-to-text-pulse/features/gender-detection"> |
There was a problem hiding this comment.
/waves/documentation/speech-to-text-pulse/features/gender-detection has no corresponding file in the repo; the only matching file is fern/products/waves/pages/v4.0.0/speech-to-text/features/age-and-gender-detection.mdx (and its versions counterpart). Any user clicking this card will hit a 404.
🤖 AI Agent Prompt for Cursor/Windsurf
📋 Copy this prompt to your AI coding assistant (Cursor, Windsurf, etc.) to get help fixing this issue
In file `fern/products/waves/pages/v4.0.0/speech-to-text/pre-recorded/features.mdx`, line 20, the href attribute reads `href="/waves/documentation/speech-to-text-pulse/features/gender-detection"`. No page exists at that path — the actual page is `age-and-gender-detection.mdx`. Either revert the href back to `/waves/documentation/speech-to-text-pulse/features/age-and-gender-detection`, or create a new `gender-detection.mdx` page (and update the docs nav config) if the intent is to rename the page. The simplest fix is to revert: change `href="/waves/documentation/speech-to-text-pulse/features/gender-detection"` back to `href="/waves/documentation/speech-to-text-pulse/features/age-and-gender-detection"` on line 20.
Replaces closed #135 (which got polluted with stale-base contamination). Clean re-do off current `origin/main` — exactly 5 files.
What
Rewrites the v4 API reference descriptions for the highest-traffic Waves endpoints:
Each description now follows the same shape:
Verified live
All 5 sets of code samples were live-tested against `api.smallest.ai` before commit:
JS SDK note
The official `smallestai` npm package (v1.0.1) predates Lightning v3.1 + Pulse. Until a v2 JS SDK ships, the docs show `fetch` (REST/SSE) and `ws` (WebSocket) examples. Each gotcha section calls this out so JS customers aren't surprised.