mirror of
https://github.com/dograh-hq/dograh.git
synced 2026-07-22 11:51:04 +02:00
feat: add vici dial controls from UI (#563)
* ViciDial Working * feat(telephony): add FreeSWITCH provider to upstream-PBX seam Generalize the upstream-PBX capture and control paths to dispatch by provider. FreeSWITCH bridges in with X-PBX-* headers and is driven over the Event Socket Library (uuid_kill / uuid_transfer) by the channel UUID, alongside the existing VICIdial ra_call_control path. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * add vici specific configs * feat(telephony): env-based VICIdial config + address3 post-call routing Move VICIdial agent/non-agent API and FreeSWITCH ESL connection settings out of hardcoded POC values into environment variables so the same image works against a PBX on another server. Add VICIdial update_lead forwarding: X-VICI-UPDATE-LEAD_* extracted variables are mapped to lead columns and pushed via the non-agent API before a transfer, with reserved API-control params dropped. Add hardcoded address3 disposition routing (Y/N -> in-group transfer, else hang up) and a synchronous final variable extraction so the transfer path sees the freshest conversation state. Skip upstream_pbx capture on non-PJSIP channels to avoid Asterisk 500s. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: run formatter * feat: make vici configurable from UI * chore: clean up PR * fix: incorporate review comments * chore: incorporate review comments * chore: generate client * chore: incporporate review comments * chore: incorporate review comments --------- Co-authored-by: Dograh POC <payment@dograh.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
2f7b47a1b4
commit
d8cd34e8d2
51 changed files with 2886 additions and 102 deletions
|
|
@ -98,6 +98,10 @@ class PipecatEngine:
|
|||
self._gathered_context: dict = {}
|
||||
self._user_response_timeout_task: Optional[asyncio.Task] = None
|
||||
self._pending_extraction_tasks: set[asyncio.Task] = set()
|
||||
# True once a final (synchronous) extraction has run, so the end-of-call
|
||||
# and upstream-transfer paths don't redundantly re-extract the same
|
||||
# terminal state.
|
||||
self._final_extraction_done: bool = False
|
||||
|
||||
# Will be set later in initialize() when we have
|
||||
# access to _context
|
||||
|
|
@ -507,6 +511,29 @@ class PipecatEngine:
|
|||
f"Incomplete: {incomplete}"
|
||||
)
|
||||
|
||||
async def perform_final_variable_extraction(self) -> None:
|
||||
"""Flush in-flight + current-node variable extraction synchronously.
|
||||
|
||||
Awaits any background extractions still running from previous nodes,
|
||||
then runs the current node's extraction inline so callers that need the
|
||||
freshest extracted variables before acting can rely on them -- e.g.
|
||||
end_call_with_reason before disposing the call, or an external-PBX
|
||||
transfer that maps extracted variables into a provider lead update
|
||||
call before handing the customer off.
|
||||
|
||||
Idempotent: only the first call does work. The external-PBX transfer
|
||||
runs this just before forwarding update_lead, so the subsequent
|
||||
end_call_with_reason would otherwise re-extract the same terminal state.
|
||||
"""
|
||||
if self._final_extraction_done:
|
||||
logger.debug("Final variable extraction already performed; skipping")
|
||||
return
|
||||
self._final_extraction_done = True
|
||||
await self._await_pending_extractions()
|
||||
await self._perform_variable_extraction_if_needed(
|
||||
self._current_node, run_in_background=False
|
||||
)
|
||||
|
||||
async def _setup_llm_context(self, node: Node) -> None:
|
||||
"""Common method to set up LLM context"""
|
||||
# Set OTel span name for tracing
|
||||
|
|
@ -742,13 +769,8 @@ class PipecatEngine:
|
|||
EndTaskReason.PIPELINE_ERROR.value,
|
||||
EndTaskReason.VOICEMAIL_DETECTED.value,
|
||||
):
|
||||
# Await any in-flight background extractions from previous nodes
|
||||
await self._await_pending_extractions()
|
||||
|
||||
# Perform final variable extraction synchronously before ending
|
||||
await self._perform_variable_extraction_if_needed(
|
||||
self._current_node, run_in_background=False
|
||||
)
|
||||
# Flush in-flight + current-node extractions synchronously before ending
|
||||
await self.perform_final_variable_extraction()
|
||||
|
||||
frame_to_push = (
|
||||
CancelFrame(reason=reason) if abort_immediately else EndFrame(reason=reason)
|
||||
|
|
@ -766,6 +788,20 @@ class PipecatEngine:
|
|||
call_tags.append(call_disposition)
|
||||
self._gathered_context["call_tags"] = call_tags
|
||||
|
||||
# Hangup strategies run while serializing the terminal frame. Persist
|
||||
# the final extracted values first so external-PBX adapters can apply
|
||||
# workflow lead-field mappings before terminating the customer leg.
|
||||
try:
|
||||
await db_client.update_workflow_run(
|
||||
run_id=self._workflow_run_id,
|
||||
gathered_context=self._gathered_context,
|
||||
)
|
||||
except Exception as exc:
|
||||
# Call teardown must never be held hostage by an enrichment write.
|
||||
logger.warning(
|
||||
f"Could not persist final gathered context before hangup: {exc}"
|
||||
)
|
||||
|
||||
logger.debug(
|
||||
f"Finishing run with reason: {reason}, disposition: {call_disposition} "
|
||||
f"queueing frame {frame_to_push}"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue