mirror of
https://github.com/jambonz/jambonz-feature-server.git
synced 2026-10-26 13:24:33 +00:00
287883c1480bc1132acb8b50284945255fd36d43
* feat(elevenlabs): forward DTMF to the agent and support llm:update
Adds opt-in llmOptions.dtmf {enable, interDigitTimeout, finishOnKey}.
Digits are buffered until finishOnKey, the inter-digit timeout or 50
digits, then sent as a user_message with source_medium "dtmf". Each
key press also sends user_activity so the agent turn timeout does not
fire while the caller is typing.
llm:update now forwards user_message, user_activity and
contextual_update client events instead of ignoring them.
Client events escape "|" so payloads containing it are no longer
split by the api argument delimiter (this also fixes tool results).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(elevenlabs): send DTMF per digit like ultravox, drop llmOptions.dtmf
Match the ultravox adapter: DTMF is always forwarded, with no llmOptions
setting. Each digit is sent as soon as it arrives, as a user_message with
source_medium "dtmf". Digit buffering, the inter-digit timeout and
finishOnKey are left to the agent's dtmf_input_settings.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(elevenlabs): buffer DTMF behind llmOptions.dtmf again
Per-digit sending does not work for ElevenLabs. Over the agent
WebSocket each user_message becomes its own turn: "1234#" produced 5
turns, and the agent answered "Are you still there?". The same digits
sent as a single message produce one dtmf turn, "1234".
- restores the opt-in llmOptions.dtmf {enable, interDigitTimeout, finishOnKey}
- digits are buffered and sent as one user_message, source_medium "dtmf"
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat: shared DtmfInput util with gather dtmf semantics
Any llm or agent vendor can turn caller DTMF into whole entries without
copying gather or touching it.
- lib/utils/dtmf-input.js uses gather options: finishOnKey, numDigits,
minDigits, maxDigits, interDigitTimeout, timeout (15s default, 0 = off)
- no invented defaults: no implicit "#", no 2s timeout, no 50-digit cap
- minDigits defaults to 1; in gather an unset minDigits never arms the
inter-digit timer
- stop() removes only its own dtmf listener
- elevenlabs uses it behind llmOptions.dtmf.enable
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(dtmf): review fixes for DtmfInput and elevenlabs
- inter-digit timer replaces the overall timeout while typing, as in gather
- warn when no option can ever complete an entry
- no DtmfInput when the session connects after kill (listener leak)
- llm:update rejects user_message/contextual_update without string text,
since an ElevenLabs error event ends the conversation
- unit tests use mock timers instead of wall-clock waits
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
jambonz-feature-server 
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.
Description
Core telephony feature server for the jambones platform
10 MiB
0 Stars
1 Watchers
0 Forks
Languages
JavaScript
99.9%