refactor: remove conditional voice mode support from backend and frontend (#8597)

* revert changes that made voice mode conditional to the presence of webrtcvad

* refactor: lazy-load webrtcvad dependency

* refactor: remove try-except block and directly import voice_mode_router

* [autofix.ci] apply automated fixes

* feat: add LRU caching to voice activity detector initialization

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
Gabriel Luiz Freitas Almeida
2025-06-18 09:23:31 -03:00
committed by GitHub
parent 6dd5ebe33e
commit 6ada07a61f
10 changed files with 22 additions and 46 deletions

View File

@@ -19,6 +19,7 @@ from langflow.api.v1 import (
validate_router,
variables_router,
)
from langflow.api.v1.voice_mode import router as voice_mode_router
from langflow.api.v2 import files_router as files_router_v2
from langflow.api.v2 import mcp_router as mcp_router_v2
@@ -45,18 +46,12 @@ router_v1.include_router(folders_router)
router_v1.include_router(projects_router)
router_v1.include_router(starter_projects_router)
router_v1.include_router(mcp_router)
router_v1.include_router(voice_mode_router)
router_v1.include_router(mcp_projects_router)
router_v2.include_router(files_router_v2)
router_v2.include_router(mcp_router_v2)
try:
from langflow.api.v1.voice_mode import router as voice_mode_router
router_v1.include_router(voice_mode_router)
except ImportError:
pass
router = APIRouter(
prefix="/api",
)

View File

@@ -378,11 +378,3 @@ async def verify_public_flow_and_get_user(flow_id: uuid.UUID, client_id: str | N
raise HTTPException(status_code=403, detail=msg)
return user, new_flow_id
def get_voice_mode_enabled() -> bool:
try:
import webrtcvad # noqa: F401
except ImportError:
return False
return True

View File

@@ -14,6 +14,7 @@ from langflow.api.v1.store import router as store_router
from langflow.api.v1.users import router as users_router
from langflow.api.v1.validate import router as validate_router
from langflow.api.v1.variable import router as variables_router
from langflow.api.v1.voice_mode import router as voice_mode_router
__all__ = [
"api_key_router",
@@ -32,11 +33,5 @@ __all__ = [
"users_router",
"validate_router",
"variables_router",
"voice_mode_router",
]
try:
from langflow.api.v1.voice_mode import router as voice_mode_router
__all__ += ["voice_mode_router"]
except ImportError:
pass

View File

@@ -14,7 +14,7 @@ from fastapi.responses import StreamingResponse
from loguru import logger
from sqlmodel import select
from langflow.api.utils import CurrentActiveUser, DbSession, get_voice_mode_enabled, parse_value
from langflow.api.utils import CurrentActiveUser, DbSession, parse_value
from langflow.api.v1.schemas import (
ConfigResponse,
CustomComponentRequest,
@@ -752,7 +752,6 @@ async def get_config():
return {
"feature_flags": FEATURE_FLAGS,
**settings_service.settings.model_dump(),
"voice_mode_enabled": get_voice_mode_enabled(),
}
except Exception as exc:
raise HTTPException(status_code=500, detail=str(exc)) from exc

View File

@@ -388,7 +388,6 @@ class ConfigResponse(BaseModel):
public_flow_cleanup_interval: int
public_flow_expiration: int
event_delivery: Literal["polling", "streaming", "direct"]
voice_mode_enabled: bool
class CancelFlowResponse(BaseModel):

View File

@@ -7,14 +7,13 @@ import traceback
import uuid
from collections import defaultdict
from datetime import datetime, timezone
from functools import partial
from functools import lru_cache, partial
from typing import Any
from uuid import UUID, uuid4
import numpy as np
import requests
import sqlalchemy
import webrtcvad
import websockets
from cryptography.fernet import InvalidToken
from elevenlabs import ElevenLabs
@@ -83,6 +82,13 @@ CLIENT_TO_LF = "Client → LF"
# --- Helper Functions ---
@lru_cache(maxsize=1)
def get_vad():
import webrtcvad
return webrtcvad.Vad(mode=3)
async def authenticate_and_get_openai_key(session: DbSession, user: User, websocket: WebSocket):
"""Authenticate the user using a token or API key and retrieve the OpenAI API key.
@@ -129,7 +135,7 @@ class VoiceConfig:
self.elevenlabs_model = "eleven_multilingual_v2"
self.elevenlabs_client = None
self.elevenlabs_key = None
self.barge_in_enabled = False
self._barge_in_enabled = False
self.progress_enabled = True
self.default_openai_realtime_session = {
@@ -151,6 +157,12 @@ class VoiceConfig:
}
self.openai_realtime_session: dict[str, Any] = {}
@property
def barge_in_enabled(self):
# Later on we may want to tie this value
# to the availability of the webrtcvad package.
return self._barge_in_enabled
def get_session_dict(self):
return dict(self.default_openai_realtime_session)
@@ -765,11 +777,11 @@ async def flow_as_tool_websocket(
vad_queue: asyncio.Queue = asyncio.Queue()
vad_audio_buffer = bytearray()
bot_speaking_flag = [False]
vad = webrtcvad.Vad(mode=3)
async def process_vad_audio() -> None:
nonlocal vad_audio_buffer
last_speech_time = datetime.now(tz=timezone.utc)
vad = get_vad()
while True:
base64_data = await vad_queue.get()
raw_chunk_24k = base64.b64decode(base64_data)

View File

@@ -21,7 +21,6 @@ export interface ConfigResponse {
webhook_polling_interval: number;
serialization_max_items_length: number;
event_delivery: EventDeliveryType;
voice_mode_enabled: boolean;
}
export const useGetConfig: useQueryFunctionType<undefined, ConfigResponse> = (
@@ -45,9 +44,7 @@ export const useGetConfig: useQueryFunctionType<undefined, ConfigResponse> = (
(state) => state.setWebhookPollingInterval,
);
const setEventDelivery = useUtilityStore((state) => state.setEventDelivery);
const setVoiceModeEnabled = useFlowsManagerStore(
(state) => state.setVoiceModeEnabled,
);
const { query } = UseRequestProcessor();
const getConfigFn = async () => {
@@ -69,7 +66,6 @@ export const useGetConfig: useQueryFunctionType<undefined, ConfigResponse> = (
data.webhook_polling_interval ?? DEFAULT_POLLING_INTERVAL,
);
setEventDelivery(data.event_delivery ?? EventDeliveryType.POLLING);
setVoiceModeEnabled(Boolean(data.voice_mode_enabled));
}
return data;
};

View File

@@ -1,7 +1,6 @@
import ForwardedIconComponent from "@/components/common/genericIconComponent";
import { Button } from "@/components/ui/button";
import { ICON_STROKE_WIDTH } from "@/constants/constants";
import useFlowsManagerStore from "@/stores/flowsManagerStore";
import { useVoiceStore } from "@/stores/voiceStore";
interface VoiceButtonProps {
@@ -12,13 +11,6 @@ const VoiceButton = ({ toggleRecording }: VoiceButtonProps) => {
const setNewSessionCloseVoiceAssistant = useVoiceStore(
(state) => state.setNewSessionCloseVoiceAssistant,
);
const voiceModeEnabled = useFlowsManagerStore(
(state) => state.voiceModeEnabled,
);
if (!voiceModeEnabled) {
return null;
}
return (
<>

View File

@@ -17,8 +17,6 @@ const past = {};
const future = {};
const useFlowsManagerStore = create<FlowsManagerStoreType>((set, get) => ({
voiceModeEnabled: false,
setVoiceModeEnabled: (voiceModeEnabled: boolean) => set({ voiceModeEnabled }),
IOModalOpen: false,
setIOModalOpen: (IOModalOpen: boolean) => {
set({ IOModalOpen });

View File

@@ -29,8 +29,6 @@ export type FlowsManagerStoreType = {
IOModalOpen: boolean;
setIOModalOpen: (IOModalOpen: boolean) => void;
resetStore: () => void;
voiceModeEnabled: boolean;
setVoiceModeEnabled: (voiceModeEnabled: boolean) => void;
};
export type UseUndoRedoOptions = {