fix: voice_mode break fix (#8014)
* 🔧 (use-get-messages-polling.ts): refactor enqueuePolling method to simplify logic and improve readability 🔧 (use-get-messages-polling.ts): refactor startNextPolling method to remove unnecessary code and improve efficiency 🔧 (use-get-messages-polling.ts): refactor removeFromQueue method to simplify and improve maintainability 🔧 (use-get-messages-polling.ts): refactor useGetMessagesPollingMutation to handle stopping polling and removing from queue 🔧 (use-get-transactions.ts): add early return to getTransactionsFn to handle empty id case 🔧 (use-get-voice-list.ts): refactor useGetVoiceList function to handle empty elevenlabsApiKey case and improve readability 🔧 (audio-settings-dialog.tsx): update useGetVoiceList call to pass elevenLabsApiKey as argument * queues should hold payloads not strings * ✨ (use-get-global-variables.ts): add setGlobalVariablesEntities function to store global variables entities for use in the application ✨ (audio-settings-dialog.tsx): introduce debounced function to update ElevenLabs API key in global variables entities 📝 (globalVariables.ts): add setGlobalVariablesEntities function to the global variables store for managing entities 📝 (index.ts): define GlobalVariable type for global variables entities in the store * ✨ (frontend): add support for extracting flowId from URL query parameters to allow direct linking to specific flow logs. --------- Co-authored-by: cristhianzl <cristhian.lousa@gmail.com>
This commit is contained in:
parent
0e6fc94dad
commit
9096293e52
9 changed files with 74 additions and 44 deletions
|
|
@ -1143,8 +1143,8 @@ async def flow_tts_websocket(
|
||||||
try:
|
try:
|
||||||
await client_websocket.accept()
|
await client_websocket.accept()
|
||||||
|
|
||||||
openai_send_q: asyncio.Queue[str] = asyncio.Queue()
|
openai_send_q: asyncio.Queue[dict] = asyncio.Queue()
|
||||||
client_send_q: asyncio.Queue[str] = asyncio.Queue()
|
client_send_q: asyncio.Queue[dict] = asyncio.Queue()
|
||||||
|
|
||||||
log_event = create_event_logger()
|
log_event = create_event_logger()
|
||||||
|
|
||||||
|
|
@ -1171,13 +1171,13 @@ async def flow_tts_websocket(
|
||||||
def openai_send(payload):
|
def openai_send(payload):
|
||||||
log_event(payload, LF_TO_OPENAI)
|
log_event(payload, LF_TO_OPENAI)
|
||||||
logger.trace(f"Sending text {LF_TO_OPENAI}: {payload['type']}")
|
logger.trace(f"Sending text {LF_TO_OPENAI}: {payload['type']}")
|
||||||
openai_send_q.put_nowait(json.dumps(payload))
|
openai_send_q.put_nowait(payload)
|
||||||
logger.trace("JSON sent.")
|
logger.trace("JSON sent.")
|
||||||
|
|
||||||
def client_send(payload):
|
def client_send(payload):
|
||||||
log_event(payload, LF_TO_CLIENT)
|
log_event(payload, LF_TO_CLIENT)
|
||||||
logger.trace(f"Sending JSON {LF_TO_CLIENT}: {payload['type']}")
|
logger.trace(f"Sending JSON {LF_TO_CLIENT}: {payload['type']}")
|
||||||
client_send_q.put_nowait(json.dumps(payload))
|
client_send_q.put_nowait(payload)
|
||||||
logger.trace("JSON sent.")
|
logger.trace("JSON sent.")
|
||||||
|
|
||||||
async def close():
|
async def close():
|
||||||
|
|
|
||||||
|
|
@ -34,20 +34,12 @@ const MessagesPollingManager = {
|
||||||
activePolls: new Map<string, PollingItem>(),
|
activePolls: new Map<string, PollingItem>(),
|
||||||
|
|
||||||
enqueuePolling(id: string, pollingItem: PollingItem) {
|
enqueuePolling(id: string, pollingItem: PollingItem) {
|
||||||
if (!this.pollingQueue.has(id)) {
|
this.stopAll();
|
||||||
this.pollingQueue.set(id, []);
|
|
||||||
}
|
|
||||||
this.pollingQueue.set(
|
|
||||||
id,
|
|
||||||
(this.pollingQueue.get(id) || []).filter(
|
|
||||||
(item) => item.timestamp !== pollingItem.timestamp,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
this.pollingQueue.get(id)?.push(pollingItem);
|
|
||||||
|
|
||||||
if (!this.activePolls.has(id)) {
|
this.pollingQueue.clear();
|
||||||
this.startNextPolling(id);
|
this.pollingQueue.set(id, [pollingItem]);
|
||||||
}
|
|
||||||
|
this.startNextPolling(id);
|
||||||
},
|
},
|
||||||
|
|
||||||
startNextPolling(id: string) {
|
startNextPolling(id: string) {
|
||||||
|
|
@ -67,12 +59,7 @@ const MessagesPollingManager = {
|
||||||
if (activePoll) {
|
if (activePoll) {
|
||||||
clearInterval(activePoll.interval);
|
clearInterval(activePoll.interval);
|
||||||
this.activePolls.delete(id);
|
this.activePolls.delete(id);
|
||||||
const queue = this.pollingQueue.get(id) || [];
|
this.pollingQueue.delete(id);
|
||||||
this.pollingQueue.set(
|
|
||||||
id,
|
|
||||||
queue.filter((item) => item.timestamp !== activePoll.timestamp),
|
|
||||||
);
|
|
||||||
this.startNextPolling(id);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -83,11 +70,7 @@ const MessagesPollingManager = {
|
||||||
},
|
},
|
||||||
|
|
||||||
removeFromQueue(id: string, timestamp: number) {
|
removeFromQueue(id: string, timestamp: number) {
|
||||||
const queue = this.pollingQueue.get(id) || [];
|
this.pollingQueue.delete(id);
|
||||||
this.pollingQueue.set(
|
|
||||||
id,
|
|
||||||
queue.filter((item) => item.timestamp !== timestamp),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -146,6 +129,10 @@ export const useGetMessagesPollingMutation = (
|
||||||
return Promise.reject("Request already in progress");
|
return Promise.reject("Request already in progress");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (MessagesPollingManager.activePolls.has(requestId)) {
|
||||||
|
MessagesPollingManager.stopPoll(requestId);
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
requestIdRef.current === requestId &&
|
requestIdRef.current === requestId &&
|
||||||
MessagesPollingManager.activePolls.has(requestId)
|
MessagesPollingManager.activePolls.has(requestId)
|
||||||
|
|
@ -189,11 +176,15 @@ export const useGetMessagesPollingMutation = (
|
||||||
return () => {
|
return () => {
|
||||||
if (requestIdRef.current) {
|
if (requestIdRef.current) {
|
||||||
MessagesPollingManager.stopPoll(requestIdRef.current);
|
MessagesPollingManager.stopPoll(requestIdRef.current);
|
||||||
|
MessagesPollingManager.removeFromQueue(
|
||||||
|
requestIdRef.current,
|
||||||
|
Date.now(),
|
||||||
|
);
|
||||||
|
requestIdRef.current = null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Cast the mutation to the correct type
|
|
||||||
const mutation = mutate(
|
const mutation = mutate(
|
||||||
["useGetMessagesMutation"],
|
["useGetMessagesMutation"],
|
||||||
(payload: MessagesQueryParams) =>
|
(payload: MessagesQueryParams) =>
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,10 @@ export const useGetTransactionsQuery: useQueryFunctionType<
|
||||||
};
|
};
|
||||||
|
|
||||||
const getTransactionsFn = async () => {
|
const getTransactionsFn = async () => {
|
||||||
|
if (!id) return { pagination: {}, rows: [], columns: [] };
|
||||||
|
|
||||||
const config = {};
|
const config = {};
|
||||||
|
|
||||||
config["params"] = { flow_id: id };
|
config["params"] = { flow_id: id };
|
||||||
if (params) {
|
if (params) {
|
||||||
config["params"] = { ...config["params"], ...params };
|
config["params"] = { ...config["params"], ...params };
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,9 @@ export const useGetGlobalVariables: useQueryFunctionType<
|
||||||
const setUnavailableFields = useGlobalVariablesStore(
|
const setUnavailableFields = useGlobalVariablesStore(
|
||||||
(state) => state.setUnavailableFields,
|
(state) => state.setUnavailableFields,
|
||||||
);
|
);
|
||||||
|
const setGlobalVariablesEntities = useGlobalVariablesStore(
|
||||||
|
(state) => state.setGlobalVariablesEntities,
|
||||||
|
);
|
||||||
|
|
||||||
const isAuthenticated = useAuthStore((state) => state.isAuthenticated);
|
const isAuthenticated = useAuthStore((state) => state.isAuthenticated);
|
||||||
|
|
||||||
|
|
@ -28,6 +31,7 @@ export const useGetGlobalVariables: useQueryFunctionType<
|
||||||
const res = await api.get(`${getURL("VARIABLES")}/`);
|
const res = await api.get(`${getURL("VARIABLES")}/`);
|
||||||
setGlobalVariablesEntries(res.data.map((entry) => entry.name));
|
setGlobalVariablesEntries(res.data.map((entry) => entry.name));
|
||||||
setUnavailableFields(getUnavailableFields(res.data));
|
setUnavailableFields(getUnavailableFields(res.data));
|
||||||
|
setGlobalVariablesEntities(res.data);
|
||||||
return res.data;
|
return res.data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,22 +4,18 @@ import { api } from "../../api";
|
||||||
import { getURL } from "../../helpers/constants";
|
import { getURL } from "../../helpers/constants";
|
||||||
import { UseRequestProcessor } from "../../services/request-processor";
|
import { UseRequestProcessor } from "../../services/request-processor";
|
||||||
|
|
||||||
export const useGetVoiceList: useQueryFunctionType<undefined, any> = (
|
export const useGetVoiceList = (elevenlabsApiKey: string, options?: any) => {
|
||||||
options,
|
|
||||||
) => {
|
|
||||||
const { query } = UseRequestProcessor();
|
const { query } = UseRequestProcessor();
|
||||||
const setVoices = useVoiceStore((state) => state.setVoices);
|
const setVoices = useVoiceStore((state) => state.setVoices);
|
||||||
const voices = useVoiceStore((state) => state.voices);
|
const voices = useVoiceStore((state) => state.voices);
|
||||||
|
|
||||||
const getVoiceListFn = async (): Promise<
|
const getVoiceListFn = async () => {
|
||||||
{
|
|
||||||
name: string;
|
|
||||||
voice_id: string;
|
|
||||||
}[]
|
|
||||||
> => {
|
|
||||||
if (voices.length > 0) {
|
if (voices.length > 0) {
|
||||||
return voices;
|
return voices;
|
||||||
}
|
}
|
||||||
|
if (!elevenlabsApiKey) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
const res = await api.get(`${getURL("VOICE")}/elevenlabs/voice_ids`);
|
const res = await api.get(`${getURL("VOICE")}/elevenlabs/voice_ids`);
|
||||||
const data = res.data;
|
const data = res.data;
|
||||||
|
|
@ -41,7 +37,7 @@ export const useGetVoiceList: useQueryFunctionType<undefined, any> = (
|
||||||
};
|
};
|
||||||
|
|
||||||
const queryResult = query(
|
const queryResult = query(
|
||||||
["useGetVoiceList"],
|
["useGetVoiceList", elevenlabsApiKey],
|
||||||
getVoiceListFn,
|
getVoiceListFn,
|
||||||
defaultOptions,
|
defaultOptions,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,9 @@ import {
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu";
|
} from "@/components/ui/dropdown-menu";
|
||||||
import { Separator } from "@/components/ui/separator";
|
import { Separator } from "@/components/ui/separator";
|
||||||
|
import { usePatchGlobalVariables } from "@/controllers/API/queries/variables";
|
||||||
import { useGetVoiceList } from "@/controllers/API/queries/voice/use-get-voice-list";
|
import { useGetVoiceList } from "@/controllers/API/queries/voice/use-get-voice-list";
|
||||||
|
import { useDebounce } from "@/hooks/use-debounce";
|
||||||
import GeneralDeleteConfirmationModal from "@/shared/components/delete-confirmation-modal";
|
import GeneralDeleteConfirmationModal from "@/shared/components/delete-confirmation-modal";
|
||||||
import GeneralGlobalVariableModal from "@/shared/components/global-variable-modal";
|
import GeneralGlobalVariableModal from "@/shared/components/global-variable-modal";
|
||||||
import { useGlobalVariablesStore } from "@/stores/globalVariablesStore/globalVariables";
|
import { useGlobalVariablesStore } from "@/stores/globalVariablesStore/globalVariables";
|
||||||
|
|
@ -69,6 +71,10 @@ const SettingsVoiceModal = ({
|
||||||
(state) => state.globalVariablesEntries,
|
(state) => state.globalVariablesEntries,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const globalVariablesEntities = useGlobalVariablesStore(
|
||||||
|
(state) => state.globalVariablesEntities,
|
||||||
|
);
|
||||||
|
|
||||||
const openaiVoices = useVoiceStore((state) => state.openaiVoices);
|
const openaiVoices = useVoiceStore((state) => state.openaiVoices);
|
||||||
const [allVoices, setAllVoices] = useState<
|
const [allVoices, setAllVoices] = useState<
|
||||||
{
|
{
|
||||||
|
|
@ -83,7 +89,7 @@ const SettingsVoiceModal = ({
|
||||||
data: voiceList,
|
data: voiceList,
|
||||||
isFetched,
|
isFetched,
|
||||||
refetch,
|
refetch,
|
||||||
} = useGetVoiceList({
|
} = useGetVoiceList(elevenLabsApiKey, {
|
||||||
enabled: shouldFetchVoices,
|
enabled: shouldFetchVoices,
|
||||||
refetchOnMount: shouldFetchVoices,
|
refetchOnMount: shouldFetchVoices,
|
||||||
refetchOnWindowFocus: shouldFetchVoices,
|
refetchOnWindowFocus: shouldFetchVoices,
|
||||||
|
|
@ -158,6 +164,8 @@ const SettingsVoiceModal = ({
|
||||||
return globalVariables?.map((variable) => variable).includes(variable);
|
return globalVariables?.map((variable) => variable).includes(variable);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const { mutate: updateVariable } = usePatchGlobalVariables();
|
||||||
|
|
||||||
const handleSetMicrophone = (deviceId: string) => {
|
const handleSetMicrophone = (deviceId: string) => {
|
||||||
setSelectedMicrophone(deviceId);
|
setSelectedMicrophone(deviceId);
|
||||||
localStorage.setItem("lf_selected_microphone", deviceId);
|
localStorage.setItem("lf_selected_microphone", deviceId);
|
||||||
|
|
@ -227,6 +235,25 @@ const SettingsVoiceModal = ({
|
||||||
const showAddOpenAIKeyButton = !hasOpenAIAPIKey || isEditingOpenAIKey;
|
const showAddOpenAIKeyButton = !hasOpenAIAPIKey || isEditingOpenAIKey;
|
||||||
const showAllSettings = hasOpenAIAPIKey && !isEditingOpenAIKey;
|
const showAllSettings = hasOpenAIAPIKey && !isEditingOpenAIKey;
|
||||||
|
|
||||||
|
const debouncedSetElevenLabsApiKey = useDebounce((value: string) => {
|
||||||
|
const globalVariable = globalVariablesEntities?.find(
|
||||||
|
(variable) => variable.name === "ELEVENLABS_API_KEY",
|
||||||
|
);
|
||||||
|
|
||||||
|
if (globalVariable) {
|
||||||
|
updateVariable({
|
||||||
|
name: "ELEVENLABS_API_KEY",
|
||||||
|
value: value,
|
||||||
|
id: globalVariable.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, 2000);
|
||||||
|
|
||||||
|
const handleSetElevenLabsApiKey = (value: string) => {
|
||||||
|
setElevenLabsApiKey(value);
|
||||||
|
debouncedSetElevenLabsApiKey(value);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<DropdownMenu open={open} onOpenChange={onOpenChangeDropdownMenu}>
|
<DropdownMenu open={open} onOpenChange={onOpenChangeDropdownMenu}>
|
||||||
|
|
@ -372,9 +399,7 @@ const SettingsVoiceModal = ({
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
value={elevenLabsApiKey}
|
value={elevenLabsApiKey}
|
||||||
onChange={(value) => {
|
onChange={handleSetElevenLabsApiKey}
|
||||||
setElevenLabsApiKey(value);
|
|
||||||
}}
|
|
||||||
selectedOption={
|
selectedOption={
|
||||||
checkIfGlobalVariableExists(elevenLabsApiKey)
|
checkIfGlobalVariableExists(elevenLabsApiKey)
|
||||||
? elevenLabsApiKey
|
? elevenLabsApiKey
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import { FlowSettingsPropsType } from "@/types/components";
|
||||||
import { convertUTCToLocalTimezone } from "@/utils/utils";
|
import { convertUTCToLocalTimezone } from "@/utils/utils";
|
||||||
import { ColDef, ColGroupDef } from "ag-grid-community";
|
import { ColDef, ColGroupDef } from "ag-grid-community";
|
||||||
import { useCallback, useEffect, useState } from "react";
|
import { useCallback, useEffect, useState } from "react";
|
||||||
|
import { useSearchParams } from "react-router-dom";
|
||||||
import BaseModal from "../baseModal";
|
import BaseModal from "../baseModal";
|
||||||
|
|
||||||
export default function FlowLogsModal({
|
export default function FlowLogsModal({
|
||||||
|
|
@ -19,9 +20,11 @@ export default function FlowLogsModal({
|
||||||
const [pageSize, setPageSize] = useState(20);
|
const [pageSize, setPageSize] = useState(20);
|
||||||
const [columns, setColumns] = useState<Array<ColDef | ColGroupDef>>([]);
|
const [columns, setColumns] = useState<Array<ColDef | ColGroupDef>>([]);
|
||||||
const [rows, setRows] = useState<any>([]);
|
const [rows, setRows] = useState<any>([]);
|
||||||
|
const [searchParams] = useSearchParams();
|
||||||
|
const flowIdFromUrl = searchParams.get("id");
|
||||||
|
|
||||||
const { data, isLoading, refetch } = useGetTransactionsQuery({
|
const { data, isLoading, refetch } = useGetTransactionsQuery({
|
||||||
id: currentFlowId,
|
id: currentFlowId ?? flowIdFromUrl,
|
||||||
params: {
|
params: {
|
||||||
page: pageIndex,
|
page: pageIndex,
|
||||||
size: pageSize,
|
size: pageSize,
|
||||||
|
|
|
||||||
|
|
@ -11,5 +11,9 @@ export const useGlobalVariablesStore = create<GlobalVariablesStore>(
|
||||||
setGlobalVariablesEntries: (entries) => {
|
setGlobalVariablesEntries: (entries) => {
|
||||||
set({ globalVariablesEntries: entries });
|
set({ globalVariablesEntries: entries });
|
||||||
},
|
},
|
||||||
|
setGlobalVariablesEntities: (entities) => {
|
||||||
|
set({ globalVariablesEntities: entities });
|
||||||
|
},
|
||||||
|
globalVariablesEntities: undefined,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
|
import { GlobalVariable } from "@/types/global_variables";
|
||||||
|
|
||||||
export type GlobalVariablesStore = {
|
export type GlobalVariablesStore = {
|
||||||
globalVariablesEntries: Array<string> | undefined;
|
globalVariablesEntries: Array<string> | undefined;
|
||||||
setGlobalVariablesEntries: (entries: Array<string>) => void;
|
setGlobalVariablesEntries: (entries: Array<string>) => void;
|
||||||
unavailableFields: { [name: string]: string };
|
unavailableFields: { [name: string]: string };
|
||||||
setUnavailableFields: (fields: { [name: string]: string }) => void;
|
setUnavailableFields: (fields: { [name: string]: string }) => void;
|
||||||
|
globalVariablesEntities: Array<GlobalVariable> | undefined;
|
||||||
|
setGlobalVariablesEntities: (entities: GlobalVariable[]) => void;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue