cal-core 0.2.158

Callable core lib
Documentation
use crate::device::shared::{AiOptions, RecordOptions, TranscribeOptions};
use crate::{format_number, AsrVendor, FlowState, NumberFormat, Region, VoiceServer};
use cal_jambonz::dial::TranscribeDial;
use cal_jambonz::listen::Listen;
use cal_jambonz::recognizer::Recognizer;
use cal_jambonz::vendors::amazon::{AWSRecognizer, AwsAsrLanguage};
use cal_jambonz::vendors::deepgram::DeepgramRecognizer;
use cal_jambonz::vendors::google::{
    GoogleInteractionType, GoogleRecognizer, GoogleRecognizerLanguage, GoogleSpeechModel,
};
use cal_jambonz::vendors::ibm::IbmRecognizer;
use cal_jambonz::vendors::microsoft::MSRecognizer;
use cal_jambonz::vendors::nvidia::NvidiaRecognizer;
use cal_jambonz::vendors::soniox::SonioxRecognizer;
use cal_jambonz::verbs;
use rand::prelude::SliceRandom;
use rand::rng;
use std::collections::HashMap;
use std::sync::Arc;

pub fn get_transcribe(transcribe_opts: TranscribeOptions) -> Option<TranscribeDial> {
    if transcribe_opts.enabled {
        match transcribe_opts.language {
            AsrVendor::Deepgram => {
                let transcribe = TranscribeDial {
                    transcription_hook: "transcribe".to_string(),
                    recognizer: Recognizer::Deepgram(DeepgramRecognizer {
                        language: None,
                        separate_recognition_per_channel: Some(true),
                        hints: None,
                        vad: None,
                        interim: None,
                        alt_languages: None,
                        asr_dtmf_termination_digit: None,
                        asr_timeout: None,
                        deepgram_options: None,
                    }),
                };
                Some(transcribe)
            }
            AsrVendor::Google => {
                let transcribe = TranscribeDial {
                    transcription_hook: "transcribe".to_string(),
                    recognizer: Recognizer::Google(GoogleRecognizer {
                        language: Some(GoogleRecognizerLanguage::EnglishUnitedKingdom),
                        interaction_type: Some(GoogleInteractionType::PhoneCall),
                        model: Some(GoogleSpeechModel::PhoneCall),
                        separate_recognition_per_channel: Some(true),
                        enhanced_model: Some(true),
                        vad: None,
                        interim: None,
                        alt_languages: None,
                        asr_dtmf_termination_digit: None,
                        asr_timeout: None,
                        diarization: None,
                        diarization_min_speakers: None,
                        diarization_max_speakers: None,
                        hints: None,
                        hints_boost: None,
                        naics_code: None,
                        punctuation: None,
                        single_utterance: None,
                    }),
                };
                Some(transcribe)
            }
            AsrVendor::Aws => {
                let transcribe = TranscribeDial {
                    transcription_hook: "transcribe".to_string(),
                    recognizer: Recognizer::Aws(AWSRecognizer {
                        language: Some(AwsAsrLanguage::EnglishBritish),
                        separate_recognition_per_channel: Some(true),
                        identify_channels: Some(true),
                        vad: None,
                        interim: None,
                        alt_languages: None,
                        asr_dtmf_termination_digit: None,
                        asr_timeout: None,
                        filter_method: None,
                        vocabulary_name: None,
                        vocabulary_filter_name: None,
                    }),
                };
                Some(transcribe)
            }
            AsrVendor::Ibm => {
                let transcribe = TranscribeDial {
                    transcription_hook: "transcribe".to_string(),
                    recognizer: Recognizer::Ibm(IbmRecognizer {
                        vad: None,
                        interim: None,
                        language: None,
                        alt_languages: None,
                        asr_dtmf_termination_digit: None,
                        asr_timeout: None,
                        separate_recognition_per_channel: Some(true),
                        ibm_options: None,
                    }),
                };
                Some(transcribe)
            }
            AsrVendor::Microsoft => {
                let transcribe = TranscribeDial {
                    transcription_hook: "transcribe".to_string(),
                    recognizer: Recognizer::Microsoft(MSRecognizer {
                        vad: None,
                        interim: None,
                        language: None,
                        alt_languages: None,
                        asr_dtmf_termination_digit: None,
                        asr_timeout: None,
                        separate_recognition_per_channel: None,
                        azure_service_endpoint: None,
                        azure_options: None,
                        hints: None,
                        initial_speech_timeout_ms: None,
                        profanity_filter: None,
                        profanity_option: None,
                        output_format: None,
                        request_snr: None,
                    }),
                };
                Some(transcribe)
            }
            AsrVendor::Nuance => {
                let transcribe = TranscribeDial {
                    transcription_hook: "transcribe".to_string(),
                    recognizer: Recognizer::Microsoft(MSRecognizer {
                        vad: None,
                        interim: None,
                        language: None,
                        alt_languages: None,
                        asr_dtmf_termination_digit: None,
                        asr_timeout: None,
                        separate_recognition_per_channel: None,
                        azure_service_endpoint: None,
                        azure_options: None,
                        hints: None,
                        initial_speech_timeout_ms: None,
                        profanity_filter: None,
                        profanity_option: None,
                        output_format: None,
                        request_snr: None,
                    }),
                };
                Some(transcribe)
            }
            AsrVendor::Nvidia => {
                let transcribe = TranscribeDial {
                    transcription_hook: "transcribe".to_string(),
                    recognizer: Recognizer::Nvidia(NvidiaRecognizer {
                        vad: None,
                        interim: None,
                        language: None,
                        alt_languages: None,
                        asr_dtmf_termination_digit: None,
                        asr_timeout: None,
                        separate_recognition_per_channel: None,
                        hints: None,
                        hints_boost: None,
                        nvidia_options: None,
                    }),
                };
                Some(transcribe)
            }
            AsrVendor::Soniox => {
                let transcribe = TranscribeDial {
                    transcription_hook: "transcribe".to_string(),
                    recognizer: Recognizer::Soniox(SonioxRecognizer {
                        vad: None,
                        interim: None,
                        language: None,
                        alt_languages: None,
                        asr_dtmf_termination_digit: None,
                        asr_timeout: None,
                        separate_recognition_per_channel: None,
                        hints: None,
                        soniox_options: None,
                    }),
                };
                Some(transcribe)
            }
        }
    } else {
        None
    }
}

pub fn get_caller_id(format: &NumberFormat, state: &FlowState) -> String {
    let caller_id = match state.data.get("from") {
        Some(dt) => dt.value.clone(),
        None => state.initial_request.to.clone(),
    };
    format_number(
        caller_id.as_str(),
        state.account.country_code(),
        &format.clone(),
    )
}

pub fn get_called_id(format: &NumberFormat, state: &FlowState) -> String {
    let called_id = match state.data.get("to") {
        Some(dt) => dt.value.clone(),
        None => state.initial_request.to.clone(),
    };
    format_number(
        called_id.as_str(),
        state.account.country_code(),
        &format.clone(),
    )
}

pub fn get_proxy(
    proxies: Vec<String>,
    state: &FlowState,
    regions: Vec<Arc<Region>>,
) -> Option<VoiceServer> {
    let mut proxies = proxies.clone();
    proxies.shuffle(&mut rng());

    // Prefer current region servers
    for proxy in &proxies {
        if let Some(vs) = state.region.voice_servers.iter().find(|vs| vs.id == *proxy) {
            return Some(vs.clone());
        }
    }

    // Try any other matching server
    for region in &regions {
        for proxy in &proxies {
            if let Some(vs) = region.voice_servers.iter().find(|vs| vs.id == *proxy) {
                return Some(vs.clone());
            }
        }
    }

    None
}

pub fn get_listen(
    record_options: &RecordOptions,
    ai_options: AiOptions,
    state: &FlowState,
) -> Option<Listen> {
    match record_options.enabled {
        true => {
            let mut meta: HashMap<String, String> = HashMap::new();
            meta.insert("type".to_string(), "session:record".to_string());
            meta.insert("account_id".to_string(), state.account.id.clone());
            meta.insert(
                "retention".to_string(),
                record_options.retention.to_string(),
            );
            meta.insert("ai_summary".to_string(), ai_options.summary.to_string());
            meta.insert(
                "ai_transcribe".to_string(),
                ai_options.transcribe.to_string(),
            );

            let transcribe: Option<verbs::transcribe::Transcribe> = match ai_options.recognizer {
                Some(recognizer) => Some(verbs::transcribe::Transcribe {
                    transcription_hook: format!("{}/transcribe", state.base_url),
                    recognizer,
                }),
                None => None,
            };

            let listen = Listen::new(
                format!("{}/record", state.base_url),
                format!("{}/record-complete", state.base_url),
            )
            .mix_type(Some(record_options.mix_type.clone()))
            .transcribe(transcribe)
            .metadata(Some(meta))
            .clone();

            Some(listen)
        }
        false => None,
    }
}