Dave HortonandClaude Opus 5.5 baf97dabc2 fix(gather): clear a finalized deepgram interim word so UtteranceEnd is not deferred forever (#1588)
* fix(gather): clear a finalized deepgram interim word so UtteranceEnd is not deferred forever

The #1088 check defers UtteranceEnd while an interim word newer than
last_word_end is pending. The marker was only cleared by a non-empty
is_final, so an interim word that Deepgram later dropped (e.g. line
noise, followed by an empty final) left it set: the gather waited for
the caller to speak again and merged both utterances.

Clear the marker on any Deepgram is_final result whose window
(start + duration) covers it. A word that Deepgram carries into the
next segment still defers UtteranceEnd.

Port of jambonz/feature-server#223.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(gather): return the buffer when a deferred deepgram UtteranceEnd is satisfied

Clearing the unprocessed-word marker only helps if the covering final
arrives before UtteranceEnd. If it arrives after, UtteranceEnd has
already been deferred and Deepgram sends no further UtteranceEnd until
new speech, so the next utterance is still merged in.

Remember a deferred UtteranceEnd, and when a later final clears the
marker, return the buffered transcript once that final has been
handled (setImmediate, since _onTranscription has early returns).

Port of jambonz/feature-server#225.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(gather): only an empty final ends a gather early after a deferred UtteranceEnd

A final with words means the caller resumed talking after the pause;
Deepgram will send a new UtteranceEnd after those words, so keep the
existing flow instead of cutting the caller off mid-sentence (or
skipping the asrTimeout wait). Only a final that drops the pending word
(empty) leaves nothing else to end the gather.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 17:45:27 -04:00
2023-12-11 08:37:36 -05:00
2020-01-07 10:34:03 -05:00
2021-08-30 17:12:00 -04:00
2025-06-28 15:01:09 -04:00
2025-08-21 14:09:20 -04:00
2026-03-20 08:57:16 -04:00
2024-06-28 09:05:05 -04:00

jambonz-feature-server CI

This application implements the core feature server of the jambones platform.

Note: If you are a developer looking to work on the code please read our how-to for that.

Configuration

Configuration is provided via environment variables:

variable meaning required?
AWS_ACCESS_KEY_ID aws access key id, used for TTS/STT as well SNS notifications no
AWS_REGION aws region no
AWS_SECRET_ACCESS_KEY aws secret access key, used per above no
AWS_SNS_TOPIC_ARN aws sns topic arn that scale-in lifecycle notifications will be published to no
DRACHTIO_HOST ip address of drachtio server (typically '127.0.0.1') yes
DRACHTIO_PORT listening port of drachtio server for control connections (typically 9022) yes
DRACHTIO_SECRET shared secret yes
ENABLE_METRICS if 1, metrics will be generated no
ENCRYPTION_SECRET secret for credential encryption(JWT_SECRET is deprecated) yes
GOOGLE_APPLICATION_CREDENTIALS path to gcp service key file yes
HTTP_PORT tcp port to listen on for API requests from jambonz-api-server yes
HTTP_IP IP Address for API requests from jambonz-api-server no
JAMBONES_GATHER_EARLY_HINTS_MATCH if true and hints are provided, gather will opportunistically review interim transcripts if possible to reduce ASR latency no
JAMBONES_FREESWITCH IP:port:secret for Freeswitch server (e.g. '127.0.0.1:8021:JambonzR0ck$' yes
JAMBONES_HOLD_UNHOLD_EVENTS if set, surface in-dialog hold/un-hold: emits a 'hold'/'unhold' event and, when sipRequestWithinDialogHook is configured, delivers it to the application. Disabled when unset no
JAMBONES_LOGLEVEL log level for application, 'info' or 'debug' no
JAMBONES_MYSQL_HOST mysql host yes
JAMBONES_MYSQL_USER mysql username yes
JAMBONES_MYSQL_PASSWORD mysql password yes
JAMBONES_MYSQL_DATABASE mysql data yes
JAMBONES_MYSQL_CONNECTION_LIMIT mysql connection limit no
JAMBONES_NETWORK_CIDR CIDR of private network that feature server is running in (e.g. '172.31.0.0/16') yes
JAMBONES_REDIS_HOST redis host yes
JAMBONES_REDIS_PORT redis port yes
JAMBONES_SBCS list of IP addresses (on the internal network) of SBCs, comma-separated yes
STATS_HOST ip address of metrics host (usually '127.0.0.1' since telegraf is installed locally no
STATS_PORT listening port for metrics host no
STATS_PROTOCOL 'tcp' or 'udp' no
STATS_TELEGRAF if 1, metrics will be generated in telegraf format no
JAMBONZ_RECORD_WS_BASE_URL recording websocket URL to send the recording audio no
JAMBONZ_RECORD_WS_USERNAME recording websocket username no
JAMBONZ_RECORD_WS_PASSWORD recording websocket password no
ANCHOR_MEDIA_ALWAYS keep media on media server no
JAMBONZ_DISABLE_DIAL_PAI_HEADER control P-Asserted-Identity header on B-Leg no

running under pm2

Typically, this application runs under pm2 using an ecosystem.config.js file similar to this:

module.exports = {
  apps : [
  {
    name: 'jambonz-feature-server',
    cwd: '/home/admin/apps/jambonz-feature-server',
    script: 'app.js',
    instance_var: 'INSTANCE_ID',
    out_file: '/home/admin/.pm2/logs/jambonz-feature-server.log',
    err_file: '/home/admin/.pm2/logs/jambonz-feature-server.log',
    exec_mode: 'fork',
    instances: 1,
    autorestart: true,
    watch: false,
    max_memory_restart: '1G',
    env: {
      NODE_ENV: 'production',
      GOOGLE_APPLICATION_CREDENTIALS: '/home/admin/credentials/gcp.json',
      AWS_ACCESS_KEY_ID: 'XXXXXXXXXXXX',
      AWS_SECRET_ACCESS_KEY: 'YYYYYYYYYYYYYYYYYYYYY',
      AWS_REGION: 'us-west-1',
      ENABLE_METRICS: 1,
      STATS_HOST: '127.0.0.1',
      STATS_PORT: 8125,
      STATS_PROTOCOL: 'tcp',
      STATS_TELEGRAF: 1,
      AWS_SNS_TOPIC_ARN: 'arn:aws:sns:us-west-1:xxxxxxxxxxx:terraform-20201107200347128600000002',
      JAMBONES_NETWORK_CIDR: '172.31.0.0/16',
      JAMBONES_MYSQL_HOST: 'aurora-cluster-jambonz.cluster-yyyyyyyyyyy.us-west-1.rds.amazonaws.com',
      JAMBONES_MYSQL_USER: 'admin',
      JAMBONES_MYSQL_PASSWORD: 'foobarbz',
      JAMBONES_MYSQL_DATABASE: 'jambones',
      JAMBONES_MYSQL_CONNECTION_LIMIT: 10,
      JAMBONES_REDIS_HOST: 'jambonz.zzzzzzz.0001.usw1.cache.amazonaws.com',
      JAMBONES_REDIS_PORT: 6379,
      JAMBONES_LOGLEVEL: 'debug',
      HTTP_PORT: 3000,
      DRACHTIO_HOST: '127.0.0.1',
      DRACHTIO_PORT: 9022,
      DRACHTIO_SECRET: 'sharedsecret',
      JAMBONES_SBCS: '172.31.32.10',
      JAMBONES_FREESWITCH: '127.0.0.1:8021:sharedsecret'
    }
  }]
};

Running the test suite

Please see this.

S
Description
Core telephony feature server for the jambones platform
Readme MIT
10 MiB
Languages
JavaScript 99.9%