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:
parent
6dd5ebe33e
commit
6ada07a61f
10 changed files with 22 additions and 46 deletions
|
|
@ -19,6 +19,7 @@ from langflow.api.v1 import (
|
||||||
validate_router,
|
validate_router,
|
||||||
variables_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 files_router as files_router_v2
|
||||||
from langflow.api.v2 import mcp_router as mcp_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(projects_router)
|
||||||
router_v1.include_router(starter_projects_router)
|
router_v1.include_router(starter_projects_router)
|
||||||
router_v1.include_router(mcp_router)
|
router_v1.include_router(mcp_router)
|
||||||
|
router_v1.include_router(voice_mode_router)
|
||||||
router_v1.include_router(mcp_projects_router)
|
router_v1.include_router(mcp_projects_router)
|
||||||
|
|
||||||
router_v2.include_router(files_router_v2)
|
router_v2.include_router(files_router_v2)
|
||||||
router_v2.include_router(mcp_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(
|
router = APIRouter(
|
||||||
prefix="/api",
|
prefix="/api",
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
raise HTTPException(status_code=403, detail=msg)
|
||||||
|
|
||||||
return user, new_flow_id
|
return user, new_flow_id
|
||||||
|
|
||||||
|
|
||||||
def get_voice_mode_enabled() -> bool:
|
|
||||||
try:
|
|
||||||
import webrtcvad # noqa: F401
|
|
||||||
except ImportError:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
|
||||||
|
|
@ -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.users import router as users_router
|
||||||
from langflow.api.v1.validate import router as validate_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.variable import router as variables_router
|
||||||
|
from langflow.api.v1.voice_mode import router as voice_mode_router
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"api_key_router",
|
"api_key_router",
|
||||||
|
|
@ -32,11 +33,5 @@ __all__ = [
|
||||||
"users_router",
|
"users_router",
|
||||||
"validate_router",
|
"validate_router",
|
||||||
"variables_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
|
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ from fastapi.responses import StreamingResponse
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from sqlmodel import select
|
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 (
|
from langflow.api.v1.schemas import (
|
||||||
ConfigResponse,
|
ConfigResponse,
|
||||||
CustomComponentRequest,
|
CustomComponentRequest,
|
||||||
|
|
@ -752,7 +752,6 @@ async def get_config():
|
||||||
return {
|
return {
|
||||||
"feature_flags": FEATURE_FLAGS,
|
"feature_flags": FEATURE_FLAGS,
|
||||||
**settings_service.settings.model_dump(),
|
**settings_service.settings.model_dump(),
|
||||||
"voice_mode_enabled": get_voice_mode_enabled(),
|
|
||||||
}
|
}
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
raise HTTPException(status_code=500, detail=str(exc)) from exc
|
raise HTTPException(status_code=500, detail=str(exc)) from exc
|
||||||
|
|
|
||||||
|
|
@ -388,7 +388,6 @@ class ConfigResponse(BaseModel):
|
||||||
public_flow_cleanup_interval: int
|
public_flow_cleanup_interval: int
|
||||||
public_flow_expiration: int
|
public_flow_expiration: int
|
||||||
event_delivery: Literal["polling", "streaming", "direct"]
|
event_delivery: Literal["polling", "streaming", "direct"]
|
||||||
voice_mode_enabled: bool
|
|
||||||
|
|
||||||
|
|
||||||
class CancelFlowResponse(BaseModel):
|
class CancelFlowResponse(BaseModel):
|
||||||
|
|
|
||||||
|
|
@ -7,14 +7,13 @@ import traceback
|
||||||
import uuid
|
import uuid
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
from functools import partial
|
from functools import lru_cache, partial
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from uuid import UUID, uuid4
|
from uuid import UUID, uuid4
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import requests
|
import requests
|
||||||
import sqlalchemy
|
import sqlalchemy
|
||||||
import webrtcvad
|
|
||||||
import websockets
|
import websockets
|
||||||
from cryptography.fernet import InvalidToken
|
from cryptography.fernet import InvalidToken
|
||||||
from elevenlabs import ElevenLabs
|
from elevenlabs import ElevenLabs
|
||||||
|
|
@ -83,6 +82,13 @@ CLIENT_TO_LF = "Client → LF"
|
||||||
# --- Helper Functions ---
|
# --- 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):
|
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.
|
"""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_model = "eleven_multilingual_v2"
|
||||||
self.elevenlabs_client = None
|
self.elevenlabs_client = None
|
||||||
self.elevenlabs_key = None
|
self.elevenlabs_key = None
|
||||||
self.barge_in_enabled = False
|
self._barge_in_enabled = False
|
||||||
self.progress_enabled = True
|
self.progress_enabled = True
|
||||||
|
|
||||||
self.default_openai_realtime_session = {
|
self.default_openai_realtime_session = {
|
||||||
|
|
@ -151,6 +157,12 @@ class VoiceConfig:
|
||||||
}
|
}
|
||||||
self.openai_realtime_session: dict[str, Any] = {}
|
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):
|
def get_session_dict(self):
|
||||||
return dict(self.default_openai_realtime_session)
|
return dict(self.default_openai_realtime_session)
|
||||||
|
|
||||||
|
|
@ -765,11 +777,11 @@ async def flow_as_tool_websocket(
|
||||||
vad_queue: asyncio.Queue = asyncio.Queue()
|
vad_queue: asyncio.Queue = asyncio.Queue()
|
||||||
vad_audio_buffer = bytearray()
|
vad_audio_buffer = bytearray()
|
||||||
bot_speaking_flag = [False]
|
bot_speaking_flag = [False]
|
||||||
vad = webrtcvad.Vad(mode=3)
|
|
||||||
|
|
||||||
async def process_vad_audio() -> None:
|
async def process_vad_audio() -> None:
|
||||||
nonlocal vad_audio_buffer
|
nonlocal vad_audio_buffer
|
||||||
last_speech_time = datetime.now(tz=timezone.utc)
|
last_speech_time = datetime.now(tz=timezone.utc)
|
||||||
|
vad = get_vad()
|
||||||
while True:
|
while True:
|
||||||
base64_data = await vad_queue.get()
|
base64_data = await vad_queue.get()
|
||||||
raw_chunk_24k = base64.b64decode(base64_data)
|
raw_chunk_24k = base64.b64decode(base64_data)
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ export interface ConfigResponse {
|
||||||
webhook_polling_interval: number;
|
webhook_polling_interval: number;
|
||||||
serialization_max_items_length: number;
|
serialization_max_items_length: number;
|
||||||
event_delivery: EventDeliveryType;
|
event_delivery: EventDeliveryType;
|
||||||
voice_mode_enabled: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useGetConfig: useQueryFunctionType<undefined, ConfigResponse> = (
|
export const useGetConfig: useQueryFunctionType<undefined, ConfigResponse> = (
|
||||||
|
|
@ -45,9 +44,7 @@ export const useGetConfig: useQueryFunctionType<undefined, ConfigResponse> = (
|
||||||
(state) => state.setWebhookPollingInterval,
|
(state) => state.setWebhookPollingInterval,
|
||||||
);
|
);
|
||||||
const setEventDelivery = useUtilityStore((state) => state.setEventDelivery);
|
const setEventDelivery = useUtilityStore((state) => state.setEventDelivery);
|
||||||
const setVoiceModeEnabled = useFlowsManagerStore(
|
|
||||||
(state) => state.setVoiceModeEnabled,
|
|
||||||
);
|
|
||||||
const { query } = UseRequestProcessor();
|
const { query } = UseRequestProcessor();
|
||||||
|
|
||||||
const getConfigFn = async () => {
|
const getConfigFn = async () => {
|
||||||
|
|
@ -69,7 +66,6 @@ export const useGetConfig: useQueryFunctionType<undefined, ConfigResponse> = (
|
||||||
data.webhook_polling_interval ?? DEFAULT_POLLING_INTERVAL,
|
data.webhook_polling_interval ?? DEFAULT_POLLING_INTERVAL,
|
||||||
);
|
);
|
||||||
setEventDelivery(data.event_delivery ?? EventDeliveryType.POLLING);
|
setEventDelivery(data.event_delivery ?? EventDeliveryType.POLLING);
|
||||||
setVoiceModeEnabled(Boolean(data.voice_mode_enabled));
|
|
||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import ForwardedIconComponent from "@/components/common/genericIconComponent";
|
import ForwardedIconComponent from "@/components/common/genericIconComponent";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { ICON_STROKE_WIDTH } from "@/constants/constants";
|
import { ICON_STROKE_WIDTH } from "@/constants/constants";
|
||||||
import useFlowsManagerStore from "@/stores/flowsManagerStore";
|
|
||||||
import { useVoiceStore } from "@/stores/voiceStore";
|
import { useVoiceStore } from "@/stores/voiceStore";
|
||||||
|
|
||||||
interface VoiceButtonProps {
|
interface VoiceButtonProps {
|
||||||
|
|
@ -12,13 +11,6 @@ const VoiceButton = ({ toggleRecording }: VoiceButtonProps) => {
|
||||||
const setNewSessionCloseVoiceAssistant = useVoiceStore(
|
const setNewSessionCloseVoiceAssistant = useVoiceStore(
|
||||||
(state) => state.setNewSessionCloseVoiceAssistant,
|
(state) => state.setNewSessionCloseVoiceAssistant,
|
||||||
);
|
);
|
||||||
const voiceModeEnabled = useFlowsManagerStore(
|
|
||||||
(state) => state.voiceModeEnabled,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!voiceModeEnabled) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,6 @@ const past = {};
|
||||||
const future = {};
|
const future = {};
|
||||||
|
|
||||||
const useFlowsManagerStore = create<FlowsManagerStoreType>((set, get) => ({
|
const useFlowsManagerStore = create<FlowsManagerStoreType>((set, get) => ({
|
||||||
voiceModeEnabled: false,
|
|
||||||
setVoiceModeEnabled: (voiceModeEnabled: boolean) => set({ voiceModeEnabled }),
|
|
||||||
IOModalOpen: false,
|
IOModalOpen: false,
|
||||||
setIOModalOpen: (IOModalOpen: boolean) => {
|
setIOModalOpen: (IOModalOpen: boolean) => {
|
||||||
set({ IOModalOpen });
|
set({ IOModalOpen });
|
||||||
|
|
|
||||||
|
|
@ -29,8 +29,6 @@ export type FlowsManagerStoreType = {
|
||||||
IOModalOpen: boolean;
|
IOModalOpen: boolean;
|
||||||
setIOModalOpen: (IOModalOpen: boolean) => void;
|
setIOModalOpen: (IOModalOpen: boolean) => void;
|
||||||
resetStore: () => void;
|
resetStore: () => void;
|
||||||
voiceModeEnabled: boolean;
|
|
||||||
setVoiceModeEnabled: (voiceModeEnabled: boolean) => void;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type UseUndoRedoOptions = {
|
export type UseUndoRedoOptions = {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue