feat: Add end call reason in tool calls.

This commit is contained in:
Abhishek Kumar 2026-02-21 14:21:39 +05:30
parent e111cbb36d
commit 7e2de092ae
13 changed files with 391 additions and 182 deletions

View file

@ -63,7 +63,7 @@ class PipecatEngine:
call_context_vars: dict,
workflow_run_id: Optional[int] = None,
node_transition_callback: Optional[
Callable[[str, Optional[str]], Awaitable[None]]
Callable[[str, str, Optional[str], Optional[str]], Awaitable[None]]
] = None,
embeddings_api_key: Optional[str] = None,
embeddings_model: Optional[str] = None,
@ -456,14 +456,22 @@ class PipecatEngine:
# Track previous node for transition event
previous_node_name = self._current_node.name if self._current_node else None
previous_node_id = self._current_node.id if self._current_node else None
# Set current node for all nodes (including static ones) so STT mute filter works
self._current_node = node
# Track visited nodes in gathered context for call tags
nodes_visited = self._gathered_context.setdefault("nodes_visited", [])
if node.name not in nodes_visited:
nodes_visited.append(node.name)
# Send node transition event if callback is provided
if self._node_transition_callback:
try:
await self._node_transition_callback(node.name, previous_node_name)
await self._node_transition_callback(
node_id, node.name, previous_node_id, previous_node_name
)
except Exception as e:
# Log but don't fail - feedback is non-critical
logger.debug(f"Failed to send node transition event: {e}")

View file

@ -231,6 +231,20 @@ class CustomToolManager:
message_type = config.get("messageType", "none")
custom_message = config.get("customMessage", "")
# Handle end call reason if enabled
end_call_reason_enabled = config.get("endCallReason", False)
if end_call_reason_enabled:
reason = (
function_call_params.arguments.get("reason", "")
or "end_call_tool"
)
logger.info(f"End call reason: {reason}")
self._engine._gathered_context["call_disposition"] = reason
call_tags = self._engine._gathered_context.get("call_tags", [])
if reason not in call_tags:
call_tags.extend([reason, "end_call_tool"])
self._engine._gathered_context["call_tags"] = call_tags
# Send result callback first
await function_call_params.result_callback(
{"status": "success", "action": "ending_call"},

View file

@ -51,6 +51,19 @@ def tool_to_function_schema(tool: Any) -> Dict[str, Any]:
if param_required:
required.append(param_name)
# If this is an end_call tool with endCallReason enabled, add a required 'reason' parameter
if definition.get("type") == "end_call" and config.get("endCallReason", False):
default_description = (
"The reason for ending the call (e.g., 'voicemail_detected', "
"'issue_resolved', 'customer_requested')"
)
properties["reason"] = {
"type": "string",
"description": config.get("endCallReasonDescription")
or default_description,
}
required.append("reason")
# Sanitize tool name for function name (lowercase, underscores only)
function_name = re.sub(r"[^a-z0-9_]", "_", tool.name.lower())
# Remove consecutive underscores and trim