Skip to main content
Version: 0.9.17-patch1

TOBi managed LLM

TOBi (type vodafone_tobi) is a managed LLM integration, not a modular STT/TTS vendor. The platform hosts the conversational brain; your flow configures how Delphi forwards SIP context to the bot and which managed actions (transfer, hangup) the bot may trigger.

Rendered on the canvas as the Managed LLM node.

Pipeline role

StageTOBi
STT / TTS / RealtimeNot configured here — handled by the managed integration
LLMvodafone_tobi in pipeline LLM slot or Managed LLM node

Key fields

FieldDescription
Base URLVodafone chatbot API host (with or without /v1/chatbot path suffix)
Client ID / Client SecretOAuth-style credentials via team variables
Bot nameManaged bot identifier
Callback / response pathsAdvanced routing for bot callbacks
Channel Data ForwardingSIP header → bot context rules — see SIP header context rules
Application MetadataOptional VGW metadata attributes sent to TOBi at conversation creation.
DTMF defaultsManaged DTMF collection defaults for the integration

Managed transfer and hangup actions are configured on the Managed LLM / Managed action inspectors and validated at runtime against platform egress and transfer restrictions.

For bot-triggered hangup, TOBi hangupSipHeaders are added to the configured BYE headers; a bot value wins a case-insensitive name collision for that call. If the merged optional set exceeds the SIP budget, Delphi drops the bot set, retains valid configured headers, and still disconnects. Platform SIP/Q.850 causes and carrier-facing BYE behavior are documented in Call termination and transfer signaling.

TOBi often sends multiple text messages before hangup; pipeline mode plays them in order and defers hangup until speech finishes. See Speech playback and hangup.

Application Metadata

v0.9.15 adds an Application Metadata section to the Managed TOBi panel. Select only the VGW attributes the bot should receive; Delphi sends nothing by default until a flow opts in.

AttributeValue source
VGW-EnvironmentVGW_ENVIRONMENT, then ENVIRONMENT, then OTEL_DEPLOYMENT_ENVIRONMENT.
VGW-ConversationIDTOBi/managed conversation identifier when available.
VGW-IpTelproTelPro edge IP propagated by TelPro.
VGW-IpVoiceVoice instance IP.
VGW-CallIdSIP Call-ID / Delphi call correlation ID.
VGW-CalleeCalled number or endpoint.
VGW-CallerCaller number.
VGW-CallerHostCaller host from SIP signaling when available.

Application metadata is separate from Channel Data Forwarding. Internal Delphi headers, including TelPro-provided VGW helper headers, are not forwarded through channelDataRules, but they can still be resolved into selected Application Metadata attributes.

Publish required

After changing Application Metadata checkboxes (or related Channel Data rules), publish the flow. Draft-only changes are not served to live endpoints, so metadata will not appear in channelData until publish.

Set optional VGW_ENVIRONMENT on the Voice host when VGW-Environment should be a deployment label distinct from generic ENVIRONMENT — see the environment variable index.

Transaction correlation (transactionId)

Every TOBi conversation gets a stable transactionId — a UUID generated by the TOBi LLM component at construction time. This id is separate from the distributed trace_id and provides a single correlation point for all BOT requests, SIP signaling, and OTel spans in one conversation.

How it flows

  1. Outbound BOT requests — The TOBi LLM component sends transactionId as the vf-trace-transaction-id HTTP header on every request to the TOBi API. This is a provider-side header contract: the TOBi API expects it and echoes it back on callbacks.

  2. Inbound webhook callbacks — When the TOBi API posts a callback to POST /api/v1/webhooks/tobi/{channelId}, it includes the same vf-trace-transaction-id header. TelAPI extracts it and uses it as the transactionId for the rest of the call. See Webhooks.

  3. Redis propagation — TelAPI writes transactionId into the voiceai:calls:{callId} Redis key so Kamailio can read it on in-dialog requests (re-INVITE, BYE) and forward it as the X-Transaction-ID SIP header to TelSys.

  4. TelSys dialplan — Asterisk reads X-Transaction-ID at INVITE time and includes transaction_id in all structured log events. TelPhi also sets it via ARI setChannelVar for resumed sessions.

  5. OTel spans — TelPhi attaches transactionId as a span attribute on the call span and on all four modular audio pipeline spans (user.speech, user.transcript, ai.transcript, ai.response). The log-to-span sidecar injects it on Kamailio, TelSys, and RTPEngine spans.

Searching by transactionId

In SigNoz, filter by transactionId to see every event for one conversation across all services:

attributes.transactionId = "<uuid>"

See Transaction correlation for the full propagation chain, SigNoz query patterns, and diagnostic guidance.

OpenAPI reference

The vf-trace-transaction-id header is a provider-side contract header — it belongs to the TOBi API, not to Delphi's own API surface. It is therefore not modelled as a Delphi OpenAPI parameter. The callback body itself is documented as the TobiCallbackBody schema in the generated OpenAPI reference.

ReferenceLink
TOBi webhook endpointRedoc
TobiCallbackBody schemaRedoc
TobiBotControlEvent schemaRedoc

The callback body does not contain transactionId as a field — the id travels as the vf-trace-transaction-id HTTP header, not inside the JSON payload.

Sensitive storage (transcripts and logs)

TOBi can request that Delphi treat a session as sensitive so transcript / log persistence is suppressed or restored mid-call. The bot sends this via channel data / runtime commands (sensitiveInfoOnStorage / update_sensitive_storage). Delphi applies the update in arrival order so older protected text is not written after a later enable.

Use this when a conversation must not retain STT/TTS transcript artefacts even if the app Recording toggle is on. See also Recording and transcript storage.

DTMF collection

Managed DTMF defaults (max digits, timeouts, inter-digit timeout, terminator) are configured on the TOBi panel and can be updated live with the update_dtmf_config callback action (interDigitTimeoutMs, timeoutMs, and related fields). The same config event also carries barge-in / interruption-prevention flags (bargeIn, bargeInOnDTMF, bargeInMinWordCount); those apply immediately so a bargeIn: false update can lock the announcement sent in the same callback. Field reference: Managed callback actions.

Disabling the TTS cache for one response

TOBi supports the AudioCodes/Vodafone disableTtsCache parameter at the per-response level. When the bot sets activityParams.disableTtsCache: true on a single text message, the audio pipeline bypasses the TTS media cache for only that one spoken sentence — no cache.fetch, no cache.upload. The cache stays on for every other reply in the call, so the optimisation that keeps repeated phrases cheap is preserved.

This is useful for dynamic or personalised content where a cached clip would be wrong — one-time codes, account numbers, names, balances, OTPs.

Wire shape

{
"conversation": {"identifier": {"id": "<conversationId>", "name": "TOBi"}},
"from": {"identifier": {"id": "tobi", "name": "TOBi"}},
"messages": {
"timestamp": "<ISO>",
"message": [
{
"messageId": 2,
"sequence": 2,
"content": {
"textMessage": {"textPlain": "I have something sensitive to tell you."},
"activityParams": {"disableTtsCache": true}
}
}
]
},
"metadata": {}
}

The activityParams object is attached sibling to textMessage on each messages.message[].content entry. Other messages in the same callback that omit activityParams cache normally.

How the bypass is observed

The flag is forwarded through the pipeline and produces structured log lines at each hop:

StageLog eventField
TelAPI inbound webhooktobi.callback.receiveddisableTtsCache: true
TelAPI → Redis stream publishtobi.callback.publisheddisableTtsCache: true
TelPhi stream readertobi.message.inbounddisableTtsCache: true
Audio pipeline (per sentence)tts_cache_bypassed_for_responseproviderType, cacheKey, textLength

Filter the audit log on disableTtsCache: true or tts_cache_bypassed_for_response to confirm the bypass actually fired on a given reply.

What it does not change

  • The platform-wide TTS media cache and the ttsMediaCache feature flag — unaffected.
  • Other replies in the same call — they continue to hit the cache.
  • The bot's runtime configuration — update_tts_config runtime commands still drive whole-call TTS settings; see Managed callback actions.
  • The voice-bot channel — disableTtsCache is not applicable when the framework performs speech-to-text on the bot side (AudioCodes note).
Per-response vs. per-call

For turning the cache off for the whole call, send an update_tts_config runtime command with ttsCaching: 'off' from a bot-originated configuration event. For turning it off for one sentence, use the per-response activityParams.disableTtsCache shown above.

Termination reason propagation

When a call ends, Delphi tells TOBi why it ended. Every endConversation PUT (PUT /conversation) carries two metadata fields — metadata.reason and metadata.reasonCode — so Vodafone Ops can group terminations by category and read the specific cause. Both are populated regardless of who initiated the end (the bot or the SIP side). The internal Delphi field reasonDetails is serialized to the wire name reasonCode at the provider boundary only; everywhere else in Delphi (logs, OTel, types) it stays reasonDetails.

This is the SCRUM-742 acceptance criterion: Ops must be able to see why a call terminated, not just that it did.

Wire shape

{
"conversation": {"identifier": {"id": "<conversationId>", "name": "TOBi"}},
"from": {"identifier": {"id": "<userId>", "name": "<userName>"}},
"forwardEventToBot": true,
"text": "conversationEnded",
"metadata": {
"channel": "Voice AI",
"reason": "<BotSide|ClientSide|Error|SpeechService|Media|System|Transferred>",
"reasonCode": "<human-readable detail>"
}
}

reason and reasonCode are not fields in the published v2.0.0 swagger. They ride the MetaData free-form bag, which is additionalProperties: true (the same bag that already carries channel and closeConversation). When Delphi captured no reason for a call, both keys are omitted from metadata rather than sent empty.

Reason enum

reasonMeaning
BotSideThe bot (TOBi sandbox tool, external-LLM action, or closing-phrase detection) ended it.
ClientSideThe caller / SIP side hung up (normal clear, busy, rejected, SIP Cancel, …).
ErrorA server-side or integration error forced the end.
SpeechServiceAn STT or TTS error forced the end (failover).
MediaA media / RTP / transport-capacity problem forced the end.
SystemHousekeeping: graceful shutdown, max-call-age reap, generic stasis_end.
TransferredThe call was transferred to another destination (SIP transfer / Refer).

How the reason is chosen (precedence)

Delphi resolves a single termination reason per call, and first caller wins — once a reason is stashed for a channel it is not overwritten by a later signal. The precedence, highest to lowest:

  1. Bot-side callbackcloseConversation, a bot hangup runtime command, or closing-phrase detection fires a BotSide reason into the gateway before SIP tear-down.
  2. Gateway trigger stash — failover, channel-setup errors, transfers, and the max-call-age reaper stash a reason on the channel data before hangup.
  3. ARI Q.850 cause code — when nothing was stashed, the Asterisk hangup cause (and cause_txt) is mapped to a reason.
  4. DefaultSystem / stasis_end.

reasonCode examples

reasonreasonCodeWhen
Transferredtransferred: <target>; <reason>SIP transfer / Refer (see Transfers)
BotSidetobi_close_conversationBot sent metadata.closeConversation: true
BotSideexternal_llm_hangup / external_llm_hangup:<reason>Bot hangup runtime command (conversationCompleted, userSilent, …)
BotSidetobi_closing_phrase:<reason>Closing-phrase detection
BotSidefailover_bot_error / failover_bot_timeoutBot failover
SpeechServicefailover_stt_error / failover_tts_errorSTT / TTS failover
Errorfailover_system_errorSystem failover
ClientSide<cause_txt> (e.g. Normal Clearing)Caller hung up
Media<cause_txt>Media/RTP cause code
Systemmax_call_age_reapMax-call-age reaper
Systemprovider_stop / provider_destroyGraceful provider shutdown
Systemstasis_endDefault when nothing else applied

Bot close signals (inbound)

TOBi can end the conversation in two ways, and Delphi stashes a BotSide termination reason from either before the SIP tear-down so it reaches the endConversation PUT:

  • metadata.closeConversation: true on a callback — Delphi fires the bot-side termination callback with tobi_close_conversation.
  • A hangup runtime command with a reason — Delphi fires the callback with external_llm_hangup (or external_llm_hangup:<reason> when the bot supplied one). Common bot reasons include conversationCompleted, silentCollCustomer, telcoTesterHangUp, and userSilent.

Both are idempotent under first-caller-wins: if the bot already signalled close (for example a hangup with conversationCompleted followed by closeConversation), the second signal does not overwrite the first.

Transfers

A bot- or flow-initiated transfer stashes a Transferred reason with reasonCode transferred: <target>; <reason> (with No target / No reason fallbacks when either is absent) before the SIP Refer, so the endConversation PUT reflects the actual destination. This matches the Vodafone Xfer sip:<target>; <reason> sample shape. The stash covers all transfer paths — runtime transfer command, the setTransferHandler defaults, and the flow-engine executeCallAction (including the failover-policy ladder that flows through it).

Observability

The same reason feeds two more surfaces alongside the wire payload, so a termination can be traced end-to-end:

  • OTel span attributes on the call span: call.end_reason_category (= reason) and call.end_reason_details (= reasonDetails).
  • Structured log fields: terminationReason and terminationReasonDetails on the endConversation log event.

See SigNoz monitoring to group calls by call.end_reason_category, and Application flow logging to filter the structured logs.

Known gaps — follow-ups

A few AC cases need new detectors that do not exist in the gateway yet, and currently rely on the Q.850 cause-code / cause_txt fallback (or the System / stasis_end default) rather than a specific reasonCode:

  • media-connect-failed / SBC data underrun — no RTP-liveness watchdog.
  • user-inactivity timeout — no dedicated inactivity watchdog (STT-side inactivityTimeoutSeconds is a passthrough, not a termination source).
  • upstream TOBi websocket / posting-activity errors — detected, but the specific error text (401, timeout, websocket closed) is not yet threaded into reasonCode; the call fails over with failover_bot_error and the detail is logged separately.

These are tracked as SCRUM-742 follow-ups.

API reference

Bot callback payloads (transferTarget, hangupSipHeaders, failover, and related fields) are documented in Managed callback actions.

See also