Compare commits

..

7 Commits

Author SHA1 Message Date
Hoan Luu Huu
e425d825bc feat: custom Vendor (#215)
* feat: custom Vendor

* feat: custom Vendor

* fix: application with custom tts/stt vendor

* fix custom speech name when editing

* fix: all comments

* fix: remove custom in the list and show extra (custom)

* fix: prettier and application sythesizer selector

* fix: addd VITE_DISABLE_CUSTOM_SPEECH

---------

Co-authored-by: Quan HL <quanluuhoang8@gmail.com>
2023-03-14 08:45:06 -04:00
EgleH
a8d28da221 add soniox as speech provider (#211)
Co-authored-by: EgleHelms <e.helms@cognigy.com>
2023-03-03 18:53:46 -05:00
EgleH
e3855e83f7 conditional required causing issue with focusable fields (#210)
Co-authored-by: EgleHelms <e.helms@cognigy.com>
2023-02-23 12:18:01 -05:00
Dave Horton
446b6e76e2 update dockerfile 2023-02-23 08:21:09 -05:00
EgleH
0b55cdcf85 add env VITE_APP_DISABLE_DEFAULT_TRUNK_ROUTING (#209)
Co-authored-by: EgleHelms <e.helms@cognigy.com>
2023-02-22 14:05:56 -05:00
Snyk bot
f1743a9129 fix: Dockerfile to reduce vulnerabilities (#208)
The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-ALPINE316-OPENSSL-3314624
- https://snyk.io/vuln/SNYK-ALPINE316-OPENSSL-3314641
- https://snyk.io/vuln/SNYK-ALPINE316-OPENSSL-3314641
- https://snyk.io/vuln/SNYK-ALPINE316-OPENSSL-3314643
- https://snyk.io/vuln/SNYK-ALPINE316-OPENSSL-3314643
2023-02-22 07:32:42 -05:00
EgleH
ec46121696 upgrade node image (#206)
Co-authored-by: EgleHelms <e.helms@cognigy.com>
2023-02-20 10:31:07 -05:00
13 changed files with 403 additions and 117 deletions

5
.env
View File

@@ -2,4 +2,7 @@ VITE_API_BASE_URL=http://127.0.0.1:3000/v1
VITE_DEV_BASE_URL=http://127.0.0.1:3000/v1
## enables choosing units and lisenced account call limits
# VITE_APP_ENABLE_ACCOUNT_LIMITS_ALL=true
# VITE_APP_ENABLE_ACCOUNT_LIMITS_ALL=true
# disables controls for default application routing to carrier for SP and account level users
#VITE_APP_DISABLE_DEFAULT_TRUNK_ROUTING=true

View File

@@ -1,4 +1,4 @@
FROM node:18.14.0-alpine3.16 as builder
FROM node:18.14.1-alpine3.16 as builder
RUN apk update && apk add --no-cache python3 make g++
COPY . /opt/app
WORKDIR /opt/app/
@@ -6,7 +6,7 @@ RUN npm install
RUN npm run build
RUN npm prune
FROM node:18.9.0-alpine as webapp
FROM node:18.14.1-alpine as webapp
RUN apk add curl
WORKDIR /opt/app
COPY . /opt/app

View File

@@ -28,6 +28,11 @@ export const API_BASE_URL =
/** Serves mock API responses from a local dev API server */
export const DEV_BASE_URL = import.meta.env.VITE_DEV_BASE_URL;
/** Disable custom speech vendor*/
export const DISABLE_CUSTOM_SPEECH: boolean = JSON.parse(
import.meta.env.VITE_DISABLE_CUSTOM_SPEECH || "false"
);
/** TCP Max Port */
export const TCP_MAX_PORT = 65535;

View File

@@ -309,6 +309,9 @@ export interface SpeechCredential {
stt_region: null | string;
instance_id: null | string;
riva_server_uri: null | string;
auth_token: null | string;
custom_stt_url: null | string;
custom_tts_url: null | string;
}
export interface Alert {

View File

@@ -418,11 +418,6 @@ export const AccountForm = ({ apps, limits, account }: AccountFormProps) => {
username: e.target.value,
});
}}
required={
webhook.stateVal.password && !webhook.stateVal.username
? true
: false
}
/>
<label htmlFor={`${webhook.prefix}_password`}>
Password
@@ -438,11 +433,6 @@ export const AccountForm = ({ apps, limits, account }: AccountFormProps) => {
password: e.target.value,
});
}}
required={
webhook.stateVal.username && !webhook.stateVal.password
? true
: false
}
/>
</Checkzone>
</div>

View File

@@ -20,6 +20,8 @@ import {
VENDOR_WELLSAID,
useSpeechVendors,
VENDOR_DEEPGRAM,
VENDOR_SONIOX,
VENDOR_CUSTOM,
} from "src/vendor";
import {
postApplication,
@@ -39,6 +41,7 @@ import type {
Voice,
VoiceLanguage,
Language,
VendorOptions,
} from "src/vendor/types";
import type {
@@ -47,9 +50,10 @@ import type {
Application,
WebhookMethod,
UseApiDataMap,
SpeechCredential,
} from "src/api/types";
import { MSG_REQUIRED_FIELDS, MSG_WEBHOOK_FIELDS } from "src/constants";
import { isUserAccountScope, useRedirect } from "src/utils";
import { hasLength, isUserAccountScope, useRedirect } from "src/utils";
import { setAccountFilter, setLocation } from "src/store/localStore";
type ApplicationFormProps = {
@@ -82,6 +86,10 @@ export const ApplicationForm = ({ application }: ApplicationFormProps) => {
useState<keyof RecognizerVendors>(VENDOR_GOOGLE);
const [recogLang, setRecogLang] = useState(LANG_EN_US);
const [message, setMessage] = useState("");
const [apiUrl, setApiUrl] = useState("");
const [credentials] = useApiData<SpeechCredential[]>(apiUrl);
const [softTtsVendor, setSoftTtsVendor] = useState<VendorOptions[]>(vendors);
const [softSttVendor, setSoftSttVendor] = useState<VendorOptions[]>(vendors);
/** This lets us map and render the same UI for each... */
const webhooks = [
@@ -185,6 +193,40 @@ export const ApplicationForm = ({ application }: ApplicationFormProps) => {
}
};
useEffect(() => {
if (credentials && hasLength(credentials)) {
const v = credentials
.filter((tv) => tv.vendor.startsWith(VENDOR_CUSTOM) && tv.use_for_tts)
.map((tv) =>
Object.assign({
name:
tv.vendor.substring(VENDOR_CUSTOM.length + 1) +
` (${VENDOR_CUSTOM})`,
value: tv.vendor,
})
);
setSoftTtsVendor(vendors.concat(v));
const v2 = credentials
.filter((tv) => tv.vendor.startsWith(VENDOR_CUSTOM) && tv.use_for_stt)
.map((tv) =>
Object.assign({
name:
tv.vendor.substring(VENDOR_CUSTOM.length + 1) +
` (${VENDOR_CUSTOM})`,
value: tv.vendor,
})
);
setSoftSttVendor(vendors.concat(v2));
}
}, [credentials]);
useEffect(() => {
if (accountSid) {
setApiUrl(`Accounts/${accountSid}/SpeechCredentials`);
}
}, [accountSid]);
useEffect(() => {
setLocation();
if (application && application.data) {
@@ -356,13 +398,6 @@ export const ApplicationForm = ({ application }: ApplicationFormProps) => {
username: e.target.value,
});
}}
required={
webhook.required &&
!webhook.stateVal.username &&
webhook.stateVal.password
? true
: false
}
/>
<label htmlFor={`${webhook.prefix}_password`}>Password</label>
<Passwd
@@ -376,13 +411,6 @@ export const ApplicationForm = ({ application }: ApplicationFormProps) => {
password: e.target.value,
});
}}
required={
webhook.required &&
webhook.stateVal.username &&
!webhook.stateVal.password
? true
: false
}
/>
</Checkzone>
</fieldset>
@@ -395,13 +423,22 @@ export const ApplicationForm = ({ application }: ApplicationFormProps) => {
id="synthesis_vendor"
name="synthesis_vendor"
value={synthVendor}
options={vendors.filter(
(vendor) => vendor.value != VENDOR_DEEPGRAM
options={softTtsVendor.filter(
(vendor) =>
vendor.value != VENDOR_DEEPGRAM &&
vendor.value != VENDOR_SONIOX &&
vendor.value !== VENDOR_CUSTOM
)}
onChange={(e) => {
const vendor = e.target.value as keyof SynthesisVendors;
setSynthVendor(vendor);
/** When Custom Vendor is used, user you have to input the lange and voice. */
if (vendor.toString().startsWith(VENDOR_CUSTOM)) {
setSynthVoice("");
return;
}
/** When using Google and en-US, ensure "Standard-C" is used as default */
if (
e.target.value === VENDOR_GOOGLE &&
@@ -430,55 +467,88 @@ export const ApplicationForm = ({ application }: ApplicationFormProps) => {
setSynthVoice(newLang!.voices[0].value);
}}
/>
{synthVendor && synthLang && (
<>
<label htmlFor="synthesis_lang">Language</label>
<Selector
id="synthesis_lang"
name="synthesis_lang"
value={synthLang}
options={synthesis[synthVendor as keyof SynthesisVendors].map(
(lang: VoiceLanguage) => ({
{synthVendor &&
!synthVendor.toString().startsWith(VENDOR_CUSTOM) &&
synthLang && (
<>
<label htmlFor="synthesis_lang">Language</label>
<Selector
id="synthesis_lang"
name="synthesis_lang"
value={synthLang}
options={synthesis[
synthVendor as keyof SynthesisVendors
].map((lang: VoiceLanguage) => ({
name: lang.name,
value: lang.code,
})
)}
onChange={(e) => {
const language = e.target.value;
setSynthLang(language);
}))}
onChange={(e) => {
const language = e.target.value;
setSynthLang(language);
/** When using Google and en-US, ensure "Standard-C" is used as default */
if (
synthVendor === VENDOR_GOOGLE &&
language === LANG_EN_US
) {
setSynthVoice(LANG_EN_US_STANDARD_C);
return;
/** When using Google and en-US, ensure "Standard-C" is used as default */
if (
synthVendor === VENDOR_GOOGLE &&
language === LANG_EN_US
) {
setSynthVoice(LANG_EN_US_STANDARD_C);
return;
}
const newLang = synthesis[
synthVendor as keyof SynthesisVendors
].find((lang) => lang.code === language);
setSynthVoice(newLang!.voices[0].value);
}}
/>
<label htmlFor="synthesis_voice">Voice</label>
<Selector
id="synthesis_voice"
name="synthesis_voice"
value={synthVoice}
options={
synthesis[synthVendor as keyof SynthesisVendors]
.filter(
(lang: VoiceLanguage) => lang.code === synthLang
)
.flatMap((lang: VoiceLanguage) =>
lang.voices.map((voice: Voice) => ({
name: voice.name,
value: voice.value,
}))
) as Voice[]
}
const newLang = synthesis[
synthVendor as keyof SynthesisVendors
].find((lang) => lang.code === language);
setSynthVoice(newLang!.voices[0].value);
onChange={(e) => setSynthVoice(e.target.value)}
/>
</>
)}
{synthVendor.toString().startsWith(VENDOR_CUSTOM) && (
<>
<label htmlFor="custom_vendor_synthesis_lang">Language</label>
<input
id="custom_vendor_synthesis_lang"
type="text"
name="custom_vendor_synthesis_lang"
placeholder="Required"
required
value={synthLang}
onChange={(e) => {
setSynthLang(e.target.value);
}}
/>
<label htmlFor="synthesis_voice">Voice</label>
<Selector
id="synthesis_voice"
name="synthesis_voice"
<label htmlFor="custom_vendor_synthesis_voice">Voice</label>
<input
id="custom_vendor_synthesis_voice"
type="text"
name="custom_vendor_synthesis_voice"
placeholder="Required"
required
value={synthVoice}
options={
synthesis[synthVendor as keyof SynthesisVendors]
.filter((lang: VoiceLanguage) => lang.code === synthLang)
.flatMap((lang: VoiceLanguage) =>
lang.voices.map((voice: Voice) => ({
name: voice.name,
value: voice.value,
}))
) as Voice[]
}
onChange={(e) => setSynthVoice(e.target.value)}
onChange={(e) => {
setSynthVoice(e.target.value);
}}
/>
</>
)}
@@ -491,13 +561,18 @@ export const ApplicationForm = ({ application }: ApplicationFormProps) => {
id="recognizer_vendor"
name="recognizer_vendor"
value={recogVendor}
options={vendors.filter(
(vendor) => vendor.value != VENDOR_WELLSAID
options={softSttVendor.filter(
(vendor) =>
vendor.value != VENDOR_WELLSAID &&
vendor.value !== VENDOR_CUSTOM
)}
onChange={(e) => {
const vendor = e.target.value as keyof RecognizerVendors;
setRecogVendor(vendor);
/**When vendor is custom, Language is input by user */
if (vendor.toString() === VENDOR_CUSTOM) return;
/** Google and AWS have different language lists */
/** If the new language doesn't map then default to "en-US" */
const newLang = recognizers[vendor].find(
@@ -512,19 +587,37 @@ export const ApplicationForm = ({ application }: ApplicationFormProps) => {
}
}}
/>
{recogVendor && recogLang && (
{recogVendor &&
!recogVendor.toString().startsWith(VENDOR_CUSTOM) &&
recogLang && (
<>
<label htmlFor="recognizer_lang">Language</label>
<Selector
id="recognizer_lang"
name="recognizer_lang"
value={recogLang}
options={recognizers[
recogVendor as keyof RecognizerVendors
].map((lang: Language) => ({
name: lang.name,
value: lang.code,
}))}
onChange={(e) => {
setRecogLang(e.target.value);
}}
/>
</>
)}
{recogVendor.toString().startsWith(VENDOR_CUSTOM) && (
<>
<label htmlFor="recognizer_lang">Language</label>
<Selector
id="recognizer_lang"
name="recognizer_lang"
<label htmlFor="custom_vendor_recognizer_voice">Language</label>
<input
id="custom_vendor_recognizer_voice"
type="text"
name="custom_vendor_recognizer_voice"
placeholder="Required"
required
value={recogLang}
options={recognizers[
recogVendor as keyof RecognizerVendors
].map((lang: Language) => ({
name: lang.name,
value: lang.code,
}))}
onChange={(e) => {
setRecogLang(e.target.value);
}}

View File

@@ -45,6 +45,7 @@ import {
isUserAccountScope,
hasLength,
isValidPort,
disableDefaultTrunkRouting,
} from "src/utils";
import type {
@@ -725,18 +726,21 @@ export const CarrierForm = ({
: false
}
/>
{accountSid && hasLength(applications) && (
<>
<ApplicationSelect
label="Default Application"
defaultOption="None"
application={[applicationSid, setApplicationSid]}
applications={applications.filter(
(application) => application.account_sid === accountSid
)}
/>
</>
)}
{user &&
disableDefaultTrunkRouting(user?.scope) &&
accountSid &&
hasLength(applications) && (
<>
<ApplicationSelect
label="Default Application"
defaultOption="None"
application={[applicationSid, setApplicationSid]}
applications={applications.filter(
(application) => application.account_sid === accountSid
)}
/>
</>
)}
</fieldset>
<fieldset>
<Checkzone

View File

@@ -1,4 +1,4 @@
import React, { useEffect, useState } from "react";
import React, { Fragment, useEffect, useState } from "react";
import { Button, ButtonGroup, MS } from "@jambonz/ui-kit";
import { Link, useNavigate } from "react-router-dom";
@@ -9,6 +9,7 @@ import {
Selector,
Passwd,
AccountSelect,
Checkzone,
} from "src/components/forms";
import { toastError, toastSuccess, useSelectState } from "src/store";
import {
@@ -27,6 +28,8 @@ import {
VENDOR_DEEPGRAM,
VENDOR_IBM,
VENDOR_NVIDIA,
VENDOR_SONIOX,
VENDOR_CUSTOM,
} from "src/vendor";
import { MSG_REQUIRED_FIELDS } from "src/constants";
import {
@@ -40,6 +43,7 @@ import { CredentialStatus } from "./status";
import type { RegionVendors, GoogleServiceKey, Vendor } from "src/vendor/types";
import type { Account, SpeechCredential, UseApiDataMap } from "src/api/types";
import { setAccountFilter, setLocation } from "src/store/localStore";
import { DISABLE_CUSTOM_SPEECH } from "src/api/constants";
type SpeechServiceFormProps = {
credential?: UseApiDataMap<SpeechCredential>;
@@ -52,7 +56,9 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
const regions = useRegionVendors();
const [accounts] = useServiceProviderData<Account[]>("Accounts");
const [accountSid, setAccountSid] = useState("");
const [initialTtsCheck, setInitialTtsCheck] = useState(false);
const [ttsCheck, setTtsCheck] = useState(false);
const [initialSttCheck, setInitialSttCheck] = useState(false);
const [sttCheck, setSttCheck] = useState(false);
const [vendor, setVendor] = useState<Lowercase<Vendor>>(
"" as Lowercase<Vendor>
@@ -77,6 +83,12 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
const [customSttEndpoint, setCustomSttEndpoint] = useState("");
const [tmpCustomSttEndpoint, setTmpCustomSttEndpoint] = useState("");
const [rivaServerUri, setRivaServerUri] = useState("");
const [customVendorName, setCustomVendorName] = useState("");
const [customVendorAuthToken, setCustomVendorAuthToken] = useState("");
const [tmpCustomVendorTtsUrl, setTmpCustomVendorTtsUrl] = useState("");
const [customVendorTtsUrl, setCustomVendorTtsUrl] = useState("");
const [tmpCustomVendorSttUrl, setTmpCustomVendorSttUrl] = useState("");
const [customVendorSttUrl, setCustomVendorSttUrl] = useState("");
const handleFile = (file: File) => {
const handleError = () => {
@@ -141,6 +153,14 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
...(vendor === VENDOR_NVIDIA && {
riva_server_uri: rivaServerUri || null,
}),
...(vendor === VENDOR_CUSTOM && {
vendor: (vendor + ":" + customVendorName) as Lowercase<Vendor>,
use_for_tts: ttsCheck ? 1 : 0,
use_for_stt: sttCheck ? 1 : 0,
custom_tts_url: customVendorTtsUrl || null,
custom_stt_url: customVendorSttUrl || null,
auth_token: customVendorAuthToken || null,
}),
};
if (credential && credential.data) {
@@ -173,7 +193,8 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
api_key:
vendor === VENDOR_MICROSOFT ||
vendor === VENDOR_WELLSAID ||
vendor === VENDOR_DEEPGRAM
vendor === VENDOR_DEEPGRAM ||
vendor === VENDOR_SONIOX
? apiKey
: null,
client_id: vendor === VENDOR_NUANCE ? clientId : null,
@@ -196,7 +217,10 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
setLocation();
if (credential && credential.data) {
if (credential.data.vendor) {
setVendor(credential.data.vendor);
const v = credential.data.vendor.startsWith(VENDOR_CUSTOM)
? VENDOR_CUSTOM
: vendor;
setVendor(v);
}
if (credential.data.account_sid) {
@@ -205,14 +229,18 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
if (credential.data.use_for_stt) {
setSttCheck(true);
setInitialSttCheck(true);
} else {
setSttCheck(false);
setInitialSttCheck(false);
}
if (credential.data.use_for_tts) {
setTtsCheck(true);
setInitialTtsCheck(true);
} else {
setTtsCheck(false);
setInitialTtsCheck(false);
}
if (credential.data.service_key) {
@@ -277,6 +305,16 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
setCustomSttEndpoint(credential.data.custom_stt_endpoint || "");
setTmpCustomTtsEndpoint(credential.data.custom_tts_endpoint || "");
setTmpCustomSttEndpoint(credential.data.custom_stt_endpoint || "");
setCustomVendorName(
credential.data.vendor.startsWith(VENDOR_CUSTOM)
? credential.data.vendor.substring(VENDOR_CUSTOM.length + 1)
: credential.data.vendor
);
setCustomVendorAuthToken(credential.data.auth_token || "");
setCustomVendorSttUrl(credential.data.custom_stt_url || "");
setTmpCustomVendorSttUrl(credential.data.custom_stt_url || "");
setCustomVendorTtsUrl(credential.data.custom_tts_url || "");
setTmpCustomVendorTtsUrl(credential.data.custom_tts_url || "");
}
}, [credential]);
@@ -305,7 +343,11 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
name: "Select a vendor",
value: "",
},
].concat(vendors)}
]
.concat(vendors)
.filter(
(v) => !DISABLE_CUSTOM_SPEECH || v.value !== VENDOR_CUSTOM
)}
onChange={(e) => {
setVendor(e.target.value as Lowercase<Vendor>);
setRegion("");
@@ -315,6 +357,23 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
disabled={credential ? true : false}
required
/>
{vendor === VENDOR_CUSTOM && (
<>
<label htmlFor="custom_vendor_name">
Name<span>*</span>
</label>
<input
id="custom_vendor_name"
required
type="text"
name="custom_vendor_name"
placeholder="Vendor Name"
value={customVendorName}
onChange={(e) => setCustomVendorName(e.target.value)}
disabled={credential ? true : false}
/>
</>
)}
</fieldset>
<fieldset>
<AccountSelect
@@ -327,19 +386,21 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
</fieldset>
{vendor && (
<fieldset>
{vendor !== VENDOR_DEEPGRAM && (
<label htmlFor="use_for_tts" className="chk">
<input
id="use_for_tts"
name="use_for_tts"
type="checkbox"
onChange={(e) => setTtsCheck(e.target.checked)}
defaultChecked={ttsCheck}
/>
<div>Use for text-to-speech</div>
</label>
)}
{vendor !== VENDOR_WELLSAID && (
{vendor !== VENDOR_DEEPGRAM &&
vendor !== VENDOR_SONIOX &&
vendor != VENDOR_CUSTOM && (
<label htmlFor="use_for_tts" className="chk">
<input
id="use_for_tts"
name="use_for_tts"
type="checkbox"
onChange={(e) => setTtsCheck(e.target.checked)}
defaultChecked={ttsCheck}
/>
<div>Use for text-to-speech</div>
</label>
)}
{vendor !== VENDOR_WELLSAID && vendor !== VENDOR_CUSTOM && (
<label htmlFor="use_for_stt" className="chk">
<input
id="use_for_stt"
@@ -351,6 +412,87 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
<div>Use for speech-to-text</div>
</label>
)}
{vendor === VENDOR_CUSTOM && (
<Fragment>
<Checkzone
hidden
name="custom_vendor_use_for_tts"
label="Use for text-to-speech"
initialCheck={initialTtsCheck}
handleChecked={(e) => {
setTtsCheck(e.target.checked);
if (!e.target.checked) {
setTmpCustomVendorTtsUrl(customVendorTtsUrl);
setCustomVendorTtsUrl("");
} else {
setCustomVendorTtsUrl(tmpCustomVendorTtsUrl);
}
}}
>
<label htmlFor="custom_vendor_use_for_tts">
TTS HTTP URL<span>*</span>
</label>
<input
id="custom_vendor_use_for_tts"
type="text"
name="custom_vendor_use_for_tts"
placeholder="Required"
required={ttsCheck}
value={customVendorTtsUrl}
onChange={(e) => {
setCustomVendorTtsUrl(e.target.value);
}}
/>
</Checkzone>
<Checkzone
hidden
name="custom_vendor_use_for_stt"
label="Use for speech-to-text"
initialCheck={initialSttCheck}
handleChecked={(e) => {
setSttCheck(e.target.checked);
if (!e.target.checked) {
setTmpCustomVendorSttUrl(customVendorSttUrl);
setCustomVendorSttUrl("");
} else {
setCustomVendorSttUrl(tmpCustomVendorSttUrl);
}
}}
>
<label htmlFor="custom_vendor_use_for_stt">
STT websocket URL<span>*</span>
</label>
<input
id="custom_vendor_use_for_stt"
type="text"
name="custom_vendor_use_for_stt"
placeholder="Required"
required={sttCheck}
value={customVendorSttUrl}
onChange={(e) => {
setCustomVendorSttUrl(e.target.value);
}}
/>
</Checkzone>
</Fragment>
)}
</fieldset>
)}
{vendor === VENDOR_CUSTOM && (
<fieldset>
<label htmlFor="custom_vendor_auth_token">
Authentication Token
</label>
<input
id="custom_vendor_auth_token"
type="text"
name="custom_vendor_auth_token"
placeholder="Authentication Token"
value={customVendorAuthToken}
onChange={(e) => setCustomVendorAuthToken(e.target.value)}
disabled={credential ? true : false}
/>
</fieldset>
)}
{vendor === VENDOR_GOOGLE && (
@@ -448,7 +590,8 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
)}
{(vendor === VENDOR_MICROSOFT ||
vendor === VENDOR_WELLSAID ||
vendor === VENDOR_DEEPGRAM) && (
vendor === VENDOR_DEEPGRAM ||
vendor === VENDOR_SONIOX) && (
<fieldset>
<label htmlFor={`${vendor}_apikey`}>
API key<span>*</span>

View File

@@ -26,6 +26,7 @@ import type { SpeechCredential, Account } from "src/api/types";
import { ScopedAccess } from "src/components/scoped-access";
import { Scope } from "src/store/types";
import { getAccountFilter, setLocation } from "src/store/localStore";
import { VENDOR_CUSTOM } from "src/vendor";
export const SpeechServices = () => {
const user = useSelectState("user");
@@ -140,7 +141,14 @@ export const SpeechServices = () => {
title="Edit application"
className="i"
>
<strong>Vendor: {credential.vendor}</strong>
<strong>
Vendor:{" "}
{credential.vendor.startsWith(VENDOR_CUSTOM)
? credential.vendor.substring(
VENDOR_CUSTOM.length + 1
)
: credential.vendor}
</strong>
<Icons.ArrowRight />
</Link>
</ScopedAccess>

View File

@@ -225,6 +225,17 @@ export const createFilterString = (filterValue: string, label: string) => {
return filterString.join("/");
};
export const disableDefaultTrunkRouting = (userScope: UserData["scope"]) => {
if (import.meta.env.VITE_APP_DISABLE_DEFAULT_TRUNK_ROUTING) {
if (userScope === USER_ADMIN) {
return true;
} else {
return false;
}
}
return true;
};
export {
withSuspense,
useMobileMedia,

13
src/vendor/index.tsx vendored
View File

@@ -17,6 +17,8 @@ export const VENDOR_NUANCE = "nuance";
export const VENDOR_DEEPGRAM = "deepgram";
export const VENDOR_IBM = "ibm";
export const VENDOR_NVIDIA = "nvidia";
export const VENDOR_SONIOX = "soniox";
export const VENDOR_CUSTOM = "custom";
export const vendors: VendorOptions[] = [
{
@@ -51,6 +53,14 @@ export const vendors: VendorOptions[] = [
name: "WellSaid",
value: VENDOR_WELLSAID,
},
{
name: "Soniox",
value: VENDOR_SONIOX,
},
{
name: "Custom",
value: VENDOR_CUSTOM,
},
];
export const useRegionVendors = () => {
@@ -104,6 +114,7 @@ export const useSpeechVendors = () => {
import("./speech-recognizer/deepgram-speech-recognizer-lang"),
import("./speech-recognizer/ibm-speech-recognizer-lang"),
import("./speech-recognizer/nvidia-speech-recognizer-lang"),
import("./speech-recognizer/soniox-speech-recognizer-lang"),
import("./speech-synthesis/aws-speech-synthesis-lang"),
import("./speech-synthesis/google-speech-synthesis-lang"),
import("./speech-synthesis/ms-speech-synthesis-lang"),
@@ -120,6 +131,7 @@ export const useSpeechVendors = () => {
{ default: deepgramRecognizer },
{ default: ibmRecognizer },
{ default: nvidiaRecognizer },
{ default: sonioxRecognizer },
{ default: awsSynthesis },
{ default: googleSynthesis },
{ default: msSynthesis },
@@ -147,6 +159,7 @@ export const useSpeechVendors = () => {
deepgram: deepgramRecognizer,
ibm: ibmRecognizer,
nvidia: nvidiaRecognizer,
soniox: sonioxRecognizer,
},
});
}

View File

@@ -0,0 +1,10 @@
import type { Language } from "../types";
export const languages: Language[] = [
{
name: "English (United States)",
code: "en-US",
},
];
export default languages;

5
src/vendor/types.ts vendored
View File

@@ -6,7 +6,9 @@ export type Vendor =
| "Nuance"
| "Deepgram"
| "IBM"
| "Nvidia";
| "Nvidia"
| "Soniox"
| "Custom";
export interface VendorOptions {
name: Vendor;
@@ -62,6 +64,7 @@ export interface RecognizerVendors {
deepgram: Language[];
ibm: Language[];
nvidia: Language[];
soniox: Language[];
}
export interface SynthesisVendors {