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

@ -4,6 +4,7 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/com
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import { Switch } from "@/components/ui/switch";
import { Textarea } from "@/components/ui/textarea";
import { type EndCallMessageType } from "../../config";
@ -17,6 +18,10 @@ export interface EndCallToolConfigProps {
onMessageTypeChange: (messageType: EndCallMessageType) => void;
customMessage: string;
onCustomMessageChange: (message: string) => void;
endCallReason: boolean;
onEndCallReasonChange: (enabled: boolean) => void;
endCallReasonDescription: string;
onEndCallReasonDescriptionChange: (description: string) => void;
}
export function EndCallToolConfig({
@ -28,6 +33,10 @@ export function EndCallToolConfig({
onMessageTypeChange,
customMessage,
onCustomMessageChange,
endCallReason,
onEndCallReasonChange,
endCallReasonDescription,
onEndCallReasonDescriptionChange,
}: EndCallToolConfigProps) {
return (
<Card>
@ -63,6 +72,35 @@ export function EndCallToolConfig({
/>
</div>
<div className="grid gap-2 pt-4 border-t">
<div className="flex items-center space-x-2">
<Switch
id="end-call-reason"
checked={endCallReason}
onCheckedChange={onEndCallReasonChange}
/>
<Label htmlFor="end-call-reason">Capture End Call Reason</Label>
</div>
<Label className="text-xs text-muted-foreground">
When enabled, the AI will provide a reason for ending the call.
The reason will be set as the call disposition and added to call tags for analytics.
</Label>
{endCallReason && (
<div className="grid gap-2 pt-2">
<Label>Reason Description</Label>
<Label className="text-xs text-muted-foreground">
Instructions shown to the AI for what kind of reason to provide
</Label>
<Textarea
value={endCallReasonDescription}
onChange={(e) => onEndCallReasonDescriptionChange(e.target.value)}
placeholder="e.g., The reason for ending the call (e.g., 'voicemail_detected', 'issue_resolved', 'customer_requested')"
rows={2}
/>
</div>
)}
</div>
<div className="grid gap-4 pt-4 border-t">
<Label>Goodbye Message</Label>
<Label className="text-xs text-muted-foreground">

View file

@ -9,6 +9,7 @@ import {
updateToolApiV1ToolsToolUuidPut,
} from "@/client/sdk.gen";
import type { ToolResponse, TransferCallConfig as APITransferCallConfig } from "@/client/types.gen";
import type { EndCallConfig } from "@/client/types.gen";
import { type HttpMethod, type KeyValueItem, type ToolParameter, validateUrl } from "@/components/http";
import { Button } from "@/components/ui/button";
import {
@ -22,7 +23,7 @@ import { Skeleton } from "@/components/ui/skeleton";
import { useAuth } from "@/lib/auth";
import {
type EndCallConfig,
DEFAULT_END_CALL_REASON_DESCRIPTION,
type EndCallMessageType,
getCategoryConfig,
getToolTypeLabel,
@ -68,6 +69,15 @@ export default function ToolDetailPage() {
// End Call form state
const [endCallMessageType, setEndCallMessageType] = useState<EndCallMessageType>("none");
const [endCallCustomMessage, setEndCallCustomMessage] = useState("");
const [endCallReason, setEndCallReason] = useState(false);
const [endCallReasonDescription, setEndCallReasonDescription] = useState("");
const handleEndCallReasonChange = (enabled: boolean) => {
setEndCallReason(enabled);
if (enabled && !endCallReasonDescription) {
setEndCallReasonDescription(DEFAULT_END_CALL_REASON_DESCRIPTION);
}
};
// Transfer Call form state
const [transferDestination, setTransferDestination] = useState("");
@ -119,9 +129,13 @@ export default function ToolDetailPage() {
if (config) {
setEndCallMessageType(config.messageType || "none");
setEndCallCustomMessage(config.customMessage || "");
setEndCallReason(config.endCallReason ?? false);
setEndCallReasonDescription(config.endCallReasonDescription || "");
} else {
setEndCallMessageType("none");
setEndCallCustomMessage("");
setEndCallReason(false);
setEndCallReasonDescription("");
}
} else if (tool.category === "transfer_call") {
// Populate transfer call specific fields
@ -225,6 +239,8 @@ export default function ToolDetailPage() {
config: {
messageType: endCallMessageType,
customMessage: endCallMessageType === "custom" ? endCallCustomMessage : undefined,
endCallReason,
endCallReasonDescription: endCallReason ? endCallReasonDescription || undefined : undefined,
},
},
};
@ -432,6 +448,10 @@ const data = await response.json();`;
onMessageTypeChange={setEndCallMessageType}
customMessage={endCallCustomMessage}
onCustomMessageChange={setEndCallCustomMessage}
endCallReason={endCallReason}
onEndCallReasonChange={handleEndCallReasonChange}
endCallReasonDescription={endCallReasonDescription}
onEndCallReasonDescriptionChange={setEndCallReasonDescription}
/>
) : isTransferCallTool ? (
<TransferCallToolConfig

View file

@ -3,6 +3,8 @@
import { Cog, Globe, type LucideIcon, PhoneForwarded, PhoneOff, Puzzle } from "lucide-react";
import { type ReactNode } from "react";
import type { EndCallConfig } from "@/client/types.gen";
export type ToolCategory = "http_api" | "end_call" | "transfer_call" | "native" | "integration";
export type EndCallMessageType = "none" | "custom";
@ -110,15 +112,13 @@ export function getToolTypeLabel(category: string): string {
}
}
// End Call tool specific configuration
export interface EndCallConfig {
messageType: EndCallMessageType;
customMessage?: string;
}
export const DEFAULT_END_CALL_REASON_DESCRIPTION =
"The reason for ending the call (e.g., 'voicemail_detected', 'issue_resolved', 'customer_requested')";
export const DEFAULT_END_CALL_CONFIG: EndCallConfig = {
messageType: "none",
customMessage: "",
endCallReason: false,
};
// Transfer Call tool specific configuration

View file

@ -569,6 +569,14 @@ export type EndCallConfig = {
* Custom message to play before ending the call
*/
customMessage?: string | null;
/**
* When enabled, LLM must provide a reason for ending the call. The reason is set as call disposition and added to call tags.
*/
endCallReason?: boolean;
/**
* Description shown to the LLM for the reason parameter. Used only when endCallReason is enabled.
*/
endCallReasonDescription?: string | null;
};
/**