Compare commits

...

16 Commits

Author SHA1 Message Date
snyk-bot
0b061b982f fix: Dockerfile to reduce vulnerabilities
The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-ALPINE317-OPENSSL-3368755
- https://snyk.io/vuln/SNYK-ALPINE317-OPENSSL-3368755
- https://snyk.io/vuln/SNYK-ALPINE317-OPENSSL-5291795
- https://snyk.io/vuln/SNYK-ALPINE317-OPENSSL-6032385
- https://snyk.io/vuln/SNYK-ALPINE317-OPENSSL-6032385
2024-03-28 23:18:09 +00:00
Hoan Luu Huu
e8355a1dd3 issue 409, shoud not get Invoices in self-hosted system (#412) 2024-03-19 07:23:10 -04:00
Hoan Luu Huu
8be61ddfad remove use_stream in elevenlabs and whisper speech (#406) 2024-02-20 08:01:12 -05:00
Dave Horton
05c1d9efaa prettier fix 2024-02-12 09:03:12 -05:00
Dave Horton
01a5476dfe update github actions 2024-02-12 08:57:49 -05:00
Hoan Luu Huu
9d2fee64e6 Support deepgram onprem (#398)
* Support deepgram onprem

* wip

* wip
2024-02-12 08:52:54 -05:00
Hoan Luu Huu
3a87f5f1c2 support use_streaming elevenlabs and whisper (#396) 2024-02-12 08:16:31 -05:00
Hoan Luu Huu
a991b56a4e Fix/speech selection after updating application (#395)
* fix speech selection show app languge and voice

* wip

* wip

* wip

* wip
2024-01-27 11:26:37 -05:00
Dave Horton
6e14207327 sync with change of property name to time_to_first_byte_ms (#394) 2024-01-25 13:16:42 -05:00
Hoan Luu Huu
8d8d46e76e draw tts region label to lower possition (#393) 2024-01-24 19:48:56 -05:00
Hoan Luu Huu
7f72d739cd Fix/tts region use tts_time_to_first_byte streaming api (#391)
* tts region tts_time_to_first_byte_ms

* wip
2024-01-24 10:19:46 -05:00
Hoan Luu Huu
c804d60664 wavesurfer region use s instead of sec (#390) 2024-01-24 08:44:31 -05:00
Hoan Luu Huu
df3fc8f2b7 playback latency region on wavesurfer (#387)
* playback latency region on wavesurfer

* remove start-audio region
2024-01-22 20:31:02 -05:00
Hoan Luu Huu
65e5b511c3 gather speech verb hook latency (#383)
* gather speech verb hook latency

* wip
2024-01-17 12:37:36 -05:00
Hoan Luu Huu
dc519bdef9 fix remove speech label, cause application save wrong label (#384)
* fix remove speech label, cause application save wrong label

* fix microsoft custom_tts input
2024-01-17 09:17:25 -05:00
Hoan Luu Huu
af1ba3a15c fix app cannot set elevenlab tts (#386) 2024-01-17 07:30:36 -05:00
10 changed files with 391 additions and 89 deletions

View File

@@ -12,18 +12,18 @@ jobs:
pr-checks:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Cache node_modules
id: node-cache
uses: actions/cache@v2
uses: actions/cache@v3
with:
path: node_modules
key: node-modules-${{ hashFiles('package-lock.json') }}
- name: Cache cypress binary
id: cypress-cache
uses: actions/cache@v2
uses: actions/cache@v3
with:
path: /home/runner/.cache/Cypress
key: cypress-${{ hashFiles('package-lock.json') }}

View File

@@ -6,7 +6,7 @@ RUN npm install
RUN npm run build
RUN npm prune
FROM node:18.14.1-alpine as webapp
FROM node:18.19.1-alpine as webapp
RUN apk add curl
WORKDIR /opt/app
COPY . /opt/app

View File

@@ -51,6 +51,11 @@ export interface WaveSurferTtsLatencyResult {
isCached: string;
}
export interface WaveSurferGatherSpeechVerbHookLatencyResult {
statusCode: number;
latency: string;
}
export interface WaveSurferDtmfResult {
dtmf: string;
duration: string;

View File

@@ -419,6 +419,8 @@ export interface SpeechCredential {
model_id: null | string;
model: null | string;
options: null | string;
deepgram_stt_uri: null | string;
deepgram_stt_use_tls: number;
}
export interface Alert {

View File

@@ -11,6 +11,7 @@ type CheckzoneProps = {
hidden?: boolean;
children: React.ReactNode;
initialCheck: boolean;
disabled?: boolean;
handleChecked?: (e: React.ChangeEvent<HTMLInputElement>) => void;
};
@@ -28,6 +29,7 @@ export const Checkzone = forwardRef<CheckzoneRef, CheckzoneProps>(
children,
initialCheck,
handleChecked,
disabled = false,
}: CheckzoneProps,
ref
) => {
@@ -51,6 +53,7 @@ export const Checkzone = forwardRef<CheckzoneRef, CheckzoneProps>(
<label>
<div className="label-container">
<input
disabled={disabled}
ref={ref}
type="checkbox"
name={name}

View File

@@ -40,6 +40,7 @@ import {
PlanType,
USER_ACCOUNT,
WEBHOOK_METHODS,
STRIPE_PUBLISHABLE_KEY,
} from "src/api/constants";
import { MSG_REQUIRED_FIELDS, MSG_WEBHOOK_FIELDS } from "src/constants";
@@ -85,7 +86,10 @@ export const AccountForm = ({
const user = useSelectState("user");
const currentServiceProvider = useSelectState("currentServiceProvider");
const [accounts] = useApiData<Account[]>("Accounts");
const [invoice] = useApiData<Invoice>("Invoices");
// Dont get Invoices if the environment is self-hosted
const [invoice] = STRIPE_PUBLISHABLE_KEY
? useApiData<Invoice>("Invoices")
: [undefined];
const [userData] = useApiData<CurrentUserData>("Users/me");
const [userCarriers] = useApiData<Carrier[]>(`VoipCarriers`);
const [userSpeechs] = useApiData<SpeechCredential[]>(

View File

@@ -386,6 +386,53 @@ export const ApplicationForm = ({ application }: ApplicationFormProps) => {
}
}, [accountSid]);
useEffect(() => {
let label: string;
// Synthesis Label
label = application?.data?.speech_synthesis_label || "";
if (ttsLabelOptions && !ttsLabelOptions.find((l) => l.value === label)) {
label = ttsLabelOptions.length ? ttsLabelOptions[0].value : "";
}
setSynthLabel(label);
// fallback Synthesis Label
label = application?.data?.fallback_speech_synthesis_label || "";
if (
fallbackTtsLabelOptions &&
!fallbackTtsLabelOptions.find((l) => l.value === label)
) {
label = fallbackTtsLabelOptions.length
? fallbackTtsLabelOptions[0].value
: "";
}
setFallbackSpeechSynthsisLabel(label);
// regconizer label
label = application?.data?.speech_recognizer_label || "";
if (sttLabelOptions && !sttLabelOptions.find((l) => l.value === label)) {
label = sttLabelOptions.length ? sttLabelOptions[0].value : "";
}
setRecogLabel(label);
// fallback regconizer label
label = application?.data?.fallback_speech_recognizer_label || "";
if (
fallbackSttLabelOptions &&
!fallbackSttLabelOptions.find((l) => l.value === label)
) {
label = fallbackSttLabelOptions.length
? fallbackSttLabelOptions[0].value
: "";
}
setFallbackSpeechRecognizerLabel(label);
}, [
ttsLabelOptions,
sttLabelOptions,
fallbackTtsLabelOptions,
fallbackSttLabelOptions,
application,
]);
useEffect(() => {
setLocation();
if (application && application.data) {
@@ -462,12 +509,7 @@ export const ApplicationForm = ({ application }: ApplicationFormProps) => {
if (application.data.speech_recognizer_language)
setRecogLang(application.data.speech_recognizer_language);
if (application.data.speech_synthesis_label) {
setSynthLabel(application.data.speech_synthesis_label);
}
if (application.data.speech_recognizer_label) {
setRecogLabel(application.data.speech_recognizer_label);
}
if (application.data.use_for_fallback_speech) {
setUseForFallbackSpeech(application.data.use_for_fallback_speech > 0);
setInitalCheckFallbackSpeech(
@@ -485,11 +527,7 @@ export const ApplicationForm = ({ application }: ApplicationFormProps) => {
application.data.fallback_speech_recognizer_language
);
}
if (application.data.fallback_speech_recognizer_label) {
setFallbackSpeechRecognizerLabel(
application.data.fallback_speech_recognizer_label
);
}
if (application.data.fallback_speech_synthesis_vendor) {
setFallbackSpeechSynthsisVendor(
application.data
@@ -506,11 +544,6 @@ export const ApplicationForm = ({ application }: ApplicationFormProps) => {
application.data.fallback_speech_synthesis_voice
);
}
if (application.data.fallback_speech_synthesis_label) {
setFallbackSpeechSynthsisLabel(
application.data.fallback_speech_synthesis_label
);
}
}
}, [application]);

View File

@@ -99,27 +99,37 @@ export const SpeechProviderSelection = ({
SelectorOption[]
>([]);
const currentVendor = useRef("");
const currentServiceProvider = useSelectState("currentServiceProvider");
const currentTtsVendor = useRef(synthVendor);
const currentSttVendor = useRef(recogVendor);
const shouldUpdateTtsVoice = useRef(false);
const shouldUpdateSttLanguage = useRef(false);
const ttsEffectTimer = useRef<number | null>(null);
const sttEffectTimer = useRef<number | null>(null);
// Get Synthesis languages and voices
useEffect(() => {
if (
!user ||
!synthVendor ||
(user?.scope === USER_ADMIN && !serviceProviderSid)
) {
return;
}
currentTtsVendor.current = synthVendor;
/** When Custom Vendor is used, user you have to input the lange and voice. */
if (synthVendor.toString().startsWith(VENDOR_CUSTOM)) {
setSynthVoice("");
return;
}
currentVendor.current = synthVendor;
if (
!user ||
(user?.scope === USER_ADMIN &&
!currentServiceProvider?.service_provider_sid)
) {
return;
// just execute last change
if (ttsEffectTimer.current) {
clearTimeout(ttsEffectTimer.current);
}
configSynthesis();
}, [synthVendor, synthLabel, currentServiceProvider]);
ttsEffectTimer.current = setTimeout(() => {
configSynthesis();
}, 200);
}, [synthVendor, synthLabel, serviceProviderSid]);
// Get Recognizer languages and voices
useEffect(() => {
@@ -130,19 +140,27 @@ export const SpeechProviderSelection = ({
}
if (
!user ||
(user?.scope === USER_ADMIN &&
!currentServiceProvider?.service_provider_sid)
!recogVendor ||
(user?.scope === USER_ADMIN && !serviceProviderSid)
) {
return;
}
configRecognizer();
}, [recogVendor, recogLabel, currentServiceProvider]);
currentSttVendor.current = recogVendor;
// just execute last change
if (sttEffectTimer.current) {
clearTimeout(sttEffectTimer.current);
}
sttEffectTimer.current = setTimeout(() => {
configRecognizer();
}, 200);
}, [recogVendor, recogLabel, serviceProviderSid]);
useEffect(() => {
if (credentials) {
setSelectedCredential(
credentials.find(
(c) => c.vendor === synthVendor && c.label === synthLabel
(c) => c.vendor === synthVendor && (c.label || "") === synthLabel
)
);
}
@@ -161,45 +179,46 @@ export const SpeechProviderSelection = ({
if (synthesisSupportedLanguagesAndVoices) {
// Extract Voice
const voicesOpts =
synthesisSupportedLanguagesAndVoices.tts.find((lang) => {
synthesisSupportedLanguagesAndVoices.tts?.find((lang) => {
if (synthVendor === VENDOR_ELEVENLABS && lang.voices.length > 0) {
return true;
}
return lang.value === synthLang;
})?.voices || [];
setSynthesisVoiceOptions(voicesOpts);
if (synthVendor === VENDOR_GOOGLE) {
getGoogleCustomVoices({
...(synthLabel && { label: synthLabel }),
account_sid: accountSid,
service_provider_sid: serviceProviderSid,
}).then(({ json }) => {
// If after successfully fetching data, vendor is still good, then apply value
if (currentVendor.current !== VENDOR_GOOGLE) {
return;
}
const customVOices = json.map((v) => ({
name: `${v.name} (Custom)`,
value: `custom_${v.google_custom_voice_sid}`,
}));
setSynthesisGoogleCustomVoiceOptions(customVOices);
setSynthesisVoiceOptions([...customVOices, ...voicesOpts]);
if (customVOices.length > 0) {
setSynthVoice(customVOices[0].value);
}
});
if (synthVendor === VENDOR_GOOGLE && synthesisGoogleCustomVoiceOptions) {
if (synthesisGoogleCustomVoiceOptions) {
setSynthesisVoiceOptions([
...synthesisGoogleCustomVoiceOptions,
...voicesOpts,
]);
} else {
setSynthesisVoiceOptions(voicesOpts);
}
if (synthesisGoogleCustomVoiceOptions.length > 0) {
updateTtsVoice(synthesisGoogleCustomVoiceOptions[0].value);
}
} else {
setSynthesisVoiceOptions(voicesOpts);
}
}
}, [synthLang, synthesisSupportedLanguagesAndVoices]);
}, [
synthLang,
synthesisSupportedLanguagesAndVoices,
synthesisGoogleCustomVoiceOptions,
]);
const configSynthesis = () => {
getSpeechSupportedLanguagesAndVoices(
currentServiceProvider?.service_provider_sid,
serviceProviderSid,
synthVendor,
synthLabel
)
.then(({ json }) => {
// while fetching data, user might change the vendor
if (currentTtsVendor.current !== synthVendor) {
return;
}
setSynthesisSupportedLanguagesAndVoices(json);
// Extract model
if (json.models && json.models.length) {
@@ -219,26 +238,27 @@ export const SpeechProviderSelection = ({
setSynthesisLanguageOptions(langOpts);
// Default setting
if (synthVendor === VENDOR_GOOGLE && synthLang === LANG_EN_US) {
setSynthVoice(LANG_EN_US_STANDARD_C);
const googleLang = json.tts.find((lang) => lang.value === synthLang);
if (
synthVendor === VENDOR_GOOGLE &&
(!googleLang ||
!googleLang.voices.find((v) => v.value === synthVoice))
) {
setSynthLang(LANG_EN_US);
updateTtsVoice(LANG_EN_US_STANDARD_C);
return;
}
if (synthVendor === VENDOR_ELEVENLABS) {
// Samve Voices applied to all languages
// Voices are only available for the 1st language.
setSynthLang(ELEVENLABS_LANG_EN);
const newLang = json.tts.find(
(lang) => lang.value === ELEVENLABS_LANG_EN
);
if (newLang) {
setSynthVoice(newLang.voices[0].value);
}
updateTtsVoice(json.tts[0].voices[0].value);
return;
}
if (synthVendor === VENDOR_WHISPER) {
const newLang = json.tts.find((lang) => lang.value === LANG_EN_US);
setSynthLang(LANG_EN_US);
setSynthVoice(newLang!.voices[0].value);
updateTtsVoice(newLang!.voices[0].value);
return;
}
/** Google and AWS have different language lists */
@@ -246,28 +266,57 @@ export const SpeechProviderSelection = ({
let newLang = json.tts.find((lang) => lang.value === synthLang);
if (newLang) {
setSynthVoice(newLang.voices[0].value);
updateTtsVoice(newLang.voices[0].value);
return;
}
newLang = json.tts.find((lang) => lang.value === LANG_EN_US);
setSynthLang(LANG_EN_US);
setSynthVoice(newLang!.voices[0].value);
updateTtsVoice(newLang!.voices[0].value);
}
})
.catch((error) => {
toastError(error.msg);
});
if (synthVendor === VENDOR_GOOGLE) {
getGoogleCustomVoices({
...(synthLabel && { label: synthLabel }),
account_sid: accountSid,
service_provider_sid: serviceProviderSid,
}).then(({ json }) => {
// If after successfully fetching data, vendor is still good, then apply value
if (currentTtsVendor.current !== VENDOR_GOOGLE) {
return;
}
const customVOices = json.map((v) => ({
name: `${v.name} (Custom)`,
value: `custom_${v.google_custom_voice_sid}`,
}));
setSynthesisGoogleCustomVoiceOptions(customVOices);
});
}
};
const updateTtsVoice = (value: string) => {
if (shouldUpdateTtsVoice.current) {
setSynthVoice(value);
shouldUpdateTtsVoice.current = false;
}
};
const configRecognizer = () => {
getSpeechSupportedLanguagesAndVoices(
currentServiceProvider?.service_provider_sid,
serviceProviderSid,
recogVendor,
recogLabel
)
.then(({ json }) => {
// while fetching data, the user might change the vendor
if (currentSttVendor.current !== recogVendor) {
return;
}
// Extract Language
const langOpts = json.stt.map((lang) => ({
name: lang.name,
@@ -276,8 +325,12 @@ export const SpeechProviderSelection = ({
setRecogLanguageOptions(langOpts);
/**When vendor is custom, Language is input by user */
if (recogVendor.toString() === VENDOR_CUSTOM) return;
if (
recogVendor.toString() === VENDOR_CUSTOM ||
!shouldUpdateSttLanguage.current
)
return;
shouldUpdateSttLanguage.current = false;
/** Google and AWS have different language lists */
/** If the new language doesn't map then default to "en-US" */
const newLang = json.stt.find((lang) => lang.value === recogLang);
@@ -287,10 +340,10 @@ export const SpeechProviderSelection = ({
!newLang
) {
setRecogLang(LANG_EN_US);
}
// Default colbalt language
if (recogVendor === VENDOR_COBALT) {
} else if (recogVendor === VENDOR_COBALT && !newLang) {
setRecogLang(LANG_COBALT_EN_US);
} else if (langOpts.length && !newLang) {
setRecogLang(langOpts[0].value);
}
})
.catch((error) => {
@@ -314,6 +367,7 @@ export const SpeechProviderSelection = ({
)}
onChange={(e) => {
const vendor = e.target.value as keyof SynthesisVendors;
shouldUpdateTtsVoice.current = true;
setSynthVendor(vendor);
setSynthLabel("");
setSynthesisLanguageOptions([]);
@@ -358,6 +412,7 @@ export const SpeechProviderSelection = ({
value={synthLang}
options={synthesisLanguageOptions}
onChange={(e) => {
shouldUpdateTtsVoice.current = true;
const language = e.target.value;
setSynthLang(language);
@@ -461,6 +516,7 @@ export const SpeechProviderSelection = ({
)}
onChange={(e) => {
const vendor = e.target.value as keyof RecognizerVendors;
shouldUpdateSttLanguage.current = true;
setRecogVendor(vendor);
setRecogLabel("");

View File

@@ -13,6 +13,7 @@ import {
JaegerRoot,
JaegerSpan,
WaveSurferDtmfResult,
WaveSurferGatherSpeechVerbHookLatencyResult,
WaveSurferSttResult,
WaveSurferTtsLatencyResult,
} from "src/api/jaeger-types";
@@ -45,6 +46,11 @@ export const Player = ({ call }: PlayerProps) => {
const [waveSurferTtsLatencyData, setWaveSurferTtsLatencyData] =
useState<WaveSurferTtsLatencyResult | null>();
const [
waveSurferGatherSpeechVerbHookLatencyData,
setWaveSurferGatherSpeechVerbHookLatencyData,
] = useState<WaveSurferGatherSpeechVerbHookLatencyResult | null>();
const [regionChecked, setRegionChecked] = useState(false);
const wavesurferId = `wavesurfer--${call_sid}`;
@@ -199,7 +205,7 @@ export const Player = ({ call }: PlayerProps) => {
color: "rgba(255, 255, 0, 0.55)",
drag: false,
resize: false,
content: `${(end - endSpeechTime).toFixed(2)} sec`,
content: `${(end - endSpeechTime).toFixed(2)}s`,
});
changeRegionMouseStyle(latencyRegion, channel);
@@ -253,11 +259,18 @@ export const Player = ({ call }: PlayerProps) => {
if (!r) {
const start =
(s.startTimeUnixNano - startPoint.startTimeUnixNano) / 1_000_000_000;
const end =
let end =
(s.endTimeUnixNano - startPoint.startTimeUnixNano) / 1_000_000_000;
const [ttsVendor] = getSpanAttributeByName(s.attributes, "tts.vendor");
const [ttsCache] = getSpanAttributeByName(s.attributes, "tts.cached");
const [streamLatency] = getSpanAttributeByName(
s.attributes,
"time_to_first_byte_ms"
);
if (streamLatency && streamLatency.value.stringValue) {
end = start + Number(streamLatency.value.stringValue) / 1_000;
}
if (ttsVendor && ttsCache && !Boolean(ttsCache.value.boolValue)) {
const latencyRegion = waveSurferRegionsPluginRef.current.addRegion({
id: s.spanId,
@@ -266,16 +279,15 @@ export const Player = ({ call }: PlayerProps) => {
color: "rgba(255, 155, 0, 0.55)",
drag: false,
resize: false,
content: `${(end - start).toFixed(2)} sec`,
content: createMultiLineTextElement(`${(end - start).toFixed(2)}s`),
});
changeRegionMouseStyle(latencyRegion, 2);
console.log(ttsCache.value.boolValue);
changeRegionMouseStyle(latencyRegion, 1);
latencyRegion.on("click", () => {
setWaveSurferTtsLatencyData({
vendor: ttsVendor.value.stringValue,
latency: `${(end - start).toFixed(2)} sec`,
latency: `${(end - start).toFixed(2)}s`,
isCached: String(ttsCache.value.boolValue),
});
});
@@ -284,6 +296,53 @@ export const Player = ({ call }: PlayerProps) => {
}
};
const drawVerbHookDelayRegion = (s: JaegerSpan, startPoint: JaegerSpan) => {
if (waveSurferRegionsPluginRef.current) {
const r = waveSurferRegionsPluginRef.current
.getRegions()
.find((r) => r.id === s.spanId);
if (!r) {
const start =
(s.startTimeUnixNano - startPoint.startTimeUnixNano) / 1_000_000_000;
const end =
(s.endTimeUnixNano - startPoint.startTimeUnixNano) / 1_000_000_000;
const tmpEnd = end - start < 0.05 ? start + 0.05 : end;
const latencyRegion = waveSurferRegionsPluginRef.current.addRegion({
id: s.spanId,
start: start,
end: tmpEnd,
color: "rgba(255, 3, 180, 0.55)",
drag: false,
resize: false,
content: createMultiLineTextElement(`${(end - start).toFixed(2)}s`),
});
const [statusCode] = getSpanAttributeByName(
s.attributes,
"http.statusCode"
);
changeRegionMouseStyle(latencyRegion, 0);
latencyRegion.on("click", () => {
setWaveSurferGatherSpeechVerbHookLatencyData({
statusCode: statusCode ? Number(statusCode.value.doubleValue) : 404,
latency: `${(end - start).toFixed(2)}s`,
});
});
}
}
};
function createMultiLineTextElement(text: string) {
const div = document.createElement("div");
div.style.paddingLeft = "10px";
div.style.paddingTop = "15px";
div.appendChild(document.createElement("br"));
div.appendChild(document.createTextNode(text));
return div;
}
const buildWavesurferRegion = () => {
if (jaegerRoot) {
const spans = getSpansFromJaegerRoot(jaegerRoot);
@@ -296,6 +355,7 @@ export const Player = ({ call }: PlayerProps) => {
drawSttRegionForSpan(s, startPoint);
});
// Trasscription
const transcribeSpans = getSpansByNameRegex(spans, /stt-listen:/);
transcribeSpans.forEach((cs) => {
// Channel start from 0
@@ -306,16 +366,35 @@ export const Player = ({ call }: PlayerProps) => {
channel > 0 ? channel - 1 : channel
);
});
// DTMF
const dtmfSpans = getSpansByNameRegex(spans, /dtmf:/);
dtmfSpans.forEach((ds) => {
drawDtmfRegionForSpan(ds, startPoint);
});
// TTS delay
const ttsSpans = getSpansByNameRegex(spans, /tts-generation/);
ttsSpans.forEach((tts) => {
drawTtsLatencyRegion(tts, startPoint);
});
// Gather verb hook delay
const verbHookSpans = getSpansByNameRegex(spans, /verb:hook/);
verbHookSpans
.filter((s) => {
const [httpBody] = getSpanAttributeByName(
s.attributes,
"http.body"
);
return (
httpBody.value.stringValue.includes(
'"reason":"speechDetected"'
) ||
httpBody.value.stringValue.includes('"reason":"dtmfDetected"')
);
})
.forEach((s) => {
drawVerbHookDelayRegion(s, startPoint);
});
}
}
};
@@ -535,7 +614,7 @@ export const Player = ({ call }: PlayerProps) => {
}
}}
/>
<div>Overlay STT, TTS latency and DTMF events</div>
<div>Show latencies</div>
</label>
</div>
{waveSurferRegionData && (
@@ -670,6 +749,38 @@ export const Player = ({ call }: PlayerProps) => {
</div>
</ModalClose>
)}
{waveSurferGatherSpeechVerbHookLatencyData && (
<ModalClose
handleClose={() => setWaveSurferGatherSpeechVerbHookLatencyData(null)}
>
<div className="spanDetailsWrapper__header">
<P>
<strong>Application Response Latency</strong>
</P>
</div>
<div className="spanDetailsWrapper">
<div className="spanDetailsWrapper__detailsWrapper">
<div className="spanDetailsWrapper__details">
<div className="spanDetailsWrapper__details_header">
<strong>Status Code:</strong>
</div>
<div className="spanDetailsWrapper__details_body">
{waveSurferGatherSpeechVerbHookLatencyData.statusCode}
</div>
</div>
<div className="spanDetailsWrapper__details">
<div className="spanDetailsWrapper__details_header">
<strong>Latency:</strong>
</div>
<div className="spanDetailsWrapper__details_body">
{waveSurferGatherSpeechVerbHookLatencyData.latency}
</div>
</div>
</div>
</div>
</ModalClose>
)}
{deleteRecordUrl && (
<Modal
handleCancel={() => setDeleteRecordUrl("")}

View File

@@ -48,6 +48,7 @@ import {
isUserAccountScope,
isNotBlank,
hasLength,
hasValue,
} from "src/utils";
import { getObscuredGoogleServiceKey } from "./utils";
import { CredentialStatus } from "./status";
@@ -144,6 +145,13 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
const [optionsInitialChecked, setOptionsInitialChecked] = useState(false);
const [options, setOptions] = useState("");
const [tmpOptions, setTmpOptions] = useState("");
const [deepgramSttUri, setDeepgramSttUri] = useState("");
const [tmpDeepgramSttUri, setTmpDeepgramSttUri] = useState("");
const [deepgramSttUseTls, setDeepgramSttUseTls] = useState(false);
const [tmpDeepgramSttUseTls, setTmpDeepgramSttUseTls] = useState(false);
const [initialDeepgramOnpremCheck, setInitialDeepgramOnpremCheck] =
useState(false);
const [isDeepgramOnpremEnabled, setIsDeepgramOnpremEnabled] = useState(false);
const handleFile = (file: File) => {
const handleError = () => {
@@ -292,6 +300,10 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
...(vendor === VENDOR_ELEVENLABS && {
options: options || null,
}),
...(vendor === VENDOR_DEEPGRAM && {
deepgram_stt_uri: deepgramSttUri || null,
deepgram_stt_use_tls: deepgramSttUseTls ? 1 : 0,
}),
};
if (credential && credential.data) {
@@ -544,6 +556,16 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
setUseCustomVoicesCheck(json.length > 0);
});
}
if (credential?.data?.deepgram_stt_uri) {
setDeepgramSttUri(credential.data.deepgram_stt_uri);
}
if (credential?.data?.deepgram_stt_use_tls) {
setDeepgramSttUseTls(
credential?.data?.deepgram_stt_use_tls > 0 ? true : false
);
}
setInitialDeepgramOnpremCheck(hasValue(credential?.data?.deepgram_stt_uri));
setIsDeepgramOnpremEnabled(hasValue(credential?.data?.deepgram_stt_uri));
}, [credential]);
const updateCustomVoices = (
@@ -1112,7 +1134,6 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
</fieldset>
)}
{(vendor === VENDOR_WELLSAID ||
vendor === VENDOR_DEEPGRAM ||
vendor === VENDOR_ASSEMBLYAI ||
vendor == VENDOR_ELEVENLABS ||
vendor === VENDOR_WHISPER ||
@@ -1132,6 +1153,22 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
/>
</fieldset>
)}
{vendor === VENDOR_DEEPGRAM && (
<fieldset>
<label htmlFor={`${vendor}_apikey`}>
API key{!isDeepgramOnpremEnabled && <span>*</span>}
</label>
<Passwd
id={`${vendor}_apikey`}
required={!isDeepgramOnpremEnabled}
name={`${vendor}_apikey`}
placeholder="API key"
value={apiKey ? getObscuredSecret(apiKey) : apiKey}
onChange={(e) => setApiKey(e.target.value)}
disabled={credential ? true : false}
/>
</fieldset>
)}
{(vendor == VENDOR_ELEVENLABS || vendor == VENDOR_WHISPER) &&
ttsModels.length > 0 && (
<fieldset>
@@ -1324,6 +1361,57 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
/>
</fieldset>
)}
{vendor === VENDOR_DEEPGRAM && (
<fieldset>
<Checkzone
disabled={hasValue(credential)}
hidden
name="use_hosted_deepgram_service"
label="Use on-prem Deepgram container"
initialCheck={initialDeepgramOnpremCheck}
handleChecked={(e) => {
// setInitialDeepgramOnpremCheck(!e.target.checked);
setIsDeepgramOnpremEnabled(e.target.checked);
if (e.target.checked) {
if (tmpDeepgramSttUri) {
setDeepgramSttUri(tmpDeepgramSttUri);
}
if (tmpDeepgramSttUseTls) {
setDeepgramSttUseTls(tmpDeepgramSttUseTls);
}
} else {
setTmpDeepgramSttUri(deepgramSttUri);
setDeepgramSttUri("");
setTmpDeepgramSttUseTls(deepgramSttUseTls);
setDeepgramSttUseTls(false);
}
}}
>
<label htmlFor="deepgram_uri_for_tts">
Container URI<span>*</span>
</label>
<input
id="deepgram_uri_for_tts"
required
type="text"
name="deepgram_uri_for_tts"
placeholder="Container URI for TTS"
value={deepgramSttUri}
onChange={(e) => setDeepgramSttUri(e.target.value)}
/>
<label htmlFor="deepgram_tts_use_tls" className="chk">
<input
id="deepgram_tts_use_tls"
name="deepgram_tts_use_tls"
type="checkbox"
onChange={(e) => setDeepgramSttUseTls(e.target.checked)}
defaultChecked={deepgramSttUseTls}
/>
<div>Use TLS</div>
</label>
</Checkzone>
</fieldset>
)}
{vendor === VENDOR_MICROSOFT && (
<React.Fragment>
<fieldset>