Skip to content

Correct the AI turn detection params - #676

Merged
Devon-White merged 6 commits into
mainfrom
t3code/fix-cloud-product-issue
Sep 17, 2026
Merged

Devon-White merged 6 commits into
mainfrom
t3code/fix-cloud-product-issue

Conversation

@Devon-White

@Devon-White Devon-White commented Sep 10, 2026

Copy link
Copy Markdown
Collaborator

Fixes signalwire/cloud-product#20939.

ai.params.enable_turn_detection was documented as a boolean that monitors partial
transcripts for sentence-ending punctuation. It actually takes a string enum —
off / both / punct_only / acoustic_only, defaulting to both — and the punctuation
behavior the page described is one of the four modes, not the parameter itself.

Chasing that turned up three more errors on turn_detection_timeout, all pre-existing,
plus a sibling parameter that was never documented.

What changed

Param Was Is
enable_turn_detection boolean, default true off / both / punct_only / acoustic_only, default both; booleans are still accepted (falseoff, trueboth), and turn_detection is an accepted alias
turn_detection_timeout default 250; 0 finalizes immediately; lower is more responsive defaults to the value of end_of_speech_timeout (700 ms); 0 means "use the default"; lowering it doesn't improve responsiveness, and values under 500 ms hurt transcription accuracy
turn_detection_min_length undocumented added — measured in characters of transcript, not milliseconds

Detail

enable_turn_detection — the page now documents all four modes and what each one
listens to, notes that booleans still work for backward compatibility, and records
turn_detection as an alias. Both tuning parameters below are documented as having no
effect when the mode is off.

turn_detection_timeout — the previous default of 250 ms was never the effective
default with turn detection enabled; the value falls back to end_of_speech_timeout,
which is 700 ms unless you set it. 0 doesn't finalize the turn immediately — it leaves
the default in place. Reducing the timeout below 500 ms does not make responses arrive
sooner and does introduce transcription errors on similar-sounding words, so the page now
states a 500 ms practical floor instead of framing lower values as a responsiveness win.

turn_detection_min_length — sets the minimum length of transcript, in characters,
before the punctuation path will treat a turn as complete. Range 0–1000. It's bypassed
when the acoustic signal alone is a confident end-of-turn, so short complete replies like
"Yes." still fire normally; the parameter only gates the punctuation-based path.

Checks

  • yarn build:specs — regenerated fern/apis/signalwire-rest/openapi.yaml and
    SWMLObject.json. The large REST diff is expected for a SWML-only .tsp change.
  • yarn fern-md-check — 2831 files valid.
  • The four #params… anchors verified by hand against the convention in the page's own
    link definitions; all targets exist.

Behavior for all four parameters was confirmed against the current engine implementation
rather than taken from the existing docs.

`enable_turn_detection` was documented as a boolean that watches partial
transcripts for sentence-ending punctuation. It takes a string enum —
`off` / `both` / `punct_only` / `acoustic_only`, defaulting to `both` —
and the punctuation-only behavior the page described is one of the four
modes, not the whole param.

Verified against mod_openai and mod_deepgram at origin/main:

- Enum values, the `turn_detection` alias, and the legacy boolean mapping
  (`false`->off, `true`->both): app_config.c:1737-1756.
- Default `both`: `ai_globals.default_turn_detection = 1`
  (api_commands.c:1334), read by app_config.c:1551.
- "No effect when off" for both tuning knobs: the
  `!= TURN_DETECTION_OFF` guard at speech_detect.c:1486.

Also fixes three pre-existing errors on `turn_detection_timeout`, and
adds the undocumented `turn_detection_min_length`:

- The default is not 250. That value is only mod_deepgram's struct
  initializer (mod_deepgram.c:7895), live when turn detection is off.
  With it on, mod_deepgram.c:9548 sets the wait to `silence_ms`, which
  mod_openai always populates from `end_of_speech_timeout`
  (speech_detect.c:1347), so the effective default is 700.
- `0` does not finalize immediately. mod_openai only sends the knob when
  `> 0` (speech_detect.c:1502), so `0` means "use the default".
- Lowering it does not improve responsiveness. The mod_deepgram comment
  records eos->push as hedge-independent across 250-1000 and homophone
  garble below 500, so the page now states the floor instead of
  inviting readers under it.
- `turn_detection_min_length` is characters of transcript, not
  milliseconds of speech: mod_deepgram_fusion.h:209 declares it "min
  text length" and mod_deepgram_fusion.c:2774 tests
  `slen >= in->min_length` over `strlen(in->text)`. It is also bypassed
  once the acoustic signal clears the gate, so it filters the
  punctuation path alone.

Regenerated openapi.yaml and SWMLObject.json via yarn build:specs.

Fixes signalwire/cloud-product#20939
@github-actions

github-actions Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

@hey-august hey-august left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

The branch is accurate and the generated output is in sync. One sentence overstates which modes the two tuning parameters affect, and the acoustic_only mode ignores both. Fix that before merging. The rest is optional.

Verified

  • The mode enum, the turn_detection alias, the boolean mapping, and the both default all match the platform's behavior.
  • The turn_detection_timeout default of end_of_speech_timeout, the meaning of 0, and the sub-500 accuracy warning all match.
  • The turn_detection_min_length parameter counts transcript characters, and the acoustic signal bypasses it when it reads the caller as finished.
  • A fresh yarn build:specs produces no diff against the committed output. yarn fern-md-check passes on all 2831 files.

Blocking

  • The acoustic_only mode ignores both tuning parameters. An acoustic-only end of turn finalizes with no wait and skips the length check, so "Every mode except off is tuned by turn_detection_timeout and turn_detection_min_length" is wrong for that mode. The sentence appears in the mdx page at line 113 and in the .tsp doc at line 161, and from there in both generated files. Suggested wording: "The both and punct_only modes honor turn_detection_timeout and turn_detection_min_length. The acoustic_only mode uses neither."

Suggested

  • The turn_detection_min_length text assumes the acoustic override exists in every mode. In punct_only there is no acoustic signal, so a value of 4 does block "Yes.". Add "in both mode" to the sentence that begins "The acoustic signal overrides this".
  • The enable_turn_detection ParamField is typed string but also accepts a boolean. The page writes unions as type="boolean | integer", so type="string | boolean" matches. Line 105 of the mdx page.
  • Consider a changelog entry. Documentation work gets none, but this publishes two capabilities customers could not discover before, the mode enum and turn_detection_min_length, plus a default change from punctuation-only detection to both. That is the exception the changelog guidance allows. Date it when the change reached customers, if that date can be established.
  • Em-dash density. The turn_detection_min_length paragraphs carry three across two short paragraphs and the enable_turn_detection block has four. The "Uh-huh." appositive earns its pair. The tail "a caller who has finished usually commits well inside it" reads the same as its own sentence.

hey-august and others added 3 commits September 11, 2026 09:13
`acoustic_only` finalizes with no wait and skips the length check, so it
honors neither `turn_detection_timeout` nor `turn_detection_min_length`.
Scope the acoustic override on `turn_detection_min_length` to `both`,
since `punct_only` has no acoustic signal to override it.

Also type `enable_turn_detection` as `string | boolean` to match the
boolean aliases, and split an em-dash tail into its own sentence.

@hey-august hey-august left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving. The follow-up commit addresses the review notes, and the branch is ready to merge.

Verified after the update

  • The acoustic_only mode is now excluded from both tuning parameters in the mdx page, the .tsp source, and both generated files. Each parameter carries a matching "no effect when off or acoustic_only" line.
  • The acoustic override on turn_detection_min_length is scoped to both mode.
  • The enable_turn_detection field is typed string | boolean, matching the page's union convention.
  • The both bullet's two acoustic claims, holding a turn open when the caller sounds mid-sentence and ending one that trails off without punctuation, both match the platform's decision logic. That answers the open question from the linked issue.
  • A fresh yarn build:specs produces no diff against the committed output. yarn fern-md-check passes.
  • The merge from main touched none of these four files beyond this branch's own changes.

Follow-up, not blocking

A changelog entry is still worth considering. This publishes the mode enum and turn_detection_min_length, which customers could not discover before, and a default change from punctuation-only detection to both. It can ship separately once the production date is known.

@Devon-White
Devon-White merged commit b49159e into main Sep 17, 2026
2 checks passed
@Devon-White
Devon-White deleted the t3code/fix-cloud-product-issue branch September 17, 2026 16:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants