* feat: Cal.ai Self Serve #2 * chore: fix import and remove logs * fix: update checkout session * fix: type errors and test * fix: imports * fix: type err * fix: type error * fix: tests * chore: save progress * fix: workflow flow * fix: workflow update bug * tests: add unit tests for retell ai webhoo * fix: status code * fix: test and delete bug * fix: add dynamic variables * fix: type err * chore: update unit test * fix: type error * chore: update default prompt * fix: type errors * fix: workflow permissions * fix: workflow page * fix: translations * feat: add call duration * chore: add booking uid * fix: button positioning * chore: update tests * chore: improvements * chore: some more improvements * refactor: improvements * refactor: code feedback * refactor: improvements * feat: enable credits for orgs (#23077) * Show credits UI for orgs * fix stripe callback url when buying credits * give orgs 20% credits * add test for calulating credits --------- Co-authored-by: CarinaWolli <wollencarina@gmail.com> Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com> * fix: types * fix: types * chore: error * fix: type error * fix: type error * chore: mock env * feat: add idempotency key to prevent double charging * chore: add userId and teamId * fix: skip inbound calls * chore: update tests * feat: add feature flag for voice agent * feat: finish test call and other improvements * chore: add alert * chore: update .env.example * chore: improvements * fix: update tests * refactor: remove un necessary * feat: add setup badge * chore: improvements * fix: use referene id * chore: improvements * fix: type error * fix: type * refactor: change pricing logic * refactor: update tests * fix: conflicts * fix: billing link for orgs * fix: types * refactor: move feature flag up * fix: alert and test call credit check * fix: update unit tests * fix: feedback * refactor: improvements * refactor: move handlers to separate files * fix: types * fix: missing import * fix: type * refactor: change general tools functions handling * refactor: use repository * refactor: improvements * fix: types * fix: type errorr * fix: auth check * feat: add creditFor * fix: update defualt prompt * fix: throw error on frontend * fix: update unit tests * fix: use deleteAllWorkflowReminders * refactor: add connect phone number * refactor: improvements * chore: translation * chore: update message * chore: translation * design improvements buy number dialog * add translation for error message * use translation key in error message * refactor: improve connect phone number tab * feat: support un saved workflow to tests * chore: remove un used * fix: remove un used * fix: remove un used * refactor: similify billing --------- Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com> Co-authored-by: CarinaWolli <wollencarina@gmail.com> Co-authored-by: Peer Richelsen <peeroke@gmail.com> Co-authored-by: Keith Williams <keithwillcode@gmail.com>
127 lines
3.4 KiB
TypeScript
127 lines
3.4 KiB
TypeScript
import { PROMPT_TEMPLATES, LegacyRetellAIService } from "@calcom/features/calAIPhone";
|
|
import { validatePhoneNumber } from "@calcom/features/calAIPhone/retellAIService";
|
|
import { templateTypeEnum } from "@calcom/features/calAIPhone";
|
|
import type { TCreatePhoneCallSchema } from "@calcom/features/calAIPhone";
|
|
import { checkRateLimitAndThrowError } from "@calcom/lib/checkRateLimitAndThrowError";
|
|
import logger from "@calcom/lib/logger";
|
|
import prisma from "@calcom/prisma";
|
|
|
|
import { TRPCError } from "@trpc/server";
|
|
|
|
export const handleCreatePhoneCall = async ({
|
|
user,
|
|
input,
|
|
}: {
|
|
user: { timeZone: string; id: number; profile?: { organization?: { id?: number } } };
|
|
input: TCreatePhoneCallSchema;
|
|
}) => {
|
|
if (!user?.profile?.organization) {
|
|
throw new TRPCError({ code: "UNAUTHORIZED", message: "User is not part of an organization" });
|
|
}
|
|
|
|
await checkRateLimitAndThrowError({
|
|
rateLimitingType: "core",
|
|
identifier: `createPhoneCall:${user.id}`,
|
|
});
|
|
|
|
await validatePhoneNumber(input.yourPhoneNumber);
|
|
|
|
const {
|
|
yourPhoneNumber,
|
|
numberToCall,
|
|
guestName,
|
|
guestEmail,
|
|
guestCompany,
|
|
eventTypeId,
|
|
beginMessage,
|
|
calApiKey,
|
|
templateType,
|
|
schedulerName,
|
|
generalPrompt: userCustomPrompt,
|
|
} = input;
|
|
|
|
const generalPrompt =
|
|
templateType === templateTypeEnum.enum.CUSTOM_TEMPLATE
|
|
? userCustomPrompt
|
|
: PROMPT_TEMPLATES[templateType]?.generalPrompt;
|
|
|
|
const retellAI = new LegacyRetellAIService({
|
|
templateType,
|
|
generalPrompt: generalPrompt ?? "",
|
|
beginMessage: beginMessage ?? null,
|
|
yourPhoneNumber,
|
|
loggedInUserTimeZone: user.timeZone,
|
|
eventTypeId,
|
|
calApiKey,
|
|
dynamicVariables: {
|
|
guestName,
|
|
guestEmail,
|
|
guestCompany,
|
|
schedulerName,
|
|
},
|
|
});
|
|
|
|
const aiPhoneCallConfig = await prisma.aIPhoneCallConfiguration.upsert({
|
|
where: {
|
|
eventTypeId,
|
|
},
|
|
update: {
|
|
beginMessage,
|
|
enabled: true,
|
|
guestName,
|
|
guestEmail,
|
|
guestCompany,
|
|
numberToCall,
|
|
yourPhoneNumber,
|
|
schedulerName,
|
|
templateType,
|
|
generalPrompt,
|
|
},
|
|
create: {
|
|
eventTypeId,
|
|
beginMessage,
|
|
enabled: true,
|
|
guestName,
|
|
guestEmail,
|
|
guestCompany,
|
|
numberToCall,
|
|
yourPhoneNumber,
|
|
schedulerName,
|
|
templateType,
|
|
generalPrompt,
|
|
},
|
|
});
|
|
|
|
// If no retell LLM is associated with the event type, create one
|
|
if (!aiPhoneCallConfig.llmId) {
|
|
const createdRetellLLM = await retellAI.createRetellLLMAndUpdateWebsocketUrl();
|
|
|
|
await prisma.aIPhoneCallConfiguration.update({
|
|
where: {
|
|
eventTypeId,
|
|
},
|
|
data: {
|
|
llmId: createdRetellLLM.llm_id,
|
|
},
|
|
});
|
|
} else {
|
|
// aiPhoneCallConfig.llmId would be set here in the else block
|
|
const retellLLM = await retellAI.getRetellLLM(aiPhoneCallConfig.llmId as string);
|
|
|
|
const shouldUpdateLLM =
|
|
retellLLM.general_prompt !== generalPrompt || retellLLM.begin_message !== beginMessage;
|
|
|
|
if (shouldUpdateLLM) {
|
|
const updatedRetellLLM = await retellAI.updatedRetellLLMAndUpdateWebsocketUrl(
|
|
aiPhoneCallConfig.llmId as string
|
|
);
|
|
logger.debug("updated Retell LLM", updatedRetellLLM);
|
|
}
|
|
}
|
|
|
|
const createPhoneCallRes = await retellAI.createRetellPhoneCall(numberToCall);
|
|
logger.debug("Create Call Response", createPhoneCallRes);
|
|
|
|
return { callId: createPhoneCallRes.call_id, agentId: createPhoneCallRes.agent_id };
|
|
};
|