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:
Sebastián Estévez 2025-05-13 16:39:55 -04:00 committed by GitHub
commit 9096293e52
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 74 additions and 44 deletions

View file

@ -1143,8 +1143,8 @@ async def flow_tts_websocket(
try:
await client_websocket.accept()
openai_send_q: asyncio.Queue[str] = asyncio.Queue()
client_send_q: asyncio.Queue[str] = asyncio.Queue()
openai_send_q: asyncio.Queue[dict] = asyncio.Queue()
client_send_q: asyncio.Queue[dict] = asyncio.Queue()
log_event = create_event_logger()
@ -1171,13 +1171,13 @@ async def flow_tts_websocket(
def openai_send(payload):
log_event(payload, LF_TO_OPENAI)
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.")
def client_send(payload):
log_event(payload, LF_TO_CLIENT)
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.")
async def close():

View file

@ -34,20 +34,12 @@ const MessagesPollingManager = {
activePolls: new Map<string, PollingItem>(),
enqueuePolling(id: string, pollingItem: PollingItem) {
if (!this.pollingQueue.has(id)) {
this.pollingQueue.set(id, []);
}
this.pollingQueue.set(
id,
(this.pollingQueue.get(id) || []).filter(
(item) => item.timestamp !== pollingItem.timestamp,
),
);
this.pollingQueue.get(id)?.push(pollingItem);
this.stopAll();
if (!this.activePolls.has(id)) {
this.startNextPolling(id);
}
this.pollingQueue.clear();
this.pollingQueue.set(id, [pollingItem]);
this.startNextPolling(id);
},
startNextPolling(id: string) {
@ -67,12 +59,7 @@ const MessagesPollingManager = {
if (activePoll) {
clearInterval(activePoll.interval);
this.activePolls.delete(id);
const queue = this.pollingQueue.get(id) || [];
this.pollingQueue.set(
id,
queue.filter((item) => item.timestamp !== activePoll.timestamp),
);
this.startNextPolling(id);
this.pollingQueue.delete(id);
}
},
@ -83,11 +70,7 @@ const MessagesPollingManager = {
},
removeFromQueue(id: string, timestamp: number) {
const queue = this.pollingQueue.get(id) || [];
this.pollingQueue.set(
id,
queue.filter((item) => item.timestamp !== timestamp),
);
this.pollingQueue.delete(id);
},
};
@ -146,6 +129,10 @@ export const useGetMessagesPollingMutation = (
return Promise.reject("Request already in progress");
}
if (MessagesPollingManager.activePolls.has(requestId)) {
MessagesPollingManager.stopPoll(requestId);
}
if (
requestIdRef.current === requestId &&
MessagesPollingManager.activePolls.has(requestId)
@ -189,11 +176,15 @@ export const useGetMessagesPollingMutation = (
return () => {
if (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(
["useGetMessagesMutation"],
(payload: MessagesQueryParams) =>

View file

@ -46,7 +46,10 @@ export const useGetTransactionsQuery: useQueryFunctionType<
};
const getTransactionsFn = async () => {
if (!id) return { pagination: {}, rows: [], columns: [] };
const config = {};
config["params"] = { flow_id: id };
if (params) {
config["params"] = { ...config["params"], ...params };

View file

@ -20,6 +20,9 @@ export const useGetGlobalVariables: useQueryFunctionType<
const setUnavailableFields = useGlobalVariablesStore(
(state) => state.setUnavailableFields,
);
const setGlobalVariablesEntities = useGlobalVariablesStore(
(state) => state.setGlobalVariablesEntities,
);
const isAuthenticated = useAuthStore((state) => state.isAuthenticated);
@ -28,6 +31,7 @@ export const useGetGlobalVariables: useQueryFunctionType<
const res = await api.get(`${getURL("VARIABLES")}/`);
setGlobalVariablesEntries(res.data.map((entry) => entry.name));
setUnavailableFields(getUnavailableFields(res.data));
setGlobalVariablesEntities(res.data);
return res.data;
};

View file

@ -4,22 +4,18 @@ import { api } from "../../api";
import { getURL } from "../../helpers/constants";
import { UseRequestProcessor } from "../../services/request-processor";
export const useGetVoiceList: useQueryFunctionType<undefined, any> = (
options,
) => {
export const useGetVoiceList = (elevenlabsApiKey: string, options?: any) => {
const { query } = UseRequestProcessor();
const setVoices = useVoiceStore((state) => state.setVoices);
const voices = useVoiceStore((state) => state.voices);
const getVoiceListFn = async (): Promise<
{
name: string;
voice_id: string;
}[]
> => {
const getVoiceListFn = async () => {
if (voices.length > 0) {
return voices;
}
if (!elevenlabsApiKey) {
return [];
}
const res = await api.get(`${getURL("VOICE")}/elevenlabs/voice_ids`);
const data = res.data;
@ -41,7 +37,7 @@ export const useGetVoiceList: useQueryFunctionType<undefined, any> = (
};
const queryResult = query(
["useGetVoiceList"],
["useGetVoiceList", elevenlabsApiKey],
getVoiceListFn,
defaultOptions,
);

View file

@ -9,7 +9,9 @@ import {
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
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 { useDebounce } from "@/hooks/use-debounce";
import GeneralDeleteConfirmationModal from "@/shared/components/delete-confirmation-modal";
import GeneralGlobalVariableModal from "@/shared/components/global-variable-modal";
import { useGlobalVariablesStore } from "@/stores/globalVariablesStore/globalVariables";
@ -69,6 +71,10 @@ const SettingsVoiceModal = ({
(state) => state.globalVariablesEntries,
);
const globalVariablesEntities = useGlobalVariablesStore(
(state) => state.globalVariablesEntities,
);
const openaiVoices = useVoiceStore((state) => state.openaiVoices);
const [allVoices, setAllVoices] = useState<
{
@ -83,7 +89,7 @@ const SettingsVoiceModal = ({
data: voiceList,
isFetched,
refetch,
} = useGetVoiceList({
} = useGetVoiceList(elevenLabsApiKey, {
enabled: shouldFetchVoices,
refetchOnMount: shouldFetchVoices,
refetchOnWindowFocus: shouldFetchVoices,
@ -158,6 +164,8 @@ const SettingsVoiceModal = ({
return globalVariables?.map((variable) => variable).includes(variable);
};
const { mutate: updateVariable } = usePatchGlobalVariables();
const handleSetMicrophone = (deviceId: string) => {
setSelectedMicrophone(deviceId);
localStorage.setItem("lf_selected_microphone", deviceId);
@ -227,6 +235,25 @@ const SettingsVoiceModal = ({
const showAddOpenAIKeyButton = !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 (
<>
<DropdownMenu open={open} onOpenChange={onOpenChangeDropdownMenu}>
@ -372,9 +399,7 @@ const SettingsVoiceModal = ({
/>
)}
value={elevenLabsApiKey}
onChange={(value) => {
setElevenLabsApiKey(value);
}}
onChange={handleSetElevenLabsApiKey}
selectedOption={
checkIfGlobalVariableExists(elevenLabsApiKey)
? elevenLabsApiKey

View file

@ -7,6 +7,7 @@ import { FlowSettingsPropsType } from "@/types/components";
import { convertUTCToLocalTimezone } from "@/utils/utils";
import { ColDef, ColGroupDef } from "ag-grid-community";
import { useCallback, useEffect, useState } from "react";
import { useSearchParams } from "react-router-dom";
import BaseModal from "../baseModal";
export default function FlowLogsModal({
@ -19,9 +20,11 @@ export default function FlowLogsModal({
const [pageSize, setPageSize] = useState(20);
const [columns, setColumns] = useState<Array<ColDef | ColGroupDef>>([]);
const [rows, setRows] = useState<any>([]);
const [searchParams] = useSearchParams();
const flowIdFromUrl = searchParams.get("id");
const { data, isLoading, refetch } = useGetTransactionsQuery({
id: currentFlowId,
id: currentFlowId ?? flowIdFromUrl,
params: {
page: pageIndex,
size: pageSize,

View file

@ -11,5 +11,9 @@ export const useGlobalVariablesStore = create<GlobalVariablesStore>(
setGlobalVariablesEntries: (entries) => {
set({ globalVariablesEntries: entries });
},
setGlobalVariablesEntities: (entities) => {
set({ globalVariablesEntities: entities });
},
globalVariablesEntities: undefined,
}),
);

View file

@ -1,6 +1,10 @@
import { GlobalVariable } from "@/types/global_variables";
export type GlobalVariablesStore = {
globalVariablesEntries: Array<string> | undefined;
setGlobalVariablesEntries: (entries: Array<string>) => void;
unavailableFields: { [name: string]: string };
setUnavailableFields: (fields: { [name: string]: string }) => void;
globalVariablesEntities: Array<GlobalVariable> | undefined;
setGlobalVariablesEntities: (entities: GlobalVariable[]) => void;
};