mirror of
https://github.com/jambonz/jambonz-webapp.git
synced 2025-12-19 05:37:43 +00:00
Compare commits
33 Commits
v0.9.4-rc4
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c33eb46ce0 | ||
|
|
f003c158dc | ||
|
|
b1ddaf230d | ||
|
|
0260b1ec8b | ||
|
|
1c1f97f045 | ||
|
|
e6c5a18c87 | ||
|
|
19742ab67e | ||
|
|
53d0c0b510 | ||
|
|
7a0eb71bae | ||
|
|
6aae8d9930 | ||
|
|
a70a1bf614 | ||
|
|
975a787f1e | ||
|
|
46e220f28b | ||
|
|
6836a99635 | ||
|
|
f7f4a2e7b1 | ||
|
|
f1f8a7d808 | ||
|
|
9dd9cf867a | ||
|
|
a372c09bc6 | ||
|
|
031e5e923e | ||
|
|
e02904f7f3 | ||
|
|
7eaf25d13f | ||
|
|
6e4d663337 | ||
|
|
c0a40dd784 | ||
|
|
536bf0f471 | ||
|
|
aaf1ede5c2 | ||
|
|
24d646f705 | ||
|
|
c648afcb1a | ||
|
|
4eca59d9bd | ||
|
|
4a293ae7da | ||
|
|
03e52e3dc5 | ||
|
|
9ab592a898 | ||
|
|
1723326890 | ||
|
|
504825d699 |
2
.env
2
.env
@@ -1,4 +1,4 @@
|
||||
#VITE_API_BASE_URL=http://127.0.0.1:3000/v1
|
||||
# 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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM node:18.15-alpine3.16 as builder
|
||||
FROM node:20-alpine 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.14.1-alpine as webapp
|
||||
FROM node:20-alpine AS webapp
|
||||
RUN apk add curl
|
||||
WORKDIR /opt/app
|
||||
COPY . /opt/app
|
||||
|
||||
3113
package-lock.json
generated
3113
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
18
package.json
18
package.json
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "jambonz-webapp",
|
||||
"description": "A simple provisioning web app for jambonz",
|
||||
"version": "0.9.4",
|
||||
"version": "0.9.5",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"engines": {
|
||||
"node": ">=14.18"
|
||||
"node": ">=18"
|
||||
},
|
||||
"contributors": [
|
||||
{
|
||||
@@ -41,7 +41,7 @@
|
||||
"deploy": "npm i && npm run build && npm run pm2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@jambonz/ui-kit": "^0.0.21",
|
||||
"@jambonz/ui-kit": "^0.0.22",
|
||||
"@stripe/react-stripe-js": "^2.6.2",
|
||||
"@stripe/stripe-js": "^3.2.0",
|
||||
"dayjs": "^1.11.10",
|
||||
@@ -71,16 +71,16 @@
|
||||
"eslint-plugin-jsx-a11y": "^6.8.0",
|
||||
"eslint-plugin-react": "^7.34.1",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"express": "^4.19.2",
|
||||
"express": "^5.1.0",
|
||||
"husky": "^9.0.11",
|
||||
"lint-staged": "^15.2.2",
|
||||
"nanoid": "^5.0.7",
|
||||
"lint-staged": "^16.1.2",
|
||||
"nanoid": "^5.1.5",
|
||||
"prettier": "^3.2.5",
|
||||
"sass": "^1.74.1",
|
||||
"serve": "^14.2.1",
|
||||
"sass": "^1.89.2",
|
||||
"serve": "^14.2.5",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.4.4",
|
||||
"vite": "^5.2.8"
|
||||
"vite": "^6.4.1"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{ts,tsx}": "eslint --max-warnings=0",
|
||||
|
||||
@@ -3,6 +3,7 @@ import type {
|
||||
Currency,
|
||||
ElevenLabsOptions,
|
||||
GoogleCustomVoice,
|
||||
InworldOptions,
|
||||
LimitField,
|
||||
LimitUnitOption,
|
||||
PasswordSettings,
|
||||
@@ -130,7 +131,7 @@ export const DEFAULT_WEBHOOK: WebHook = {
|
||||
};
|
||||
|
||||
/** Default SIP/SMPP Gateways */
|
||||
export const DEFAULT_SIP_GATEWAY: SipGateway = {
|
||||
export const DEFAULT_SIP_INBOUND_GATEWAY: SipGateway = {
|
||||
voip_carrier_sid: "",
|
||||
ipv4: "",
|
||||
port: 5060,
|
||||
@@ -244,6 +245,14 @@ export const VERBIO_STT_MODELS = [
|
||||
|
||||
export const DEFAULT_VERBIO_MODEL = "V1";
|
||||
|
||||
// ASSEMBLYAI
|
||||
export const ASSEMBLYAI_STT_VERSIONS = [
|
||||
{ name: "V2", value: "v2" },
|
||||
{ name: "V3", value: "v3" },
|
||||
];
|
||||
|
||||
export const DEFAULT_ASSEMBLYAI_STT_VERSION = "v2";
|
||||
|
||||
export const ADDITIONAL_SPEECH_VENDORS: Lowercase<Vendor>[] = ["speechmatics"];
|
||||
|
||||
// Google Custom Voice reported usage options
|
||||
@@ -277,6 +286,14 @@ export const DEFAULT_RIMELABS_OPTIONS: Partial<RimelabsOptions> = {
|
||||
reduceLatency: true,
|
||||
};
|
||||
|
||||
export const DEFAULT_INWORLD_OPTIONS: Partial<InworldOptions> = {
|
||||
audioConfig: {
|
||||
pitch: 0.0,
|
||||
speakingRate: 1.0,
|
||||
},
|
||||
temperature: 0.8,
|
||||
};
|
||||
|
||||
// PlayHT options
|
||||
export const DEFAULT_PLAYHT_OPTIONS: Partial<PlayHTOptions> = {
|
||||
quality: "medium",
|
||||
@@ -331,6 +348,12 @@ export const DTMF_TYPE_SELECTION: SelectorOptions[] = [
|
||||
{ name: "Tones", value: "tones" },
|
||||
];
|
||||
|
||||
export const TRUNK_TYPE_SELECTION: SelectorOptions[] = [
|
||||
{ name: "IP Trunk", value: "static_ip" },
|
||||
{ name: "Auth Trunk", value: "auth" },
|
||||
{ name: "Registration Trunk", value: "reg" },
|
||||
];
|
||||
|
||||
/** Available webhook methods */
|
||||
export const WEBHOOK_METHODS: WebhookOption[] = [
|
||||
{
|
||||
@@ -401,6 +424,11 @@ export const CurrencySymbol: Currency = {
|
||||
usd: "$",
|
||||
};
|
||||
|
||||
export const DEEPGRAM_STT_ENPOINT = [
|
||||
{ name: "US (Default)", value: "" },
|
||||
{ name: "EU-hosted", value: "api.eu.deepgram.com" },
|
||||
];
|
||||
|
||||
/** User scope values values */
|
||||
export const USER_ADMIN = "admin";
|
||||
export const USER_SP = "service_provider";
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
import type { Language, Model, Vendor, VoiceLanguage } from "src/vendor/types";
|
||||
import type {
|
||||
JambonzResourceOptions,
|
||||
Language,
|
||||
Model,
|
||||
Vendor,
|
||||
VoiceLanguage,
|
||||
} from "src/vendor/types";
|
||||
|
||||
/** Simple types */
|
||||
|
||||
@@ -413,6 +419,7 @@ export interface SpeechCredential {
|
||||
custom_stt_endpoint: null | string;
|
||||
client_id: null | string;
|
||||
client_secret: null | string;
|
||||
client_key: null | string;
|
||||
secret: null | string;
|
||||
nuance_tts_uri: null | string;
|
||||
nuance_stt_uri: null | string;
|
||||
@@ -429,8 +436,10 @@ export interface SpeechCredential {
|
||||
label: null | string;
|
||||
cobalt_server_uri: null | string;
|
||||
model_id: null | string;
|
||||
stt_model_id: null | string;
|
||||
voice_engine: null | string;
|
||||
engine_version: null | string;
|
||||
service_version: null | string;
|
||||
model: null | string;
|
||||
options: null | string;
|
||||
deepgram_stt_uri: null | string;
|
||||
@@ -438,6 +447,10 @@ export interface SpeechCredential {
|
||||
deepgram_stt_use_tls: number;
|
||||
speechmatics_stt_uri: null | string;
|
||||
playht_tts_uri: null | string;
|
||||
resemble_tts_uri: null | string;
|
||||
resemble_tts_use_tls: number;
|
||||
api_uri: null | string;
|
||||
houndify_server_uri: null | string;
|
||||
}
|
||||
|
||||
export interface Alert {
|
||||
@@ -457,6 +470,8 @@ export interface CarrierRegisterStatus {
|
||||
|
||||
export type DtmfType = "rfc2833" | "tones" | "info";
|
||||
|
||||
export type TrunkType = "static_ip" | "auth" | "reg";
|
||||
|
||||
export interface Carrier {
|
||||
voip_carrier_sid: string;
|
||||
name: string;
|
||||
@@ -485,6 +500,7 @@ export interface Carrier {
|
||||
register_status: CarrierRegisterStatus;
|
||||
dtmf_type: DtmfType;
|
||||
outbound_sip_proxy: string | null;
|
||||
trunk_type: TrunkType;
|
||||
}
|
||||
|
||||
export interface PredefinedCarrier extends Carrier {
|
||||
@@ -780,6 +796,16 @@ export interface RimelabsOptions {
|
||||
reduceLatency: boolean;
|
||||
}
|
||||
|
||||
export interface InworldOptions {
|
||||
audioConfig: {
|
||||
bitRate?: number;
|
||||
sampleRateHertz?: number;
|
||||
pitch?: number;
|
||||
speakingRate?: number;
|
||||
};
|
||||
temperature?: number;
|
||||
}
|
||||
|
||||
export type CartesiaEmotions =
|
||||
| "anger:lowest"
|
||||
| "anger:low"
|
||||
@@ -810,6 +836,9 @@ export interface AppEnvProperty {
|
||||
default?: string | number | boolean;
|
||||
obscure?: boolean;
|
||||
uiHint?: "input" | "textarea" | "filepicker";
|
||||
enum?: string[];
|
||||
jambonzResource?: "carriers";
|
||||
jambonzResourceOptions?: JambonzResourceOptions[];
|
||||
}
|
||||
|
||||
export interface AppEnv {
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
}
|
||||
|
||||
select {
|
||||
@include ui-mixins.m();
|
||||
appearance: none;
|
||||
padding: ui-vars.$px01 ui-vars.$px02;
|
||||
border-radius: ui-vars.$px01;
|
||||
@@ -33,6 +32,7 @@
|
||||
background-color: ui-vars.$white;
|
||||
width: 100%;
|
||||
max-width: vars.$widthinput;
|
||||
@include ui-mixins.m();
|
||||
|
||||
&:focus {
|
||||
border-color: ui-vars.$dark;
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
}
|
||||
|
||||
@mixin typeahead-input {
|
||||
@include ui-mixins.m();
|
||||
appearance: none;
|
||||
padding: ui-vars.$px01 ui-vars.$px02;
|
||||
border-radius: ui-vars.$px01;
|
||||
@@ -37,6 +36,7 @@
|
||||
max-width: vars.$widthtypeaheadinput;
|
||||
transition: border-color 0.2s ease;
|
||||
font-family: inherit;
|
||||
@include ui-mixins.m();
|
||||
|
||||
&:focus {
|
||||
border-color: ui-vars.$dark;
|
||||
@@ -84,7 +84,6 @@
|
||||
}
|
||||
|
||||
@mixin typeahead-dropdown {
|
||||
@include ui-mixins.m();
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
@@ -93,6 +92,7 @@
|
||||
border: 1px solid ui-vars.$dark;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
@include ui-mixins.m();
|
||||
}
|
||||
|
||||
@mixin typeahead-option {
|
||||
@@ -126,8 +126,8 @@
|
||||
width: 100%;
|
||||
|
||||
input {
|
||||
@include typeahead-input();
|
||||
width: 100%;
|
||||
@include typeahead-input();
|
||||
}
|
||||
|
||||
span {
|
||||
@@ -135,8 +135,8 @@
|
||||
}
|
||||
|
||||
.typeahead-dropdown {
|
||||
@include typeahead-dropdown();
|
||||
z-index: 1000;
|
||||
@include typeahead-dropdown();
|
||||
}
|
||||
|
||||
.typeahead-option {
|
||||
@@ -149,10 +149,10 @@
|
||||
width: auto;
|
||||
|
||||
input {
|
||||
@include typeahead-input();
|
||||
height: 34px;
|
||||
min-width: 370px;
|
||||
font-size: var(--mxs-size);
|
||||
@include typeahead-input();
|
||||
}
|
||||
|
||||
span {
|
||||
@@ -160,13 +160,13 @@
|
||||
}
|
||||
|
||||
.typeahead-dropdown {
|
||||
@include typeahead-dropdown();
|
||||
width: 100%;
|
||||
@include typeahead-dropdown();
|
||||
}
|
||||
|
||||
.typeahead-option {
|
||||
@include typeahead-option();
|
||||
font-size: var(--mxs-size);
|
||||
@include typeahead-option();
|
||||
}
|
||||
|
||||
.pointerevents {
|
||||
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
useServiceProviderData,
|
||||
useApiData,
|
||||
getAppEnvSchema,
|
||||
getSPVoipCarriers,
|
||||
} from "src/api";
|
||||
import {
|
||||
ROUTE_INTERNAL_ACCOUNTS,
|
||||
@@ -53,7 +54,12 @@ import type {
|
||||
AppEnv,
|
||||
} from "src/api/types";
|
||||
import { MSG_REQUIRED_FIELDS, MSG_WEBHOOK_FIELDS } from "src/constants";
|
||||
import { hasLength, isUserAccountScope, useRedirect } from "src/utils";
|
||||
import {
|
||||
hasLength,
|
||||
hasValue,
|
||||
isUserAccountScope,
|
||||
useRedirect,
|
||||
} from "src/utils";
|
||||
import { setAccountFilter, setLocation } from "src/store/localStore";
|
||||
import SpeechProviderSelection from "./speech-selection";
|
||||
import ObscureInput from "src/components/obscure-input";
|
||||
@@ -583,6 +589,51 @@ export const ApplicationForm = ({ application }: ApplicationFormProps) => {
|
||||
setFallbackSpeechRecognizerLabel(tmp);
|
||||
};
|
||||
|
||||
const fetchAppEnvJambonzResources = async (appEnv: AppEnv) => {
|
||||
if (appEnv) {
|
||||
const promises = Object.entries(appEnv).map(async ([key, value]) => {
|
||||
const { jambonzResource } = value;
|
||||
switch (jambonzResource) {
|
||||
case "carriers":
|
||||
const carriers = await getSPVoipCarriers(
|
||||
currentServiceProvider?.service_provider_sid || "",
|
||||
{
|
||||
page: 1,
|
||||
page_size: 10000,
|
||||
...(user?.account_sid && {
|
||||
account_sid: user.account_sid,
|
||||
}),
|
||||
},
|
||||
);
|
||||
if (carriers.json.total) {
|
||||
return {
|
||||
key,
|
||||
jambonzResourceOptions: carriers.json.data.map((carrier) => ({
|
||||
name: carrier.name,
|
||||
value: carrier.name,
|
||||
})),
|
||||
};
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return { key, jambonzResourceOptions: null };
|
||||
});
|
||||
|
||||
const results = await Promise.all(promises);
|
||||
|
||||
// Merge the results back into appEnv
|
||||
results.forEach(({ key, jambonzResourceOptions }) => {
|
||||
if (jambonzResourceOptions) {
|
||||
appEnv[key].jambonzResourceOptions = jambonzResourceOptions;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return appEnv;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (callWebhook && callWebhook.url) {
|
||||
// Clear any existing timeout to prevent multiple requests
|
||||
@@ -594,7 +645,26 @@ export const ApplicationForm = ({ application }: ApplicationFormProps) => {
|
||||
appEnvTimeoutRef.current = setTimeout(() => {
|
||||
getAppEnvSchema(callWebhook.url)
|
||||
.then(({ json }) => {
|
||||
setAppEnv(json);
|
||||
// fetch app env jambonz_resource
|
||||
fetchAppEnvJambonzResources(json).then((updatedEnv) => {
|
||||
setAppEnv(updatedEnv);
|
||||
const defaultEnvVars = Object.keys(updatedEnv).reduce(
|
||||
(acc, key) => {
|
||||
const value = updatedEnv[key];
|
||||
if (value?.default) {
|
||||
return { ...acc, [key]: value.default };
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{},
|
||||
);
|
||||
|
||||
setEnvVars((prev) => ({
|
||||
...defaultEnvVars,
|
||||
...(prev || {}),
|
||||
}));
|
||||
});
|
||||
// Default value
|
||||
})
|
||||
.catch((error) => {
|
||||
setMessage(error.msg);
|
||||
@@ -828,7 +898,9 @@ export const ApplicationForm = ({ application }: ApplicationFormProps) => {
|
||||
? String(defaultValue)
|
||||
: "",
|
||||
onChange: (
|
||||
e: React.ChangeEvent<HTMLInputElement>,
|
||||
e: React.ChangeEvent<
|
||||
HTMLInputElement | HTMLSelectElement
|
||||
>,
|
||||
) => {
|
||||
// Convert to proper type based on schema
|
||||
let newValue;
|
||||
@@ -853,6 +925,15 @@ export const ApplicationForm = ({ application }: ApplicationFormProps) => {
|
||||
type: isNumber ? "number" : "text",
|
||||
};
|
||||
|
||||
const isDropdown =
|
||||
(webhook.webhookEnv![key].type === "string" &&
|
||||
(webhook.webhookEnv![key].enum?.length ||
|
||||
0) > 0) ||
|
||||
hasLength(
|
||||
webhook.webhookEnv![key]
|
||||
.jambonzResourceOptions,
|
||||
);
|
||||
|
||||
const textAreaSpecificProps = {
|
||||
rows: 6,
|
||||
cols: 61,
|
||||
@@ -863,6 +944,23 @@ export const ApplicationForm = ({ application }: ApplicationFormProps) => {
|
||||
.obscure
|
||||
? ObscureInput
|
||||
: webhook.webhookEnv![key].uiHint || "input";
|
||||
if (isDropdown) {
|
||||
const options =
|
||||
webhook.webhookEnv![key]
|
||||
.jambonzResourceOptions ||
|
||||
webhook.webhookEnv![key].enum!.map(
|
||||
(option) => ({
|
||||
name: option,
|
||||
value: option,
|
||||
}),
|
||||
);
|
||||
return (
|
||||
<Selector
|
||||
{...commonProps}
|
||||
options={options}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (componentType === "filepicker") {
|
||||
return (
|
||||
<>
|
||||
@@ -886,7 +984,8 @@ export const ApplicationForm = ({ application }: ApplicationFormProps) => {
|
||||
}}
|
||||
placeholder="Choose a file"
|
||||
required={
|
||||
webhook.webhookEnv![key].required
|
||||
webhook.webhookEnv![key].required &&
|
||||
!hasValue(envVars?.[key])
|
||||
}
|
||||
/>
|
||||
{React.createElement("textarea", {
|
||||
|
||||
@@ -35,6 +35,10 @@ import {
|
||||
VENDOR_VOXIST,
|
||||
VENDOR_RIMELABS,
|
||||
VENDOR_OPENAI,
|
||||
VENDOR_INWORLD,
|
||||
VENDOR_DEEPGRAM_FLUX,
|
||||
VENDOR_RESEMBLE,
|
||||
VENDOR_HOUNDIFY,
|
||||
} from "src/vendor";
|
||||
import {
|
||||
LabelOptions,
|
||||
@@ -139,7 +143,12 @@ export const SpeechProviderSelection = ({
|
||||
ttsEffectTimer.current = setTimeout(() => {
|
||||
configSynthesis();
|
||||
}, 200);
|
||||
}, [synthVendor, synthLabel, serviceProviderSid]);
|
||||
}, [
|
||||
synthVendor,
|
||||
synthLabel,
|
||||
serviceProviderSid,
|
||||
application_speech_synthesis_voice,
|
||||
]);
|
||||
|
||||
// Get Recognizer languages and voices
|
||||
useEffect(() => {
|
||||
@@ -246,6 +255,15 @@ export const SpeechProviderSelection = ({
|
||||
// Extract model
|
||||
if (json.models && json.models.length) {
|
||||
setSynthesisModelOptions(json.models);
|
||||
if (
|
||||
synthVendor === VENDOR_DEEPGRAM &&
|
||||
(!application_speech_synthesis_voice ||
|
||||
!json.models.some(
|
||||
(m) => m.value === application_speech_synthesis_voice,
|
||||
))
|
||||
) {
|
||||
setSynthVoice(json.models[0].value);
|
||||
}
|
||||
}
|
||||
|
||||
if (json.tts && json.tts.length) {
|
||||
@@ -298,6 +316,15 @@ export const SpeechProviderSelection = ({
|
||||
updateTtsVoice(newLang!.value, newLang!.voices[0].value);
|
||||
return;
|
||||
}
|
||||
if (synthVendor === VENDOR_INWORLD) {
|
||||
let newLang = json.tts.find((lang) => lang.value === "en");
|
||||
// If the new language doesn't map then default to the first one
|
||||
if (!newLang) {
|
||||
newLang = json.tts[0];
|
||||
}
|
||||
updateTtsVoice(newLang!.value, newLang!.voices[0].value);
|
||||
return;
|
||||
}
|
||||
/** Google and AWS have different language lists */
|
||||
/** If the new language doesn't map then default to "en-US" */
|
||||
let newLang = json.tts.find((lang) => lang.value === synthLang);
|
||||
@@ -337,7 +364,6 @@ export const SpeechProviderSelection = ({
|
||||
|
||||
const updateTtsVoice = (language: string, voice: string) => {
|
||||
if (shouldUpdateTtsVoice.current) {
|
||||
console.log("xhoaluu");
|
||||
setSynthLang(language);
|
||||
setSynthVoice(voice);
|
||||
shouldUpdateTtsVoice.current = false;
|
||||
@@ -345,6 +371,9 @@ export const SpeechProviderSelection = ({
|
||||
};
|
||||
|
||||
const configRecognizer = () => {
|
||||
if (recogVendor === VENDOR_DEEPGRAM_FLUX) {
|
||||
return;
|
||||
}
|
||||
getSpeechSupportedLanguagesAndVoices(
|
||||
serviceProviderSid,
|
||||
recogVendor,
|
||||
@@ -389,19 +418,6 @@ export const SpeechProviderSelection = ({
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
synthVendor === VENDOR_DEEPGRAM &&
|
||||
synthesisModelOptions.length > 0 &&
|
||||
!synthesisModelOptions.some(
|
||||
(m) => m.value === application_speech_synthesis_voice,
|
||||
)
|
||||
) {
|
||||
setSynthVoice(synthesisModelOptions[0].value);
|
||||
} else {
|
||||
setSynthVoice(application_speech_synthesis_voice || "");
|
||||
}
|
||||
}, [synthesisModelOptions, application_speech_synthesis_voice]);
|
||||
return (
|
||||
<>
|
||||
<fieldset>
|
||||
@@ -418,6 +434,8 @@ export const SpeechProviderSelection = ({
|
||||
vendor.value !== VENDOR_SPEECHMATICS &&
|
||||
vendor.value !== VENDOR_CUSTOM &&
|
||||
vendor.value !== VENDOR_OPENAI &&
|
||||
vendor.value !== VENDOR_DEEPGRAM_FLUX &&
|
||||
vendor.value !== VENDOR_HOUNDIFY &&
|
||||
vendor.value !== VENDOR_COBALT,
|
||||
)}
|
||||
onChange={(e) => {
|
||||
@@ -572,6 +590,7 @@ export const SpeechProviderSelection = ({
|
||||
vendor.value != VENDOR_WELLSAID &&
|
||||
vendor.value != VENDOR_ELEVENLABS &&
|
||||
vendor.value != VENDOR_WHISPER &&
|
||||
vendor.value !== VENDOR_RESEMBLE &&
|
||||
vendor.value !== VENDOR_CUSTOM,
|
||||
)}
|
||||
onChange={(e) => {
|
||||
@@ -599,6 +618,7 @@ export const SpeechProviderSelection = ({
|
||||
)}
|
||||
{recogVendor &&
|
||||
!recogVendor.toString().startsWith(VENDOR_CUSTOM) &&
|
||||
recogVendor !== VENDOR_DEEPGRAM_FLUX &&
|
||||
recogLang && (
|
||||
<>
|
||||
<label htmlFor="recognizer_lang">Language</label>
|
||||
|
||||
@@ -2,11 +2,11 @@ import React, { useEffect, useState } from "react";
|
||||
import { P } from "@jambonz/ui-kit";
|
||||
|
||||
import { Modal, ModalClose } from "src/components";
|
||||
import { getFetch } from "src/api";
|
||||
import { getFetch, getLcrRoutes, getLcrs } from "src/api";
|
||||
import { API_PHONE_NUMBERS } from "src/api/constants";
|
||||
import { formatPhoneNumber, hasLength } from "src/utils";
|
||||
import { formatPhoneNumber, hasLength, hasValue } from "src/utils";
|
||||
|
||||
import type { Carrier, PhoneNumber } from "src/api/types";
|
||||
import type { Carrier, Lcr, PhoneNumber } from "src/api/types";
|
||||
|
||||
type DeleteProps = {
|
||||
carrier: Carrier;
|
||||
@@ -20,28 +20,64 @@ export const DeleteCarrier = ({
|
||||
handleSubmit,
|
||||
}: DeleteProps) => {
|
||||
const [phoneNumbers, setPhoneNumbers] = useState<PhoneNumber[]>();
|
||||
const [lcrs, setLcrs] = useState<Lcr[]>();
|
||||
|
||||
useEffect(() => {
|
||||
let ignore = false;
|
||||
|
||||
getFetch<PhoneNumber[]>(API_PHONE_NUMBERS).then(({ json }) => {
|
||||
Promise.all([
|
||||
getFetch<PhoneNumber[]>(API_PHONE_NUMBERS),
|
||||
new Promise<Lcr[]>((resolve, reject) => {
|
||||
getLcrs()
|
||||
.then(({ json }) => {
|
||||
Promise.all(
|
||||
json.map((lcr: Lcr) =>
|
||||
getLcrRoutes(lcr.lcr_sid!)
|
||||
.then(({ json }) => {
|
||||
if (
|
||||
json.some((route) =>
|
||||
route.lcr_carrier_set_entries?.some(
|
||||
(entry) =>
|
||||
entry.voip_carrier_sid === carrier.voip_carrier_sid,
|
||||
),
|
||||
)
|
||||
) {
|
||||
return lcr;
|
||||
}
|
||||
})
|
||||
.catch((error) => reject(error)),
|
||||
),
|
||||
)
|
||||
.then((lcrs) => {
|
||||
resolve(lcrs as Lcr[]);
|
||||
})
|
||||
.catch((error) => reject(error));
|
||||
})
|
||||
.catch((error) => reject(error));
|
||||
}),
|
||||
]).then(([numbers, fetchedLcrs]) => {
|
||||
if (!ignore) {
|
||||
setPhoneNumbers(
|
||||
json.filter(
|
||||
numbers.json.filter(
|
||||
(phone) => phone.voip_carrier_sid === carrier.voip_carrier_sid,
|
||||
),
|
||||
);
|
||||
|
||||
// Only set LCRs if they are not empty
|
||||
setLcrs(fetchedLcrs.filter((p) => hasValue(p)));
|
||||
}
|
||||
});
|
||||
|
||||
return function cleanup() {
|
||||
ignore = true;
|
||||
};
|
||||
}, []);
|
||||
}, [carrier.voip_carrier_sid]);
|
||||
|
||||
const hasBlockingDependencies = hasLength(phoneNumbers) || hasLength(lcrs);
|
||||
|
||||
return (
|
||||
<>
|
||||
{phoneNumbers && !hasLength(phoneNumbers) && (
|
||||
{phoneNumbers && lcrs && !hasBlockingDependencies && (
|
||||
<Modal handleCancel={handleCancel} handleSubmit={handleSubmit}>
|
||||
<P>
|
||||
Are you sure you want to delete carrier{" "}
|
||||
@@ -49,24 +85,49 @@ export const DeleteCarrier = ({
|
||||
</P>
|
||||
</Modal>
|
||||
)}
|
||||
{hasLength(phoneNumbers) && (
|
||||
{hasBlockingDependencies && (
|
||||
<ModalClose handleClose={handleCancel}>
|
||||
<P>
|
||||
In order to delete the carrier it cannot be in use by any{" "}
|
||||
<span>Phone Numbers ({phoneNumbers.length})</span>.
|
||||
{hasLength(phoneNumbers) && (
|
||||
<span>Phone Numbers ({phoneNumbers.length})</span>
|
||||
)}
|
||||
{hasLength(phoneNumbers) && hasLength(lcrs) && " or "}
|
||||
{hasLength(lcrs) && (
|
||||
<span>Outbound call Routings ({lcrs.length})</span>
|
||||
)}
|
||||
.
|
||||
</P>
|
||||
<ul className="m">
|
||||
<li>
|
||||
<strong>Phone Numbers:</strong>
|
||||
</li>
|
||||
{phoneNumbers.map((phone) => {
|
||||
return (
|
||||
<li className="txt--teal" key={phone.phone_number_sid}>
|
||||
{formatPhoneNumber(phone.number)}
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
|
||||
{hasLength(phoneNumbers) && (
|
||||
<ul className="m">
|
||||
<li>
|
||||
<strong>Phone Numbers:</strong>
|
||||
</li>
|
||||
{phoneNumbers.map((phone) => {
|
||||
return (
|
||||
<li className="txt--teal" key={phone.phone_number_sid}>
|
||||
{formatPhoneNumber(phone.number)}
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
)}
|
||||
|
||||
{hasLength(lcrs) && (
|
||||
<ul className="m">
|
||||
<li>
|
||||
<strong>Outbound Call Routing:</strong>
|
||||
</li>
|
||||
{lcrs.map((lcr) => {
|
||||
return (
|
||||
<li className="txt--teal" key={lcr.lcr_sid}>
|
||||
{lcr.name || "Default route"}
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
)}
|
||||
</ModalClose>
|
||||
)}
|
||||
</>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -67,9 +67,6 @@ export const LcrForm = ({ lcrDataMap, lcrRouteDataMap }: LcrFormProps) => {
|
||||
const [accountSid, setAccountSid] = useState("");
|
||||
const [isActive, setIsActive] = useState(true);
|
||||
const [lcrRoutes, setLcrRoutes] = useState<LcrRoute[]>([LCR_ROUTE_TEMPLATE]);
|
||||
const [previousLcrRoutes, setPreviousLcrRoutes] = useState<LcrRoute[]>([
|
||||
LCR_ROUTE_TEMPLATE,
|
||||
]);
|
||||
const [previouseLcr, setPreviousLcr] = useState<Lcr | null>();
|
||||
const [accounts] = useServiceProviderData<Account[]>("Accounts");
|
||||
const [lcrForDelete, setLcrForDelete] = useState<Lcr | null>();
|
||||
@@ -82,7 +79,7 @@ export const LcrForm = ({ lcrDataMap, lcrRouteDataMap }: LcrFormProps) => {
|
||||
setLocation();
|
||||
if (currentServiceProvider) {
|
||||
setApiUrl(
|
||||
`ServiceProviders/${currentServiceProvider.service_provider_sid}/VoipCarriers`,
|
||||
`ServiceProviders/${currentServiceProvider.service_provider_sid}/VoipCarriers${accountSid ? `?account_sid=${accountSid}` : ""}`,
|
||||
);
|
||||
}
|
||||
}, [user, currentServiceProvider, accountSid]);
|
||||
@@ -92,16 +89,8 @@ export const LcrForm = ({ lcrDataMap, lcrRouteDataMap }: LcrFormProps) => {
|
||||
setAccountSid(user?.account_sid);
|
||||
}
|
||||
|
||||
const carriersFiltered = carriers
|
||||
? carriers.filter((carrier) =>
|
||||
accountSid
|
||||
? carrier.account_sid === accountSid
|
||||
: carrier.account_sid === null,
|
||||
)
|
||||
: [];
|
||||
|
||||
const ret = carriersFiltered
|
||||
? carriersFiltered.map((c: Carrier, i) => {
|
||||
const ret = carriers
|
||||
? carriers.map((c: Carrier, i) => {
|
||||
if (i === 0) {
|
||||
setDefaultCarrier(c.voip_carrier_sid);
|
||||
}
|
||||
@@ -123,45 +112,47 @@ export const LcrForm = ({ lcrDataMap, lcrRouteDataMap }: LcrFormProps) => {
|
||||
return ret;
|
||||
}, [accountSid, carriers]);
|
||||
|
||||
if (lcrDataMap && lcrDataMap.data && lcrDataMap.data !== previouseLcr) {
|
||||
setLcrName(lcrDataMap.data.name || "");
|
||||
setIsActive(lcrDataMap.data.is_active);
|
||||
setPreviousLcr(lcrDataMap.data);
|
||||
}
|
||||
useEffect(() => {
|
||||
if (lcrDataMap && lcrDataMap.data && lcrDataMap.data !== previouseLcr) {
|
||||
setLcrName(lcrDataMap.data.name || "");
|
||||
setIsActive(lcrDataMap.data.is_active);
|
||||
setPreviousLcr(lcrDataMap.data);
|
||||
if (lcrDataMap.data.account_sid) {
|
||||
setAccountSid(lcrDataMap.data.account_sid);
|
||||
}
|
||||
}
|
||||
}, [lcrDataMap?.data, previouseLcr]);
|
||||
|
||||
useMemo(() => {
|
||||
let default_lcr_route_sid = "";
|
||||
if (
|
||||
lcrRouteDataMap &&
|
||||
lcrRouteDataMap.data &&
|
||||
lcrRouteDataMap.data !== previousLcrRoutes
|
||||
) {
|
||||
setPreviousLcrRoutes(lcrRouteDataMap.data);
|
||||
// Find default carrier
|
||||
lcrRouteDataMap.data.forEach((lr) => {
|
||||
lr.lcr_carrier_set_entries?.forEach((entry) => {
|
||||
if (
|
||||
entry.lcr_carrier_set_entry_sid ===
|
||||
lcrDataMap?.data?.default_carrier_set_entry_sid
|
||||
) {
|
||||
// Only process when both lcrDataMap and lcrRouteDataMap are available
|
||||
if (lcrRouteDataMap && lcrRouteDataMap.data && lcrDataMap?.data) {
|
||||
const defaultCarrierSetEntrySid =
|
||||
lcrDataMap.data.default_carrier_set_entry_sid;
|
||||
|
||||
// Find and store default route information
|
||||
lcrRouteDataMap.data.forEach((route) => {
|
||||
route.lcr_carrier_set_entries?.forEach((entry) => {
|
||||
if (entry.lcr_carrier_set_entry_sid === defaultCarrierSetEntrySid) {
|
||||
setDefaultLcrCarrier(entry.voip_carrier_sid || defaultCarrier);
|
||||
setDefaultLcrCarrierSetEntrySid(
|
||||
entry.lcr_carrier_set_entry_sid || null,
|
||||
);
|
||||
default_lcr_route_sid = entry.lcr_route_sid || "";
|
||||
setDefaultLcrRoute(lr);
|
||||
setDefaultLcrRoute(route);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (lcrRouteDataMap && lcrRouteDataMap.data)
|
||||
setLcrRoutes(
|
||||
lcrRouteDataMap.data.filter(
|
||||
(route) => route.lcr_route_sid !== default_lcr_route_sid,
|
||||
),
|
||||
);
|
||||
}, [lcrRouteDataMap?.data]);
|
||||
// Filter out routes that contain the default carrier set entry
|
||||
const filteredRoutes = lcrRouteDataMap.data.filter((route) => {
|
||||
return !route.lcr_carrier_set_entries?.some(
|
||||
(entry) =>
|
||||
entry.lcr_carrier_set_entry_sid === defaultCarrierSetEntrySid,
|
||||
);
|
||||
});
|
||||
|
||||
setLcrRoutes(filteredRoutes);
|
||||
}
|
||||
}, [lcrRouteDataMap?.data, lcrDataMap?.data]);
|
||||
|
||||
const addLcrRoutes = () => {
|
||||
const newLcrRoute = LCR_ROUTE_TEMPLATE;
|
||||
|
||||
@@ -83,6 +83,9 @@ export const PhoneNumbers = () => {
|
||||
page_size: Number(perPageFilter),
|
||||
...(accSid && { account_sid: accSid }),
|
||||
...(filter && { filter }),
|
||||
...(currentServiceProvider?.service_provider_sid && {
|
||||
service_provider_sid: currentServiceProvider.service_provider_sid,
|
||||
}),
|
||||
})
|
||||
.then(({ json }) => {
|
||||
if (json) {
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
.barGroup {
|
||||
border-radius: ui-vars.$px01;
|
||||
@include mixins.code();
|
||||
text-align: left;
|
||||
padding: ui-vars.$px03;
|
||||
color: ui-vars.$pink;
|
||||
@@ -13,6 +12,7 @@
|
||||
margin-top: ui-vars.$px02;
|
||||
overflow-x: auto;
|
||||
overflow-y: scroll;
|
||||
@include mixins.code();
|
||||
|
||||
@media (max-width: 600px) {
|
||||
padding: 15px;
|
||||
@@ -72,7 +72,6 @@
|
||||
|
||||
.spanDetailsWrapper {
|
||||
border-radius: ui-vars.$px01;
|
||||
@include mixins.code();
|
||||
text-align: left;
|
||||
padding: ui-vars.$px01;
|
||||
background-color: ui-vars.$white;
|
||||
@@ -82,6 +81,7 @@
|
||||
max-width: ui-vars.$width-tablet-2;
|
||||
max-height: 500px;
|
||||
overflow-y: scroll;
|
||||
@include mixins.code();
|
||||
|
||||
&__detailsWrapper {
|
||||
height: 100%;
|
||||
|
||||
@@ -161,6 +161,7 @@ export const Player = ({ call }: PlayerProps) => {
|
||||
|
||||
const drawSttRegionForSpan = (
|
||||
s: JaegerSpan,
|
||||
allSpans: JaegerSpan[],
|
||||
startPoint: JaegerSpan,
|
||||
channel = 0,
|
||||
) => {
|
||||
@@ -175,7 +176,36 @@ export const Player = ({ call }: PlayerProps) => {
|
||||
const end =
|
||||
(s.endTimeUnixNano - startPoint.startTimeUnixNano) / 1_000_000_000;
|
||||
|
||||
const endSpeechTime = getSilenceStartTime(start, end, channel);
|
||||
const verbHookSpans = getSpansByNameRegex(allSpans, /verb:hook/);
|
||||
const verbHookSpan = verbHookSpans.find(
|
||||
(v) => v.parentSpanId === s.spanId,
|
||||
);
|
||||
|
||||
let verbHookDurantion = 0;
|
||||
let latency = 0;
|
||||
if (verbHookSpan) {
|
||||
verbHookDurantion =
|
||||
(verbHookSpan.endTimeUnixNano - verbHookSpan.startTimeUnixNano) /
|
||||
1_000_000_000;
|
||||
}
|
||||
|
||||
const [sttLatencyMs] = getSpanAttributeByName(
|
||||
s.attributes,
|
||||
"stt.latency_ms",
|
||||
);
|
||||
let endSpeechTime = 0;
|
||||
if (!sttLatencyMs) {
|
||||
endSpeechTime = getSilenceStartTime(start, end, channel);
|
||||
latency = Number(
|
||||
(end - endSpeechTime - verbHookDurantion).toFixed(2),
|
||||
);
|
||||
} else {
|
||||
endSpeechTime =
|
||||
end -
|
||||
Number(sttLatencyMs.value.stringValue) / 1_000 -
|
||||
verbHookDurantion;
|
||||
latency = Number(sttLatencyMs.value.stringValue) / 1_000;
|
||||
}
|
||||
|
||||
const [sttResult] = getSpanAttributeByName(s.attributes, "stt.result");
|
||||
let att: WaveSurferSttResult;
|
||||
@@ -187,7 +217,7 @@ export const Player = ({ call }: PlayerProps) => {
|
||||
transcript: data.alternatives[0].transcript,
|
||||
confidence: data.alternatives[0].confidence,
|
||||
language_code: data.language_code,
|
||||
...(endSpeechTime > 0 && { latency: end - endSpeechTime }),
|
||||
latency,
|
||||
};
|
||||
|
||||
const [sttResolve] = getSpanAttributeByName(
|
||||
@@ -206,7 +236,7 @@ export const Player = ({ call }: PlayerProps) => {
|
||||
color: "rgba(255, 255, 0, 0.55)",
|
||||
drag: false,
|
||||
resize: false,
|
||||
content: `${(end - endSpeechTime).toFixed(2)}s`,
|
||||
content: `${latency}s`,
|
||||
});
|
||||
|
||||
changeRegionMouseStyle(latencyRegion, channel);
|
||||
@@ -353,7 +383,7 @@ export const Player = ({ call }: PlayerProps) => {
|
||||
if (startPoint) {
|
||||
const gatherSpans = getSpansByNameRegex(spans, /:gather{/);
|
||||
gatherSpans.forEach((s) => {
|
||||
drawSttRegionForSpan(s, startPoint);
|
||||
drawSttRegionForSpan(s, spans, startPoint);
|
||||
});
|
||||
|
||||
// Trasscription
|
||||
@@ -363,6 +393,7 @@ export const Player = ({ call }: PlayerProps) => {
|
||||
const channel = Number(cs.name.split(":")[1]);
|
||||
drawSttRegionForSpan(
|
||||
cs,
|
||||
spans,
|
||||
startPoint,
|
||||
channel > 0 ? channel - 1 : channel,
|
||||
);
|
||||
|
||||
@@ -52,6 +52,11 @@ import {
|
||||
VENDOR_CARTESIA,
|
||||
VENDOR_VOXIST,
|
||||
VENDOR_OPENAI,
|
||||
VENDOR_INWORLD,
|
||||
VENDOR_DEEPGRAM_FLUX,
|
||||
VENDOR_RESEMBLE,
|
||||
VENDOR_HOUNDIFY,
|
||||
VENDOR_GLADIA,
|
||||
} from "src/vendor";
|
||||
import { MSG_REQUIRED_FIELDS } from "src/constants";
|
||||
import {
|
||||
@@ -80,9 +85,13 @@ import type {
|
||||
import { setAccountFilter, setLocation } from "src/store/localStore";
|
||||
import {
|
||||
ADDITIONAL_SPEECH_VENDORS,
|
||||
ASSEMBLYAI_STT_VERSIONS,
|
||||
DEEPGRAM_STT_ENPOINT,
|
||||
DEFAULT_ASSEMBLYAI_STT_VERSION,
|
||||
DEFAULT_CARTESIA_OPTIONS,
|
||||
DEFAULT_ELEVENLABS_OPTIONS,
|
||||
DEFAULT_GOOGLE_CUSTOM_VOICE,
|
||||
DEFAULT_INWORLD_OPTIONS,
|
||||
DEFAULT_PLAYHT_OPTIONS,
|
||||
DEFAULT_RIMELABS_OPTIONS,
|
||||
DEFAULT_VERBIO_MODEL,
|
||||
@@ -101,6 +110,13 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
|
||||
const { toastError, toastSuccess } = useToast();
|
||||
const navigate = useNavigate();
|
||||
const user = useSelectState("user");
|
||||
|
||||
// ElevenLabs API URI options
|
||||
const ELEVENLABS_API_URI_OPTIONS = [
|
||||
{ name: "US", value: "api.elevenlabs.io" },
|
||||
{ name: "EU", value: "api.eu.residency.elevenlabs.io" },
|
||||
{ name: "IN", value: "api.in.residency.elevenlabs.io" },
|
||||
];
|
||||
const currentServiceProvider = useSelectState("currentServiceProvider");
|
||||
const regions = useRegionVendors();
|
||||
const [accounts] = useServiceProviderData<Account[]>("Accounts");
|
||||
@@ -114,11 +130,13 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
|
||||
);
|
||||
const [region, setRegion] = useState("");
|
||||
const [apiKey, setApiKey] = useState("");
|
||||
const [apiUri, setApiUri] = useState("api.elevenlabs.io");
|
||||
const [userId, setUserId] = useState("");
|
||||
const [accessKeyId, setAccessKeyId] = useState("");
|
||||
const [secretAccessKey, setSecretAccessKey] = useState("");
|
||||
const [clientId, setClientId] = useState("");
|
||||
const [secretKey, setSecretKey] = useState("");
|
||||
const [clientKey, setClientKey] = useState("");
|
||||
const [clientSecret, setClientSecret] = useState("");
|
||||
const [googleServiceKey, setGoogleServiceKey] =
|
||||
useState<GoogleServiceKey | null>(null);
|
||||
@@ -129,6 +147,9 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
|
||||
const [ttsModelId, setTtsModelId] = useState("");
|
||||
const [sttModelId, setSttModelId] = useState("");
|
||||
const [engineVersion, setEngineVersion] = useState(DEFAULT_VERBIO_MODEL);
|
||||
const [serviceVersion, setServiceVersion] = useState(
|
||||
DEFAULT_ASSEMBLYAI_STT_VERSION,
|
||||
);
|
||||
const [instanceId, setInstanceId] = useState("");
|
||||
const [initialCheckCustomTts, setInitialCheckCustomTts] = useState(false);
|
||||
const [initialCheckCustomStt, setInitialCheckCustomStt] = useState(false);
|
||||
@@ -198,6 +219,13 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
|
||||
const [tmpPlayhtTtsUri, setTmpPlayhtTtsUri] = useState("");
|
||||
const [initialPlayhtOnpremCheck, setInitialPlayhtOnpremCheck] =
|
||||
useState(false);
|
||||
const [resembleTtsUri, setResembleTtsUri] = useState("");
|
||||
const [tmpResembleTtsUri, setTmpResembleTtsUri] = useState("");
|
||||
const [initialResembleOnpremCheck, setInitialResembleOnpremCheck] =
|
||||
useState(false);
|
||||
const [resembleTtsUseTls, setResembleTtsUseTls] = useState(false);
|
||||
const [tmpResembleTtsUseTls, setTmpResembleTtsUseTls] = useState(false);
|
||||
const [houndifyServerUri, setHoundifyServerUri] = useState("");
|
||||
const handleFile = (file: File) => {
|
||||
const handleError = () => {
|
||||
setGoogleServiceKey(null);
|
||||
@@ -233,6 +261,8 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
|
||||
return DEFAULT_PLAYHT_OPTIONS;
|
||||
case VENDOR_RIMELABS:
|
||||
return DEFAULT_RIMELABS_OPTIONS;
|
||||
case VENDOR_INWORLD:
|
||||
return DEFAULT_INWORLD_OPTIONS;
|
||||
case VENDOR_CARTESIA:
|
||||
return DEFAULT_CARTESIA_OPTIONS;
|
||||
}
|
||||
@@ -249,6 +279,8 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
|
||||
return "https://docs.play.ht/reference/api-generate-tts-audio-stream";
|
||||
case VENDOR_RIMELABS:
|
||||
return "https://rimelabs.mintlify.app/api-reference/endpoint/streaming-mp3#variable-parameters";
|
||||
case VENDOR_INWORLD:
|
||||
return "https://docs.inworld.ai/api-reference/ttsAPI/texttospeech/synthesize-speech-stream";
|
||||
case VENDOR_CARTESIA:
|
||||
return "https://docs.cartesia.ai/api-reference/tts/bytes";
|
||||
}
|
||||
@@ -260,7 +292,19 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
|
||||
switch (vendor) {
|
||||
case VENDOR_PLAYHT:
|
||||
return "Voice Engine";
|
||||
case VENDOR_DEEPGRAM:
|
||||
return "Model ID";
|
||||
case VENDOR_CARTESIA:
|
||||
return "TTS Model ID";
|
||||
default:
|
||||
return "Model";
|
||||
}
|
||||
};
|
||||
|
||||
const getSTTModelLabelByVendor = (vendor: Lowercase<Vendor>) => {
|
||||
switch (vendor) {
|
||||
case VENDOR_CARTESIA:
|
||||
return " STT Model ID";
|
||||
case VENDOR_DEEPGRAM:
|
||||
return "Model ID";
|
||||
default:
|
||||
@@ -397,6 +441,7 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
|
||||
}),
|
||||
...(vendor === VENDOR_CARTESIA && {
|
||||
model_id: ttsModelId || null,
|
||||
stt_model_id: sttModelId || null,
|
||||
options: options || null,
|
||||
}),
|
||||
...(vendor === VENDOR_CUSTOM && {
|
||||
@@ -414,16 +459,27 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
|
||||
nuance_tts_uri: onPremNuanceTtsUrl || null,
|
||||
nuance_stt_uri: onPremNuanceSttUrl || null,
|
||||
}),
|
||||
...(vendor === VENDOR_HOUNDIFY && {
|
||||
client_id: clientId || null,
|
||||
client_key: clientKey || null,
|
||||
user_id: userId || null,
|
||||
houndify_server_uri: houndifyServerUri || null,
|
||||
}),
|
||||
...(vendor === VENDOR_COBALT && {
|
||||
cobalt_server_uri: cobaltServerUri || null,
|
||||
}),
|
||||
...((vendor === VENDOR_ELEVENLABS ||
|
||||
vendor === VENDOR_WHISPER ||
|
||||
vendor === VENDOR_INWORLD ||
|
||||
vendor === VENDOR_RIMELABS) && {
|
||||
model_id: ttsModelId || null,
|
||||
}),
|
||||
...(vendor === VENDOR_ELEVENLABS && {
|
||||
api_uri: apiUri || null,
|
||||
}),
|
||||
...((vendor === VENDOR_ELEVENLABS ||
|
||||
vendor === VENDOR_PLAYHT ||
|
||||
vendor === VENDOR_INWORLD ||
|
||||
vendor === VENDOR_RIMELABS) && {
|
||||
options: options || null,
|
||||
}),
|
||||
@@ -447,9 +503,20 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
|
||||
...(vendor === VENDOR_VERBIO && {
|
||||
engine_version: engineVersion,
|
||||
}),
|
||||
...(vendor === VENDOR_ASSEMBLYAI && {
|
||||
service_version: serviceVersion || null,
|
||||
}),
|
||||
...(vendor === VENDOR_PLAYHT && {
|
||||
playht_tts_uri: playhtTtsUri || null,
|
||||
}),
|
||||
...(vendor === VENDOR_RESEMBLE && {
|
||||
resemble_tts_uri: resembleTtsUri || null,
|
||||
resemble_tts_use_tls: resembleTtsUseTls ? 1 : 0,
|
||||
}),
|
||||
...(vendor === VENDOR_GLADIA && {
|
||||
api_key: apiKey || null,
|
||||
region: region || null,
|
||||
}),
|
||||
};
|
||||
|
||||
if (credential && credential.data) {
|
||||
@@ -496,9 +563,13 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
|
||||
vendor === VENDOR_ELEVENLABS ||
|
||||
vendor === VENDOR_PLAYHT ||
|
||||
vendor === VENDOR_RIMELABS ||
|
||||
vendor === VENDOR_INWORLD ||
|
||||
vendor === VENDOR_WHISPER ||
|
||||
vendor === VENDOR_CARTESIA ||
|
||||
vendor === VENDOR_OPENAI
|
||||
vendor === VENDOR_OPENAI ||
|
||||
vendor === VENDOR_RESEMBLE ||
|
||||
vendor === VENDOR_DEEPGRAM_FLUX ||
|
||||
vendor === VENDOR_GLADIA
|
||||
? apiKey
|
||||
: null,
|
||||
}),
|
||||
@@ -565,6 +636,7 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
|
||||
vendor === VENDOR_WHISPER ||
|
||||
vendor === VENDOR_PLAYHT ||
|
||||
vendor === VENDOR_RIMELABS ||
|
||||
vendor === VENDOR_INWORLD ||
|
||||
vendor === VENDOR_CARTESIA ||
|
||||
vendor === VENDOR_OPENAI ||
|
||||
vendor === VENDOR_DEEPGRAM
|
||||
@@ -599,7 +671,7 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
|
||||
useEffect(() => {
|
||||
const modelId = credential?.data?.model_id || "";
|
||||
if (ttsModels.length > 0 && !ttsModels.some((m) => m.value === modelId)) {
|
||||
setTtsModelId(sttModels[0].value);
|
||||
setTtsModelId(ttsModels[0].value);
|
||||
} else {
|
||||
setTtsModelId(modelId);
|
||||
}
|
||||
@@ -651,6 +723,10 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
|
||||
setApiKey(credential.data.api_key);
|
||||
}
|
||||
|
||||
if (credential.data.api_uri) {
|
||||
setApiUri(credential.data.api_uri);
|
||||
}
|
||||
|
||||
if (credential.data.region) {
|
||||
setRegion(credential.data.region);
|
||||
}
|
||||
@@ -662,6 +738,9 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
|
||||
if (credential.data.client_id) {
|
||||
setClientId(credential.data.client_id);
|
||||
}
|
||||
if (credential.data.client_key) {
|
||||
setClientKey(credential.data.client_key);
|
||||
}
|
||||
|
||||
if (credential.data.secret) {
|
||||
setSecretKey(credential.data.secret);
|
||||
@@ -758,10 +837,15 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
|
||||
(vendor === VENDOR_OPENAI || vendor === VENDOR_DEEPGRAM)
|
||||
) {
|
||||
setSttModelId(credential.data.model_id);
|
||||
} else if (credential.data.stt_model_id) {
|
||||
setSttModelId(credential.data.stt_model_id);
|
||||
}
|
||||
if (credential?.data?.playht_tts_uri) {
|
||||
setPlayhtTtsUri(credential.data.playht_tts_uri);
|
||||
}
|
||||
if (credential?.data?.resemble_tts_uri) {
|
||||
setResembleTtsUri(credential.data.resemble_tts_uri);
|
||||
}
|
||||
}
|
||||
if (credential?.data?.options) {
|
||||
setOptions(credential.data.options);
|
||||
@@ -776,9 +860,7 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
|
||||
setUseCustomVoicesCheck(json.length > 0);
|
||||
});
|
||||
}
|
||||
if (credential?.data?.deepgram_stt_uri) {
|
||||
setDeepgramSttUri(credential.data.deepgram_stt_uri);
|
||||
}
|
||||
setDeepgramSttUri(credential?.data?.deepgram_stt_uri || "");
|
||||
if (credential?.data?.deepgram_tts_uri) {
|
||||
setDeepgramTtsUri(credential.data.deepgram_tts_uri);
|
||||
}
|
||||
@@ -787,12 +869,21 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
|
||||
credential?.data?.deepgram_stt_use_tls > 0 ? true : false,
|
||||
);
|
||||
}
|
||||
setInitialDeepgramOnpremCheck(hasValue(credential?.data?.deepgram_stt_uri));
|
||||
setInitialDeepgramOnpremCheck(
|
||||
hasValue(credential?.data?.deepgram_stt_uri) &&
|
||||
!DEEPGRAM_STT_ENPOINT.map((e) => e.value).includes(
|
||||
credential?.data?.deepgram_stt_uri,
|
||||
),
|
||||
);
|
||||
|
||||
if (credential?.data?.user_id) {
|
||||
setUserId(credential.data.user_id);
|
||||
}
|
||||
|
||||
if (credential?.data?.houndify_server_uri) {
|
||||
setHoundifyServerUri(credential.data.houndify_server_uri);
|
||||
}
|
||||
|
||||
if (credential?.data?.voice_engine) {
|
||||
setTtsModelId(credential.data.voice_engine);
|
||||
}
|
||||
@@ -817,6 +908,9 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
|
||||
if (credential?.data?.engine_version) {
|
||||
setEngineVersion(credential.data.engine_version);
|
||||
}
|
||||
if (credential?.data?.service_version) {
|
||||
setServiceVersion(credential.data.service_version);
|
||||
}
|
||||
|
||||
if (credential?.data?.speechmatics_stt_uri) {
|
||||
setInitialSpeechMaticsOnpremCheck(
|
||||
@@ -825,6 +919,15 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
|
||||
setSpeechmaticsEndpoint(credential.data.speechmatics_stt_uri);
|
||||
}
|
||||
setInitialPlayhtOnpremCheck(hasValue(credential?.data?.playht_tts_uri));
|
||||
setInitialResembleOnpremCheck(hasValue(credential?.data?.resemble_tts_uri));
|
||||
if (credential?.data?.resemble_tts_use_tls) {
|
||||
setResembleTtsUseTls(
|
||||
credential?.data?.resemble_tts_use_tls > 0 ? true : false,
|
||||
);
|
||||
setTmpResembleTtsUseTls(
|
||||
credential?.data?.resemble_tts_use_tls > 0 ? true : false,
|
||||
);
|
||||
}
|
||||
}, [credential]);
|
||||
|
||||
const updateCustomVoices = (
|
||||
@@ -888,6 +991,9 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
|
||||
setVendor(e.target.value as Lowercase<Vendor>);
|
||||
setRegion("");
|
||||
setApiKey("");
|
||||
setApiUri(
|
||||
e.target.value === VENDOR_ELEVENLABS ? "api.elevenlabs.io" : "",
|
||||
);
|
||||
setGoogleServiceKey(null);
|
||||
}}
|
||||
disabled={credential ? true : false}
|
||||
@@ -943,7 +1049,10 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
|
||||
vendor !== VENDOR_COBALT &&
|
||||
vendor !== VENDOR_SONIOX &&
|
||||
vendor !== VENDOR_SPEECHMATICS &&
|
||||
vendor !== VENDOR_DEEPGRAM_FLUX &&
|
||||
vendor !== VENDOR_HOUNDIFY &&
|
||||
vendor !== VENDOR_OPENAI &&
|
||||
vendor !== VENDOR_GLADIA &&
|
||||
vendor != VENDOR_CUSTOM && (
|
||||
<label htmlFor="use_for_tts" className="chk">
|
||||
<input
|
||||
@@ -961,7 +1070,8 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
|
||||
vendor !== VENDOR_WHISPER &&
|
||||
vendor !== VENDOR_PLAYHT &&
|
||||
vendor !== VENDOR_RIMELABS &&
|
||||
vendor !== VENDOR_CARTESIA &&
|
||||
vendor !== VENDOR_INWORLD &&
|
||||
vendor !== VENDOR_RESEMBLE &&
|
||||
vendor !== VENDOR_ELEVENLABS && (
|
||||
<label htmlFor="use_for_stt" className="chk">
|
||||
<input
|
||||
@@ -1341,6 +1451,56 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{vendor === VENDOR_HOUNDIFY && (
|
||||
<fieldset>
|
||||
<label htmlFor="houndify_client_id">
|
||||
Client ID
|
||||
{!onPremNuanceSttCheck && !onPremNuanceTtsCheck && <span>*</span>}
|
||||
</label>
|
||||
<input
|
||||
id="houndify_client_id"
|
||||
required={!onPremNuanceSttCheck && !onPremNuanceTtsCheck}
|
||||
type="text"
|
||||
name="houndify_client_id"
|
||||
placeholder="Client ID"
|
||||
value={clientId}
|
||||
onChange={(e) => setClientId(e.target.value)}
|
||||
disabled={credential ? true : false}
|
||||
/>
|
||||
<label htmlFor="houndify_secret">
|
||||
Client Key
|
||||
{!onPremNuanceSttCheck && !onPremNuanceTtsCheck && <span>*</span>}
|
||||
</label>
|
||||
<Passwd
|
||||
id="houndify_secret"
|
||||
required={!onPremNuanceSttCheck && !onPremNuanceTtsCheck}
|
||||
name="houndify_secret"
|
||||
placeholder="Client Key"
|
||||
value={clientKey ? getObscuredSecret(clientKey) : clientKey}
|
||||
onChange={(e) => setClientKey(e.target.value)}
|
||||
disabled={credential ? true : false}
|
||||
/>
|
||||
<label htmlFor="houndify_user_id">User ID</label>
|
||||
<input
|
||||
id="houndify_user_id"
|
||||
type="text"
|
||||
name="houndify_user_id"
|
||||
placeholder="User ID"
|
||||
value={userId}
|
||||
onChange={(e) => setUserId(e.target.value)}
|
||||
disabled={credential ? true : false}
|
||||
/>
|
||||
<label htmlFor="houndify_server_uri">Audio Endpoint</label>
|
||||
<input
|
||||
id="houndify_server_uri"
|
||||
type="text"
|
||||
name="houndify_server_uri"
|
||||
placeholder="Audio Endpoint (optional)"
|
||||
value={houndifyServerUri}
|
||||
onChange={(e) => setHoundifyServerUri(e.target.value)}
|
||||
/>
|
||||
</fieldset>
|
||||
)}
|
||||
{vendor === VENDOR_NUANCE && (
|
||||
<>
|
||||
<fieldset>
|
||||
@@ -1497,6 +1657,22 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
|
||||
</fieldset>
|
||||
</>
|
||||
)}
|
||||
{vendor === VENDOR_ASSEMBLYAI && (
|
||||
<fieldset>
|
||||
<label htmlFor={`${vendor}_tts_model_id`}>
|
||||
Service version<span>*</span>
|
||||
</label>
|
||||
<Selector
|
||||
id={"assemblyai_service_version"}
|
||||
name={"assemblyai_service_version"}
|
||||
value={serviceVersion}
|
||||
options={ASSEMBLYAI_STT_VERSIONS}
|
||||
onChange={(e) => {
|
||||
setServiceVersion(e.target.value);
|
||||
}}
|
||||
/>
|
||||
</fieldset>
|
||||
)}
|
||||
{vendor === VENDOR_AWS && (
|
||||
<fieldset>
|
||||
<label htmlFor="vendor">
|
||||
@@ -1685,16 +1861,98 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
|
||||
</fieldset>
|
||||
)}
|
||||
|
||||
{vendor === VENDOR_RESEMBLE && (
|
||||
<fieldset>
|
||||
<Checkzone
|
||||
disabled={hasValue(credential)}
|
||||
hidden
|
||||
name="use_on-prem_resemble_container"
|
||||
label="Use on-prem Resemble container"
|
||||
initialCheck={initialResembleOnpremCheck}
|
||||
handleChecked={(e) => {
|
||||
setInitialResembleOnpremCheck(e.target.checked);
|
||||
if (e.target.checked) {
|
||||
if (tmpResembleTtsUri) {
|
||||
setResembleTtsUri(tmpResembleTtsUri);
|
||||
}
|
||||
if (tmpResembleTtsUseTls) {
|
||||
setResembleTtsUseTls(tmpResembleTtsUseTls);
|
||||
}
|
||||
} else {
|
||||
setTmpResembleTtsUri(resembleTtsUri);
|
||||
setResembleTtsUri("");
|
||||
setTmpResembleTtsUseTls(resembleTtsUseTls);
|
||||
setResembleTtsUseTls(false);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<label htmlFor="resemble_uri_for_tts">
|
||||
TTS Container URI<span>*</span>
|
||||
</label>
|
||||
<input
|
||||
id="resemble_uri_for_tts"
|
||||
required
|
||||
type="text"
|
||||
name="resemble_uri_for_tts"
|
||||
placeholder=""
|
||||
value={resembleTtsUri}
|
||||
onChange={(e) => setResembleTtsUri(e.target.value)}
|
||||
/>
|
||||
<label htmlFor="resemble_stt_use_tls" className="chk">
|
||||
<input
|
||||
id="resemble_stt_use_tls"
|
||||
name="resemble_stt_use_tls"
|
||||
type="checkbox"
|
||||
onChange={(e) => setResembleTtsUseTls(e.target.checked)}
|
||||
defaultChecked={resembleTtsUseTls}
|
||||
/>
|
||||
<div>Use TLS</div>
|
||||
</label>
|
||||
</Checkzone>
|
||||
</fieldset>
|
||||
)}
|
||||
|
||||
{vendor === VENDOR_ELEVENLABS && (
|
||||
<fieldset>
|
||||
<label htmlFor="elevenlabs_api_uri">
|
||||
Data residency<span>*</span>
|
||||
</label>
|
||||
<Selector
|
||||
id="elevenlabs_api_uri"
|
||||
name="elevenlabs_api_uri"
|
||||
value={apiUri}
|
||||
options={ELEVENLABS_API_URI_OPTIONS}
|
||||
onChange={(e) => setApiUri(e.target.value)}
|
||||
required
|
||||
/>
|
||||
<label htmlFor={`${vendor}_apikey`}>
|
||||
API key<span>*</span>
|
||||
</label>
|
||||
<Passwd
|
||||
id={`${vendor}_apikey`}
|
||||
required
|
||||
name={`${vendor}_apikey`}
|
||||
placeholder="API key"
|
||||
value={apiKey ? getObscuredSecret(apiKey) : apiKey}
|
||||
onChange={(e) => setApiKey(e.target.value)}
|
||||
disabled={credential ? true : false}
|
||||
/>
|
||||
</fieldset>
|
||||
)}
|
||||
|
||||
{(vendor === VENDOR_WELLSAID ||
|
||||
vendor === VENDOR_ASSEMBLYAI ||
|
||||
vendor === VENDOR_VOXIST ||
|
||||
vendor == VENDOR_ELEVENLABS ||
|
||||
vendor === VENDOR_WHISPER ||
|
||||
vendor === VENDOR_RIMELABS ||
|
||||
vendor === VENDOR_INWORLD ||
|
||||
vendor === VENDOR_SONIOX ||
|
||||
vendor === VENDOR_CARTESIA ||
|
||||
vendor === VENDOR_OPENAI ||
|
||||
vendor === VENDOR_SPEECHMATICS) && (
|
||||
vendor === VENDOR_DEEPGRAM_FLUX ||
|
||||
vendor === VENDOR_RESEMBLE ||
|
||||
vendor === VENDOR_SPEECHMATICS ||
|
||||
vendor === VENDOR_GLADIA) && (
|
||||
<fieldset>
|
||||
<label htmlFor={`${vendor}_apikey`}>
|
||||
API key<span>*</span>
|
||||
@@ -1710,11 +1968,12 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
|
||||
/>
|
||||
</fieldset>
|
||||
)}
|
||||
{(vendor == VENDOR_ELEVENLABS ||
|
||||
vendor == VENDOR_WHISPER ||
|
||||
vendor === VENDOR_CARTESIA ||
|
||||
{(vendor === VENDOR_ELEVENLABS ||
|
||||
vendor === VENDOR_WHISPER ||
|
||||
vendor === VENDOR_PLAYHT ||
|
||||
vendor == VENDOR_RIMELABS) &&
|
||||
vendor === VENDOR_RIMELABS ||
|
||||
vendor === VENDOR_INWORLD ||
|
||||
(ttsCheck && vendor === VENDOR_CARTESIA)) &&
|
||||
ttsModels.length > 0 && (
|
||||
<fieldset>
|
||||
<label htmlFor={`${vendor}_tts_model_id`}>
|
||||
@@ -1731,11 +1990,13 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
|
||||
/>
|
||||
</fieldset>
|
||||
)}
|
||||
{(vendor == VENDOR_OPENAI || vendor === VENDOR_DEEPGRAM) &&
|
||||
{(vendor == VENDOR_OPENAI ||
|
||||
vendor === VENDOR_DEEPGRAM ||
|
||||
(sttCheck && vendor === VENDOR_CARTESIA)) &&
|
||||
sttModels.length > 0 && (
|
||||
<fieldset>
|
||||
<label htmlFor={`${vendor}_stt_model_id`}>
|
||||
{getModelLabelByVendor(vendor)}
|
||||
{getSTTModelLabelByVendor(vendor)}
|
||||
</label>
|
||||
<Selector
|
||||
id={"stt_model_id"}
|
||||
@@ -1751,7 +2012,8 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
|
||||
{(vendor === VENDOR_ELEVENLABS ||
|
||||
vendor === VENDOR_PLAYHT ||
|
||||
vendor === VENDOR_CARTESIA ||
|
||||
vendor === VENDOR_RIMELABS) && (
|
||||
vendor === VENDOR_RIMELABS ||
|
||||
vendor === VENDOR_INWORLD) && (
|
||||
<fieldset>
|
||||
<Checkzone
|
||||
hidden
|
||||
@@ -1972,6 +2234,19 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
|
||||
onChange={(e) => setApiKey(e.target.value)}
|
||||
disabled={credential ? true : false}
|
||||
/>
|
||||
<label htmlFor={`${vendor}_deepgram_stt_enpoint`}>
|
||||
Deepgram STT Endpoint<span>*</span>
|
||||
</label>
|
||||
<Selector
|
||||
id={"deepgram_stt_enpoint"}
|
||||
name={"deepgram_stt_enpoint"}
|
||||
value={deepgramSttUri}
|
||||
options={DEEPGRAM_STT_ENPOINT}
|
||||
onChange={(e) => {
|
||||
setDeepgramSttUri(e.target.value);
|
||||
setDeepgramSttUseTls(hasValue(e.target.value));
|
||||
}}
|
||||
/>
|
||||
</Checkzone>
|
||||
<Checkzone
|
||||
disabled={hasValue(credential)}
|
||||
|
||||
@@ -7,12 +7,12 @@ input[type="text"],
|
||||
input[type="email"],
|
||||
input[type="number"],
|
||||
input[type="password"] {
|
||||
@include ui-mixins.m();
|
||||
padding: ui-vars.$px01 ui-vars.$px02;
|
||||
border-radius: ui-vars.$px01;
|
||||
border: 2px solid ui-vars.$grey;
|
||||
background-color: ui-vars.$white;
|
||||
color: inherit;
|
||||
@include ui-mixins.m();
|
||||
|
||||
&:focus {
|
||||
border-color: ui-vars.$dark;
|
||||
@@ -110,11 +110,11 @@ fieldset {
|
||||
}
|
||||
|
||||
label {
|
||||
@include ui-mixins.m();
|
||||
@include ui-mixins.font-medium();
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
@include ui-mixins.font-medium();
|
||||
@include ui-mixins.m();
|
||||
|
||||
span {
|
||||
color: ui-vars.$jambonz;
|
||||
@@ -218,7 +218,8 @@ fieldset {
|
||||
}
|
||||
}
|
||||
|
||||
.gateway {
|
||||
.gateway,
|
||||
.gateway-inbound {
|
||||
padding: ui-vars.$px02;
|
||||
border-radius: ui-vars.$px01;
|
||||
border: 2px solid ui-vars.$grey;
|
||||
@@ -284,6 +285,18 @@ fieldset {
|
||||
}
|
||||
}
|
||||
|
||||
.gateway-inbound {
|
||||
> div {
|
||||
&:nth-child(1) {
|
||||
grid-template-columns: [col] calc(70% - #{ui-vars.$px02 * 2}) [col] 30%;
|
||||
|
||||
@include mixins.small() {
|
||||
grid-template-columns: [col] 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.lcr {
|
||||
@extend .gateway;
|
||||
|
||||
|
||||
@@ -21,10 +21,10 @@
|
||||
}
|
||||
|
||||
&__row {
|
||||
@include ui-mixins.m();
|
||||
display: grid;
|
||||
padding: ui-vars.$px03;
|
||||
align-items: center;
|
||||
@include ui-mixins.m();
|
||||
|
||||
@include mixins.small() {
|
||||
padding: ui-vars.$px02;
|
||||
|
||||
@@ -30,11 +30,11 @@
|
||||
}
|
||||
|
||||
&__title {
|
||||
@include ui-mixins.p();
|
||||
display: flex;
|
||||
align-items: center;
|
||||
grid-gap: ui-vars.$px02;
|
||||
color: ui-vars.$jambonz;
|
||||
@include ui-mixins.p();
|
||||
|
||||
+ .item__meta {
|
||||
@include mixins.small() {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
@use "sass:color";
|
||||
@use "./forms";
|
||||
// @use "./cards";
|
||||
@use "./lists";
|
||||
@@ -68,7 +69,7 @@ details {
|
||||
&.ok {
|
||||
color: ui-vars.$teal;
|
||||
border: 2px solid ui-vars.$teal;
|
||||
background-color: mix(ui-vars.$white, ui-vars.$teal, 95%);
|
||||
background-color: color.mix(ui-vars.$white, ui-vars.$teal, 95%);
|
||||
}
|
||||
|
||||
&.not-tested {
|
||||
@@ -77,8 +78,8 @@ details {
|
||||
}
|
||||
|
||||
summary {
|
||||
@include ui-mixins.m();
|
||||
cursor: pointer;
|
||||
@include ui-mixins.m();
|
||||
|
||||
+ * {
|
||||
margin-top: ui-vars.$px02;
|
||||
@@ -160,7 +161,6 @@ details {
|
||||
|
||||
/** Used for recent-calls */
|
||||
.pre-grid {
|
||||
@include mixins.code();
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
grid-row-gap: ui-vars.$px01;
|
||||
@@ -171,6 +171,7 @@ details {
|
||||
background-color: ui-vars.$dark;
|
||||
border-radius: ui-vars.$px01;
|
||||
margin-top: ui-vars.$px02;
|
||||
@include mixins.code();
|
||||
}
|
||||
|
||||
.pre-grid-white {
|
||||
|
||||
28
src/vendor/index.tsx
vendored
28
src/vendor/index.tsx
vendored
@@ -12,6 +12,7 @@ export const VENDOR_MICROSOFT = "microsoft";
|
||||
export const VENDOR_WELLSAID = "wellsaid";
|
||||
export const VENDOR_NUANCE = "nuance";
|
||||
export const VENDOR_DEEPGRAM = "deepgram";
|
||||
export const VENDOR_DEEPGRAM_FLUX = "deepgramflux";
|
||||
export const VENDOR_IBM = "ibm";
|
||||
export const VENDOR_NVIDIA = "nvidia";
|
||||
export const VENDOR_SONIOX = "soniox";
|
||||
@@ -24,9 +25,13 @@ export const VENDOR_VOXIST = "voxist";
|
||||
export const VENDOR_WHISPER = "whisper";
|
||||
export const VENDOR_PLAYHT = "playht";
|
||||
export const VENDOR_RIMELABS = "rimelabs";
|
||||
export const VENDOR_INWORLD = "inworld";
|
||||
export const VENDOR_VERBIO = "verbio";
|
||||
export const VENDOR_CARTESIA = "cartesia";
|
||||
export const VENDOR_OPENAI = "openai";
|
||||
export const VENDOR_RESEMBLE = "resemble";
|
||||
export const VENDOR_HOUNDIFY = "houndify";
|
||||
export const VENDOR_GLADIA = "gladia";
|
||||
|
||||
export const vendors: VendorOptions[] = [
|
||||
{
|
||||
@@ -41,6 +46,10 @@ export const vendors: VendorOptions[] = [
|
||||
name: "Deepgram",
|
||||
value: VENDOR_DEEPGRAM,
|
||||
},
|
||||
{
|
||||
name: "Deepgram Flux",
|
||||
value: VENDOR_DEEPGRAM_FLUX,
|
||||
},
|
||||
{
|
||||
name: "IBM",
|
||||
value: VENDOR_IBM,
|
||||
@@ -101,6 +110,10 @@ export const vendors: VendorOptions[] = [
|
||||
name: "RimeLabs",
|
||||
value: VENDOR_RIMELABS,
|
||||
},
|
||||
{
|
||||
name: "Inworld",
|
||||
value: VENDOR_INWORLD,
|
||||
},
|
||||
{
|
||||
name: "Verbio",
|
||||
value: VENDOR_VERBIO,
|
||||
@@ -113,6 +126,18 @@ export const vendors: VendorOptions[] = [
|
||||
name: "OpenAI",
|
||||
value: VENDOR_OPENAI,
|
||||
},
|
||||
{
|
||||
name: "Resemble",
|
||||
value: VENDOR_RESEMBLE,
|
||||
},
|
||||
{
|
||||
name: "SoundHound",
|
||||
value: VENDOR_HOUNDIFY,
|
||||
},
|
||||
{
|
||||
name: "Gladia",
|
||||
value: VENDOR_GLADIA,
|
||||
},
|
||||
].sort((a, b) => a.name.localeCompare(b.name)) as VendorOptions[];
|
||||
|
||||
export const AWS_CREDENTIAL_ACCESS_KEY = "access_key";
|
||||
@@ -145,12 +170,14 @@ export const useRegionVendors = () => {
|
||||
import("./regions/ms-azure-regions"),
|
||||
import("./regions/ibm-regions"),
|
||||
import("./regions/speechmatics-regions"),
|
||||
import("./regions/gladia-regions"),
|
||||
]).then(
|
||||
([
|
||||
{ default: awsRegions },
|
||||
{ default: msRegions },
|
||||
{ default: ibmRegions },
|
||||
{ default: speechmaticsRegions },
|
||||
{ default: gladiaRegions },
|
||||
]) => {
|
||||
if (!ignore) {
|
||||
setRegions({
|
||||
@@ -158,6 +185,7 @@ export const useRegionVendors = () => {
|
||||
microsoft: msRegions,
|
||||
ibm: ibmRegions,
|
||||
speechmatics: speechmaticsRegions,
|
||||
gladia: gladiaRegions,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
14
src/vendor/regions/gladia-regions.ts
vendored
Normal file
14
src/vendor/regions/gladia-regions.ts
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
import type { Region } from "../types";
|
||||
|
||||
export const regions: Region[] = [
|
||||
{
|
||||
name: "US West",
|
||||
value: "us-west",
|
||||
},
|
||||
{
|
||||
name: "EU West",
|
||||
value: "eu-west",
|
||||
},
|
||||
];
|
||||
|
||||
export default regions;
|
||||
15
src/vendor/types.ts
vendored
15
src/vendor/types.ts
vendored
@@ -5,6 +5,7 @@ export type Vendor =
|
||||
| "WellSaid"
|
||||
| "Nuance"
|
||||
| "Deepgram"
|
||||
| "DeepgramFlux"
|
||||
| "IBM"
|
||||
| "Nvidia"
|
||||
| "Soniox"
|
||||
@@ -17,9 +18,13 @@ export type Vendor =
|
||||
| "whisper"
|
||||
| "playht"
|
||||
| "rimelabs"
|
||||
| "inworld"
|
||||
| "verbio"
|
||||
| "openai"
|
||||
| "Cartesia";
|
||||
| "Cartesia"
|
||||
| "Resemble"
|
||||
| "Houndify"
|
||||
| "gladia";
|
||||
|
||||
export interface VendorOptions {
|
||||
name: Vendor;
|
||||
@@ -31,6 +36,11 @@ export interface LabelOptions {
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface JambonzResourceOptions {
|
||||
name: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface Region {
|
||||
name: string;
|
||||
value: string;
|
||||
@@ -76,6 +86,7 @@ export interface RegionVendors {
|
||||
microsoft: Region[];
|
||||
ibm: Region[];
|
||||
speechmatics: Region[];
|
||||
gladia: Region[];
|
||||
}
|
||||
|
||||
export interface TtsModels {
|
||||
@@ -96,6 +107,7 @@ export interface RecognizerVendors {
|
||||
speechmatics: Language[];
|
||||
cobalt: Language[];
|
||||
assemblyai: Language[];
|
||||
deepgramflux: Language[];
|
||||
}
|
||||
|
||||
export interface SynthesisVendors {
|
||||
@@ -112,6 +124,7 @@ export interface SynthesisVendors {
|
||||
playht: VoiceLanguage[];
|
||||
cartesia: VoiceLanguage[];
|
||||
rimelabs: VoiceLanguage[];
|
||||
inworld: VoiceLanguage[];
|
||||
}
|
||||
|
||||
export interface MSRawSpeech {
|
||||
|
||||
@@ -15,6 +15,15 @@ export default defineConfig(() => {
|
||||
src: path.resolve(__dirname, "src"),
|
||||
},
|
||||
},
|
||||
|
||||
// Configure Sass to use the modern API
|
||||
css: {
|
||||
preprocessorOptions: {
|
||||
scss: {
|
||||
api: "modern-compiler",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
return config;
|
||||
|
||||
Reference in New Issue
Block a user