* 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>
187 lines
5.7 KiB
TypeScript
187 lines
5.7 KiB
TypeScript
import { ensureAIPhoneServiceRegistryInitialized } from "./initializeRegistry";
|
|
import { AIPhoneServiceProviderType } from "./interfaces/AIPhoneService.interface";
|
|
import type {
|
|
AIPhoneServiceProvider,
|
|
AIPhoneServiceProviderFactory,
|
|
AIPhoneServiceProviderConfig,
|
|
} from "./interfaces/AIPhoneService.interface";
|
|
|
|
/**
|
|
* Configuration for the registry
|
|
*/
|
|
interface RegistryConfiguration {
|
|
defaultProvider?: string;
|
|
providers?: Array<{
|
|
type: string;
|
|
factory: AIPhoneServiceProviderFactory;
|
|
}>;
|
|
}
|
|
|
|
/**
|
|
* Registry for AI phone service providers
|
|
* Allows registering and creating different AI service providers
|
|
*/
|
|
export class AIPhoneServiceRegistry {
|
|
private static factories: Map<string, AIPhoneServiceProviderFactory> = new Map();
|
|
private static defaultProvider: string = AIPhoneServiceProviderType.RETELL_AI;
|
|
private static initialized = false;
|
|
|
|
/**
|
|
* Initialize the registry with configuration
|
|
* This should be called during application startup
|
|
*/
|
|
static initialize(config?: RegistryConfiguration): void {
|
|
// Register providers if provided
|
|
if (config?.providers) {
|
|
config.providers.forEach(({ type, factory }) => {
|
|
this.registerProvider(type, factory);
|
|
});
|
|
}
|
|
|
|
// Set default provider if provided, otherwise use the first registered provider
|
|
if (config?.defaultProvider) {
|
|
this.setDefaultProvider(config.defaultProvider);
|
|
} else if (this.factories.size > 0) {
|
|
this.defaultProvider = Array.from(this.factories.keys())[0];
|
|
}
|
|
|
|
this.initialized = true;
|
|
}
|
|
|
|
static registerProvider(type: string, factory: AIPhoneServiceProviderFactory): void {
|
|
this.factories.set(type, factory);
|
|
|
|
// If no default provider is set and this is the first provider, make it default
|
|
if (!this.defaultProvider) {
|
|
this.defaultProvider = type;
|
|
}
|
|
}
|
|
|
|
static getProviderFactory(type: string): AIPhoneServiceProviderFactory | undefined {
|
|
return this.factories.get(type);
|
|
}
|
|
|
|
static createProvider(type: string, config: AIPhoneServiceProviderConfig): AIPhoneServiceProvider {
|
|
const factory = this.getProviderFactory(type);
|
|
if (!factory) {
|
|
throw new Error(
|
|
`AI phone service provider '${type}' not found. Available providers: ${Array.from(
|
|
this.factories.keys()
|
|
).join(", ")}`
|
|
);
|
|
}
|
|
return factory.create(config);
|
|
}
|
|
|
|
/**
|
|
* Create a provider instance using the default provider
|
|
*/
|
|
static createDefaultProvider(config: AIPhoneServiceProviderConfig): AIPhoneServiceProvider {
|
|
if (!this.defaultProvider) {
|
|
throw new Error(
|
|
"No default provider set. Please initialize the registry with a default provider or register at least one provider."
|
|
);
|
|
}
|
|
return this.createProvider(AIPhoneServiceRegistry.defaultProvider, config);
|
|
}
|
|
|
|
static setDefaultProvider(type: string): void {
|
|
if (!this.factories.has(type)) {
|
|
throw new Error(`Cannot set default provider to '${type}' - provider not registered`);
|
|
}
|
|
this.defaultProvider = type;
|
|
}
|
|
|
|
static getDefaultProvider(): string | null {
|
|
return AIPhoneServiceRegistry.defaultProvider;
|
|
}
|
|
|
|
static getAvailableProviders(): string[] {
|
|
return Array.from(this.factories.keys());
|
|
}
|
|
|
|
static isProviderRegistered(type: string): boolean {
|
|
return this.factories.has(type);
|
|
}
|
|
|
|
/**
|
|
* Clear all registered providers (mainly for testing purposes)
|
|
*/
|
|
static clearProviders(): void {
|
|
this.factories.clear();
|
|
this.defaultProvider = AIPhoneServiceProviderType.RETELL_AI;
|
|
this.initialized = false;
|
|
}
|
|
|
|
static isInitialized(): boolean {
|
|
return AIPhoneServiceRegistry.initialized;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Provider-specific API key resolution
|
|
* This function determines the appropriate API key based on the provider type
|
|
*/
|
|
function resolveApiKey(providerType: string, config?: Partial<AIPhoneServiceProviderConfig>): string {
|
|
// First check if API key is provided in config
|
|
if (config?.apiKey) {
|
|
return config.apiKey;
|
|
}
|
|
|
|
// Then check environment variables based on provider type
|
|
const envVarMap: Record<string, string | undefined> = {
|
|
[AIPhoneServiceProviderType.RETELL_AI]: process.env.RETELL_AI_KEY,
|
|
// Add more providers here as they are implemented
|
|
};
|
|
|
|
const apiKey = envVarMap[providerType];
|
|
|
|
if (!apiKey) {
|
|
throw new Error(
|
|
`API key not configured for provider type: ${providerType}. ` +
|
|
`Please set the API key in the config or provide the appropriate environment variable.`
|
|
);
|
|
}
|
|
|
|
return apiKey;
|
|
}
|
|
|
|
/**
|
|
* Options for creating an AI phone service provider
|
|
*/
|
|
interface CreateProviderOptions {
|
|
/** Provider type to use. If not specified, uses the default provider */
|
|
providerType?: string;
|
|
/** Configuration options for the provider */
|
|
config?: Partial<AIPhoneServiceProviderConfig>;
|
|
}
|
|
|
|
export function createAIPhoneServiceProvider(options: CreateProviderOptions = {}): AIPhoneServiceProvider {
|
|
// Ensure the registry is initialized before creating a provider
|
|
ensureAIPhoneServiceRegistryInitialized();
|
|
|
|
const { providerType, config } = options;
|
|
|
|
// Use provided type or fall back to default provider
|
|
const type = providerType || AIPhoneServiceRegistry.getDefaultProvider();
|
|
if (!type) {
|
|
throw new Error("No provider type specified and no default provider configured");
|
|
}
|
|
|
|
const apiKey = resolveApiKey(type, config);
|
|
|
|
const providerConfig: AIPhoneServiceProviderConfig = {
|
|
apiKey,
|
|
enableLogging: process.env.NODE_ENV !== "production",
|
|
...config,
|
|
};
|
|
|
|
return AIPhoneServiceRegistry.createProvider(type, providerConfig);
|
|
}
|
|
|
|
export function createDefaultAIPhoneServiceProvider(
|
|
config?: Partial<AIPhoneServiceProviderConfig>
|
|
): AIPhoneServiceProvider {
|
|
return createAIPhoneServiceProvider({ config });
|
|
}
|