Skip to content

feat: conditionally render voice button based on voice mode state#8561

Merged
ogabrielluiz merged 12 commits into
mainfrom
update-audio-deps-and-feature
Jun 17, 2025
Merged

feat: conditionally render voice button based on voice mode state#8561
ogabrielluiz merged 12 commits into
mainfrom
update-audio-deps-and-feature

Conversation

@ogabrielluiz
Copy link
Copy Markdown
Contributor

@ogabrielluiz ogabrielluiz commented Jun 16, 2025

Summary by CodeRabbit

  • New Features

    • Added a "voice mode" feature flag to the application configuration, allowing conditional enabling or disabling of voice-related functionality.
    • The voice assistant button in the chat input now only appears if voice mode is enabled.
  • Bug Fixes

    • Prevented errors when voice mode dependencies are missing by conditionally loading related features and tests.
  • Chores

    • Made the "webrtcvad" dependency optional and grouped it under an "audio" optional install category.

@ogabrielluiz ogabrielluiz requested a review from Cristhianzl June 16, 2025 15:49
@dosubot dosubot Bot added the size:M This PR changes 30-99 lines, ignoring generated files. label Jun 16, 2025
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 16, 2025

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

The changes introduce a "voice mode" feature flag, conditionally enabling voice-related functionality based on the presence of the webrtcvad dependency. The backend exposes this flag in its config API, and the frontend consumes it to control the visibility and state of voice features. Dependency management is updated to make webrtcvad optional.

Changes

Files / Areas Change Summary
pyproject.toml, src/backend/base/pyproject.toml Updated mcp version; moved webrtcvad to an optional audio dependency group.
src/backend/base/langflow/api/utils.py Added get_voice_mode_enabled() utility to detect webrtcvad availability.
src/backend/base/langflow/api/v1/endpoints.py, .../schemas.py Added voice_mode_enabled to config response and schema.
src/backend/base/langflow/api/v1/init.py, .../router.py Made voice_mode_router import and inclusion conditional on availability.
src/backend/tests/unit/test_voice_mode.py Skips all tests if webrtcvad is not installed.
src/frontend/src/controllers/API/queries/config/use-get-config.ts Added voice_mode_enabled property to config response and updated state accordingly.
src/frontend/src/stores/flowsManagerStore.ts, .../types/zustand/flowsManager/index.ts Added voiceModeEnabled state and setter to the flows manager store and its type.
src/frontend/src/modals/IOModal/components/.../voice-button.tsx Conditionally renders the voice button based on voiceModeEnabled state.

Sequence Diagram(s)

sequenceDiagram
    participant Client (Frontend)
    participant API
    participant Utils

    Client->>API: GET /config
    API->>Utils: get_voice_mode_enabled()
    Utils-->>API: true/false
    API-->>Client: { ..., voice_mode_enabled: true/false }

    Client->>Store: setVoiceModeEnabled(voice_mode_enabled)
    Store-->>Client: Updates state

    Client->>VoiceButton: Render
    alt voice_mode_enabled = true
        VoiceButton->>Client: Render button
    else voice_mode_enabled = false
        VoiceButton->>Client: Render nothing
    end
Loading

Suggested labels

lgtm

Suggested reviewers

  • mfortman11
✨ Finishing Touches
🧪 Generate Unit Tests
  • Create PR with Unit Tests
  • Commit Unit Tests in branch update-audio-deps-and-feature
  • Post Copyable Unit Tests in Comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai auto-generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@dosubot dosubot Bot added the enhancement New feature or request label Jun 16, 2025
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 16, 2025

Caution

An unexpected error occurred while opening a pull request: Reference already exists - https://docs.github.com/rest/git/refs#create-a-reference

coderabbitai Bot added a commit that referenced this pull request Jun 16, 2025
Docstrings generation was requested by @ogabrielluiz.

* #8561 (comment)

The following files were modified:

* `src/backend/base/langflow/api/utils.py`
* `src/backend/base/langflow/api/v1/endpoints.py`
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 16, 2025

Note

Generated docstrings for this pull request at #8562

@github-actions github-actions Bot added enhancement New feature or request and removed enhancement New feature or request labels Jun 16, 2025
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (8)
src/backend/base/langflow/api/router.py (1)

61-62: Move conditional include before top-level registration for clarity.

router.include_router(router_v1) is executed ↑ earlier.
Although mutating router_v1 afterwards works (it’s the same object), placing the conditional block before registering avoids future surprises and mirrors how every other sub-router is added.

-router.include_router(router_v1)
-router.include_router(router_v2)
-
-if voice_mode_router is not None:
-    router_v1.include_router(voice_mode_router)
+if voice_mode_router is not None:
+    router_v1.include_router(voice_mode_router)
+
+router.include_router(router_v1)
+router.include_router(router_v2)
src/backend/tests/unit/test_voice_mode.py (1)

4-8: Prevent NameError after module-skip by stubbing webrtcvad.

When the import fails the module is skipped, but webrtcvad is later referenced inside test bodies.
Static type checkers – and IDEs that index the file without executing it – will flag an undefined name.

 except ImportError:
     pytestmark = pytest.mark.skip(reason="webrtcvad is not installed. Skipping voice mode tests.")
+    webrtcvad = None  # type: ignore  # keep name available for linters
src/backend/base/langflow/api/utils.py (1)

383-388: Cache the import result to avoid repeated ImportErrors

ImportError is relatively expensive and the function may be called on every /config request.
Because failed imports are not cached by the import machinery, the penalty is paid on every call when webrtcvad is missing. A one-shot check keeps the overhead at zero after the first call:

-def get_voice_mode_enabled() -> bool:
-    try:
-        import webrtcvad  # noqa: F401
-    except ImportError:
-        return False
-    return True
+from functools import lru_cache
+import importlib
+
+
+@lru_cache(maxsize=1)
+def get_voice_mode_enabled() -> bool:
+    """Return True when `webrtcvad` can be imported, result is memoised."""
+    return importlib.util.find_spec("webrtcvad") is not None
src/backend/base/langflow/api/v1/endpoints.py (1)

17-17: Import list getting long – group project imports

Very minor: now that the util import block includes get_voice_mode_enabled, consider grouping all langflow.api.utils symbols on one line or using an explicit # fmt: off/isort rule to keep this section tidy.

src/frontend/src/controllers/API/queries/config/use-get-config.ts (2)

24-25: Add JSDoc or inline comment for the new field

voice_mode_enabled is self-explanatory, but adding a short JSDoc (or comment) explaining that the backend sets it based on the presence of webrtcvad helps future readers who might not be aware of that coupling.


48-50: Missing shallow selector – each config fetch will trigger two re-renders

useFlowsManagerStore is invoked three times in this hook; each call without a selector helper such as shallow produces its own subscription and re-render. Consider:

-import useFlowsManagerStore from "@/stores/flowsManagerStore";
+import useFlowsManagerStore from "@/stores/flowsManagerStore";
+import { shallow } from "zustand/shallow";

and then

-const setVoiceModeEnabled = useFlowsManagerStore(
-  (state) => state.setVoiceModeEnabled,
-);
+const setVoiceModeEnabled = useFlowsManagerStore(
+  (state) => state.setVoiceModeEnabled,
+  shallow,
+);

Same applies to the two other useFlowsManagerStore usages above.
This avoids N independent subscriptions & re-renders each time /config is polled.

src/frontend/src/modals/IOModal/components/chatView/chatInput/components/voice-assistant/components/voice-button.tsx (2)

15-21: Early bail-out is OK, but hooks still run

useVoiceStore and useFlowsManagerStore execute even when the button won’t render.
This is perfectly valid (React rules of hooks) but wastes a tiny bit of work every keystroke.
Micro-optimisation: split into two components – a wrapper that checks voiceModeEnabled and the button itself – so the heavier hooks only mount when needed.


36-41: Accessibility: provide aria-label for the mic icon

Screen-reader users won’t get any context from a pure SVG. Add an aria-label or wrap the Button with it:

-        <Button
+        <Button
+          aria-label="Start voice recording"
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5c38d02 and 623e0a0.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (12)
  • pyproject.toml (2 hunks)
  • src/backend/base/langflow/api/router.py (1 hunks)
  • src/backend/base/langflow/api/utils.py (1 hunks)
  • src/backend/base/langflow/api/v1/__init__.py (1 hunks)
  • src/backend/base/langflow/api/v1/endpoints.py (2 hunks)
  • src/backend/base/langflow/api/v1/schemas.py (1 hunks)
  • src/backend/base/pyproject.toml (1 hunks)
  • src/backend/tests/unit/test_voice_mode.py (1 hunks)
  • src/frontend/src/controllers/API/queries/config/use-get-config.ts (3 hunks)
  • src/frontend/src/modals/IOModal/components/chatView/chatInput/components/voice-assistant/components/voice-button.tsx (2 hunks)
  • src/frontend/src/stores/flowsManagerStore.ts (1 hunks)
  • src/frontend/src/types/zustand/flowsManager/index.ts (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/backend/base/langflow/api/v1/endpoints.py (1)
src/backend/base/langflow/api/utils.py (1)
  • get_voice_mode_enabled (383-388)
⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: Ruff Style Check (3.13)
  • GitHub Check: Optimize new Python code in this PR
  • GitHub Check: Update Starter Projects
  • GitHub Check: Run Ruff Check and Format
🔇 Additional comments (4)
pyproject.toml (2)

109-109: Check potential breaking-changes from mcp>=1.9.4.

Jumping two major point releases (1.6.x1.9.x) could introduce backwards-incompatible API changes.
Please confirm that:

  1. The backend codebase still imports the same public symbols.
  2. Lockfiles / Docker images are updated.
  3. Integration tests that rely on MCP still pass.

If unsure, pin an upper bound until full validation:

-    "mcp>=1.9.4",
+    "mcp>=1.9.4,<2.0.0",

193-195: 👍 Optional “audio” extra is well-scoped.

Moving webrtcvad to extras_require keeps the default install light and prevents build failures on platforms without a C-compiler. Nice!

src/backend/base/langflow/api/v1/endpoints.py (1)

752-756: Avoid calling get_voice_mode_enabled() for every request

get_voice_mode_enabled() is pure and deterministic for a given interpreter session; invoking it once at startup (or memoising as suggested in utils.py) will shave a few µs off every /config call.

No action needed if you apply the memoisation change.

src/frontend/src/types/zustand/flowsManager/index.ts (1)

32-34: State naming is clear – good addition
The new voiceModeEnabled flag and its setter read cleanly and keep the store’s API self-explanatory.

Comment on lines +234 to +236
audio = [
"webrtcvad>=2.0.10",
]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Duplicate extra name across root & sub-package: keep them in sync.

Both projects now define an audio optional-dependency.
Make sure the extras expose IDENTICAL versions otherwise users installing langflow[audio] and langflow-base[audio] together may receive conflicting constraints.

Consider centralising the requirement in the workspace or referencing the parent via workspace = true.

🤖 Prompt for AI Agents
In src/backend/base/pyproject.toml around lines 234 to 236, the 'audio' optional
dependency is defined both in the root and sub-package with potentially
different versions, causing conflicts. To fix this, ensure the 'audio' extras in
both locations specify identical versions. Ideally, centralize the 'audio'
dependency version in one place, such as the root workspace, and reference it in
the sub-package using 'workspace = true' to keep them in sync and avoid version
conflicts.

public_flow_cleanup_interval: int
public_flow_expiration: int
event_delivery: Literal["polling", "streaming", "direct"]
voice_mode_enabled: bool
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Provide a default for voice_mode_enabled to avoid mandatory field bumps.

Without a default this becomes a required property, breaking any client/deserialization relying on older config payloads that do not carry the flag.

-    voice_mode_enabled: bool
+    voice_mode_enabled: bool = False

This keeps the API backward-compatible while still allowing the backend to overwrite the value when voice mode is actually enabled.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
voice_mode_enabled: bool
voice_mode_enabled: bool = False
🤖 Prompt for AI Agents
In src/backend/base/langflow/api/v1/schemas.py at line 391, the field
voice_mode_enabled is currently required because it lacks a default value. To
maintain backward compatibility and prevent breaking clients that do not include
this flag, add a default value (e.g., False) to voice_mode_enabled. This ensures
the field is optional during deserialization while allowing the backend to
override it when voice mode is enabled.

Comment on lines +18 to +21
try:
from langflow.api.v1.voice_mode import router as voice_mode_router
except ImportError:
voice_mode_router = None
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Exporting None via __all__ may surprise dynamic importers

When webrtcvad is absent voice_mode_router is None, yet it is still listed in __all__.
Code that does from langflow.api.v1 import * will receive a None instead of a router instance and may fail later with an AttributeError.

Either remove the name from __all__ when the import fails, or replace it with a no-op router stub implementing .include_router().

 except ImportError:
-    voice_mode_router = None
+    voice_mode_router = None
+
+if voice_mode_router is None:
+    __all__.remove("voice_mode_router")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
try:
from langflow.api.v1.voice_mode import router as voice_mode_router
except ImportError:
voice_mode_router = None
try:
from langflow.api.v1.voice_mode import router as voice_mode_router
except ImportError:
voice_mode_router = None
if voice_mode_router is None:
__all__.remove("voice_mode_router")
🤖 Prompt for AI Agents
In src/backend/base/langflow/api/v1/__init__.py around lines 18 to 21, the
variable voice_mode_router is set to None if the import fails but is still
included in __all__, causing issues for dynamic importers expecting a router
instance. To fix this, modify the code to either remove voice_mode_router from
__all__ when the import fails or replace voice_mode_router with a stub object
that implements a no-op include_router() method, ensuring that imports from
langflow.api.v1 do not break due to a None value.

Comment on lines +20 to 22
voiceModeEnabled: false,
setVoiceModeEnabled: (voiceModeEnabled: boolean) => set({ voiceModeEnabled }),
IOModalOpen: false,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Reset handler forgets to clear voiceModeEnabled

resetStore() (lines 135-143) resets many flags but leaves the newly-added voiceModeEnabled untouched, so a user that toggles voice-mode may see stale state after logout / project switch.

 resetStore: () => {
   set({
+    voiceModeEnabled: false,
     flows: [],
     currentFlow: undefined,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
voiceModeEnabled: false,
setVoiceModeEnabled: (voiceModeEnabled: boolean) => set({ voiceModeEnabled }),
IOModalOpen: false,
// … elsewhere in flowsManagerStore.ts …
resetStore: () => {
set({
voiceModeEnabled: false,
flows: [],
currentFlow: undefined,
IOModalOpen: false,
// … any other flags or state to reset …
});
},
// … rest of store definition …
🤖 Prompt for AI Agents
In src/frontend/src/stores/flowsManagerStore.ts around lines 135 to 143, the
resetStore() function does not reset the voiceModeEnabled flag, causing stale
state after logout or project switch. Update resetStore() to explicitly set
voiceModeEnabled back to false along with the other flags to ensure the state is
fully cleared.

Comment on lines 71 to 73
setEventDelivery(data.event_delivery ?? EventDeliveryType.POLLING);
setVoiceModeEnabled(data.voice_mode_enabled);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Defensive cast avoids undefined → boolean mismatch

voice_mode_enabled might be missing when hitting an older backend or on failure; passing undefined to the store could break components that assume a strict boolean.

- setVoiceModeEnabled(data.voice_mode_enabled);
+ setVoiceModeEnabled(Boolean(data.voice_mode_enabled));

Alternatively default to false at destructuring:

const {
  // ...
  voice_mode_enabled = false,
} = data;
setVoiceModeEnabled(voice_mode_enabled);
🤖 Prompt for AI Agents
In src/frontend/src/controllers/API/queries/config/use-get-config.ts around
lines 71 to 73, the value passed to setVoiceModeEnabled can be undefined if
voice_mode_enabled is missing from data, which may cause issues in components
expecting a boolean. To fix this, ensure voice_mode_enabled defaults to false
either by setting a default value during destructuring from data or by
explicitly passing false when voice_mode_enabled is undefined before calling
setVoiceModeEnabled.

@github-actions github-actions Bot added enhancement New feature or request and removed enhancement New feature or request labels Jun 16, 2025
- Updated webrtcvad version requirement to >=1.9.4 in pyproject.toml and uv.lock.
- Moved webrtcvad to optional dependencies under the new 'audio' category in both pyproject.toml files for better modularity.
- Ensured compatibility with existing dependencies while enhancing documentation for optional audio features.
- Updated the API router initialization to conditionally include the voice_mode_router if it is available, improving modularity and preventing import errors.
- Adjusted the import statement for voice_mode_router to handle ImportError gracefully, ensuring the application remains robust in its absence.
- Introduced a new function `get_voice_mode_enabled` to check for the availability of the `webrtcvad` library, enhancing the API's capability to support voice mode features.
- Updated the `ConfigResponse` schema to include `voice_mode_enabled` flag, allowing clients to query the status of voice mode support.
- Integrated the voice mode feature flag into the `get_config` endpoint response, improving the configurability of the API.
- Added a conditional import for the webrtcvad library in the voice mode test suite.
- Implemented a pytest marker to skip tests when webrtcvad is unavailable, enhancing test robustness and preventing unnecessary failures.
- Introduced `voiceModeEnabled` state and `setVoiceModeEnabled` action in the flows manager store to manage the voice mode feature.
- Updated the `ConfigResponse` interface and `useGetConfig` hook to integrate the new voice mode state, allowing for dynamic configuration updates.
- Enhanced the API's configurability by ensuring the voice mode state is properly set based on the API response.
- Integrated `voiceModeEnabled` from the flows manager store to control the visibility of the VoiceButton component.
- The VoiceButton will now return null if voice mode is not enabled, enhancing the user interface by preventing unnecessary rendering.
- Updated the `setVoiceModeEnabled` function in the `useGetConfig` hook to explicitly convert the `voice_mode_enabled` value to a boolean, ensuring consistent handling of the voice mode state based on API responses.
- Updated the API router to conditionally include the voice_mode_router based on its availability, enhancing modularity and preventing import errors.
- Adjusted the __init__.py file to dynamically add voice_mode_router to the __all__ list if the import is successful, improving the API's robustness.
@ogabrielluiz ogabrielluiz force-pushed the update-audio-deps-and-feature branch from 3b20efb to 6939f57 Compare June 17, 2025 13:14
@github-actions github-actions Bot added enhancement New feature or request and removed enhancement New feature or request labels Jun 17, 2025
@github-actions github-actions Bot added enhancement New feature or request and removed enhancement New feature or request labels Jun 17, 2025
@github-actions github-actions Bot added enhancement New feature or request and removed enhancement New feature or request labels Jun 17, 2025
@ogabrielluiz ogabrielluiz enabled auto-merge June 17, 2025 14:07
Copy link
Copy Markdown
Member

@Cristhianzl Cristhianzl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@dosubot dosubot Bot added the lgtm This PR has been approved by a maintainer label Jun 17, 2025
@Cristhianzl
Copy link
Copy Markdown
Member

Cristhianzl commented Jun 17, 2025

awesome @ogabrielluiz
thank you!

@github-actions github-actions Bot added enhancement New feature or request and removed enhancement New feature or request labels Jun 17, 2025
codeflash-ai Bot added a commit that referenced this pull request Jun 17, 2025
…(`update-audio-deps-and-feature`)

Here is your optimized Python program. Your profiling data shows the majority of time is spent **importing `webrtcvad` every time** the function is called. In Python, imports are cached, but the lookup itself is still relatively expensive in repeated function calls.

**Optimization:**  
- Attempt the import **once** at module load, store the result, and have the function just return the cached result.  
- This makes the *per-call* cost essentially zero after the module is first loaded.

Here's the rewritten code.



**Why is this faster?**  
- The expensive `import` or exception check is performed **once only.**
- Each subsequent call to `get_voice_mode_enabled` is just a return statement, which is extremely fast.

Let me know if you need further optimizations or analysis!
Comment on lines +384 to +388
try:
import webrtcvad # noqa: F401
except ImportError:
return False
return True
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚡️Codeflash found 68,042% (680.42x) speedup for get_voice_mode_enabled in src/backend/base/langflow/api/utils.py

⏱️ Runtime : 64.7 milliseconds 94.9 microseconds (best of 34 runs)

📝 Explanation and details Here is your optimized Python program. Your profiling data shows the majority of time is spent **importing `webrtcvad` every time** the function is called. In Python, imports are cached, but the lookup itself is still relatively expensive in repeated function calls.

Optimization:

  • Attempt the import once at module load, store the result, and have the function just return the cached result.
  • This makes the per-call cost essentially zero after the module is first loaded.

Here's the rewritten code.

Why is this faster?

  • The expensive import or exception check is performed once only.
  • Each subsequent call to get_voice_mode_enabled is just a return statement, which is extremely fast.

Let me know if you need further optimizations or analysis!

Correctness verification report:

Test Status
⚙️ Existing Unit Tests 🔘 None Found
🌀 Generated Regression Tests 717 Passed
⏪ Replay Tests 🔘 None Found
🔎 Concolic Coverage Tests 🔘 None Found
📊 Tests Coverage 80.0%
🌀 Generated Regression Tests Details
from __future__ import annotations

# imports
import pytest  # used for our unit tests
from langflow.api.utils import get_voice_mode_enabled

# unit tests

# --------------------
# BASIC TEST CASES
# --------------------

























from __future__ import annotations

import sys
import types

# imports
import pytest  # used for our unit tests
from langflow.api.utils import get_voice_mode_enabled

# ------------------ BASIC TEST CASES ------------------

def test_voice_mode_enabled_true():
    """voice_mode module exists and ENABLED is True."""
    module = types.ModuleType("voice_mode")
    module.ENABLED = True
    sys.modules["voice_mode"] = module
    codeflash_output = get_voice_mode_enabled() # 166μs -> 570ns

def test_voice_mode_enabled_false():
    """voice_mode module exists and ENABLED is False."""
    module = types.ModuleType("voice_mode")
    module.ENABLED = False
    sys.modules["voice_mode"] = module
    codeflash_output = get_voice_mode_enabled() # 130μs -> 381ns

def test_voice_mode_enabled_missing_module():
    """voice_mode module does not exist."""
    sys.modules.pop("voice_mode", None)
    # Remove from sys.modules and ensure import will fail
    # If importlib is used, patching sys.modules is enough for this function
    codeflash_output = get_voice_mode_enabled() # 130μs -> 300ns

def test_voice_mode_enabled_missing_enabled_attribute():
    """voice_mode module exists but does not have ENABLED attribute."""
    module = types.ModuleType("voice_mode")
    # No ENABLED attribute set
    sys.modules["voice_mode"] = module
    codeflash_output = get_voice_mode_enabled() # 128μs -> 321ns

# ------------------ EDGE TEST CASES ------------------

def test_voice_mode_enabled_true_string():
    """voice_mode.ENABLED is the string 'True' (should be True)."""
    module = types.ModuleType("voice_mode")
    module.ENABLED = "True"
    sys.modules["voice_mode"] = module
    codeflash_output = get_voice_mode_enabled() # 125μs -> 351ns

def test_voice_mode_enabled_false_string():
    """voice_mode.ENABLED is the string 'False' (should be True, since bool('False') is True)."""
    module = types.ModuleType("voice_mode")
    module.ENABLED = "False"
    sys.modules["voice_mode"] = module
    # bool("False") is True, so this should return True
    codeflash_output = get_voice_mode_enabled() # 125μs -> 370ns

def test_voice_mode_enabled_zero():
    """voice_mode.ENABLED is 0 (should be False)."""
    module = types.ModuleType("voice_mode")
    module.ENABLED = 0
    sys.modules["voice_mode"] = module
    codeflash_output = get_voice_mode_enabled() # 126μs -> 351ns

def test_voice_mode_enabled_none():
    """voice_mode.ENABLED is None (should be False)."""
    module = types.ModuleType("voice_mode")
    module.ENABLED = None
    sys.modules["voice_mode"] = module
    codeflash_output = get_voice_mode_enabled() # 125μs -> 351ns

def test_voice_mode_enabled_empty_list():
    """voice_mode.ENABLED is an empty list (should be False)."""
    module = types.ModuleType("voice_mode")
    module.ENABLED = []
    sys.modules["voice_mode"] = module
    codeflash_output = get_voice_mode_enabled() # 125μs -> 300ns

def test_voice_mode_enabled_nonempty_list():
    """voice_mode.ENABLED is a non-empty list (should be True)."""
    module = types.ModuleType("voice_mode")
    module.ENABLED = [1]
    sys.modules["voice_mode"] = module
    codeflash_output = get_voice_mode_enabled() # 125μs -> 341ns

def test_voice_mode_enabled_custom_object_true():
    """voice_mode.ENABLED is a custom object with __bool__ returning True."""
    class Truthy:
        def __bool__(self):
            return True
    module = types.ModuleType("voice_mode")
    module.ENABLED = Truthy()
    sys.modules["voice_mode"] = module
    codeflash_output = get_voice_mode_enabled()

def test_voice_mode_enabled_custom_object_false():
    """voice_mode.ENABLED is a custom object with __bool__ returning False."""
    class Falsy:
        def __bool__(self):
            return False
    module = types.ModuleType("voice_mode")
    module.ENABLED = Falsy()
    sys.modules["voice_mode"] = module
    codeflash_output = get_voice_mode_enabled()


def test_voice_mode_enabled_enabled_is_callable():
    """voice_mode.ENABLED is a callable object (should be True)."""
    module = types.ModuleType("voice_mode")
    module.ENABLED = lambda: True
    sys.modules["voice_mode"] = module
    codeflash_output = get_voice_mode_enabled() # 133μs -> 440ns

def test_voice_mode_enabled_enabled_is_object_without_bool():
    """voice_mode.ENABLED is an object without __bool__ or __len__ (should be True)."""
    class NoBool:
        pass
    module = types.ModuleType("voice_mode")
    module.ENABLED = NoBool()
    sys.modules["voice_mode"] = module
    codeflash_output = get_voice_mode_enabled() # 130μs -> 391ns

# ------------------ LARGE SCALE TEST CASES ------------------

def test_voice_mode_enabled_many_modules():
    """Test that the presence of many other modules does not affect result."""
    # Add many dummy modules to sys.modules
    for i in range(1000):
        sys.modules[f"dummy_mod_{i}"] = types.ModuleType(f"dummy_mod_{i}")
    module = types.ModuleType("voice_mode")
    module.ENABLED = True
    sys.modules["voice_mode"] = module
    codeflash_output = get_voice_mode_enabled() # 130μs -> 470ns
    # Clean up dummy modules
    for i in range(1000):
        sys.modules.pop(f"dummy_mod_{i}", None)

def test_voice_mode_enabled_large_attribute_list():
    """voice_mode has many attributes, ENABLED is True."""
    module = types.ModuleType("voice_mode")
    for i in range(1000):
        setattr(module, f"attr_{i}", i)
    module.ENABLED = True
    sys.modules["voice_mode"] = module
    codeflash_output = get_voice_mode_enabled() # 130μs -> 431ns

def test_voice_mode_enabled_large_attribute_list_enabled_false():
    """voice_mode has many attributes, ENABLED is False."""
    module = types.ModuleType("voice_mode")
    for i in range(1000):
        setattr(module, f"attr_{i}", i)
    module.ENABLED = False
    sys.modules["voice_mode"] = module
    codeflash_output = get_voice_mode_enabled() # 130μs -> 440ns

def test_voice_mode_enabled_multiple_calls_consistency():
    """Repeated calls should be consistent and not leak state."""
    module = types.ModuleType("voice_mode")
    module.ENABLED = True
    sys.modules["voice_mode"] = module
    for _ in range(100):
        codeflash_output = get_voice_mode_enabled() # 87.3μs -> 120ns
    module.ENABLED = False
    for _ in range(100):
        codeflash_output = get_voice_mode_enabled() # 87.3μs -> 120ns

def test_voice_mode_enabled_performance_large_scale():
    """Performance: call function 500 times with large sys.modules."""
    for i in range(900):
        sys.modules[f"dummy_mod_{i}"] = types.ModuleType(f"dummy_mod_{i}")
    module = types.ModuleType("voice_mode")
    module.ENABLED = True
    sys.modules["voice_mode"] = module
    for _ in range(500):
        codeflash_output = get_voice_mode_enabled() # 87.6μs -> 120ns
    # Clean up dummy modules
    for i in range(900):
        sys.modules.pop(f"dummy_mod_{i}", None)
# codeflash_output is used to check that the output of the original code is the same as that of the optimized code.

To test or edit this optimization locally git merge codeflash/optimize-pr8561-2025-06-17T15.19.38

Suggested change
try:
import webrtcvad # noqa: F401
except ImportError:
return False
return True
return _VOICE_MODE_ENABLED
_VOICE_MODE_ENABLED = False

@ogabrielluiz ogabrielluiz added this pull request to the merge queue Jun 17, 2025
Merged via the queue into main with commit 4d3eae6 Jun 17, 2025
67 checks passed
@ogabrielluiz ogabrielluiz deleted the update-audio-deps-and-feature branch June 17, 2025 15:33
jordanrfrazier pushed a commit that referenced this pull request Jun 17, 2025
)

* chore: update webrtcvad dependency and add optional audio support

- Updated webrtcvad version requirement to >=1.9.4 in pyproject.toml and uv.lock.
- Moved webrtcvad to optional dependencies under the new 'audio' category in both pyproject.toml files for better modularity.
- Ensured compatibility with existing dependencies while enhancing documentation for optional audio features.

* fix: handle optional import of voice_mode_router in API initialization

- Updated the API router initialization to conditionally include the voice_mode_router if it is available, improving modularity and preventing import errors.
- Adjusted the import statement for voice_mode_router to handle ImportError gracefully, ensuring the application remains robust in its absence.

* feat: add voice mode flag to API configuration

- Introduced a new function `get_voice_mode_enabled` to check for the availability of the `webrtcvad` library, enhancing the API's capability to support voice mode features.
- Updated the `ConfigResponse` schema to include `voice_mode_enabled` flag, allowing clients to query the status of voice mode support.
- Integrated the voice mode feature flag into the `get_config` endpoint response, improving the configurability of the API.

* test: skip voice mode tests if webrtcvad is not installed

- Added a conditional import for the webrtcvad library in the voice mode test suite.
- Implemented a pytest marker to skip tests when webrtcvad is unavailable, enhancing test robustness and preventing unnecessary failures.

* feat: add voice mode state management to flows manager store

- Introduced `voiceModeEnabled` state and `setVoiceModeEnabled` action in the flows manager store to manage the voice mode feature.
- Updated the `ConfigResponse` interface and `useGetConfig` hook to integrate the new voice mode state, allowing for dynamic configuration updates.
- Enhanced the API's configurability by ensuring the voice mode state is properly set based on the API response.

* feat: conditionally render voice button based on voice mode state

- Integrated `voiceModeEnabled` from the flows manager store to control the visibility of the VoiceButton component.
- The VoiceButton will now return null if voice mode is not enabled, enhancing the user interface by preventing unnecessary rendering.

* fix: ensure boolean conversion for voice mode state in config hook

- Updated the `setVoiceModeEnabled` function in the `useGetConfig` hook to explicitly convert the `voice_mode_enabled` value to a boolean, ensuring consistent handling of the voice mode state based on API responses.

* refactor: conditionally import voice_mode_router in API initialization

- Updated the API router to conditionally include the voice_mode_router based on its availability, enhancing modularity and preventing import errors.
- Adjusted the __init__.py file to dynamically add voice_mode_router to the __all__ list if the import is successful, improving the API's robustness.

* chore: revert mcp version update

* refactor: reorder router initialization and add audio flag to backend install task

* feat: add audio module to backend installation command in VS Code tasks

* ci: add audio extras to uv sync in frontend tests
@ogabrielluiz ogabrielluiz mentioned this pull request Jun 18, 2025
ogabrielluiz added a commit to bkatya2001/langflow that referenced this pull request Jun 24, 2025
…ngflow-ai#8561)

* chore: update webrtcvad dependency and add optional audio support

- Updated webrtcvad version requirement to >=1.9.4 in pyproject.toml and uv.lock.
- Moved webrtcvad to optional dependencies under the new 'audio' category in both pyproject.toml files for better modularity.
- Ensured compatibility with existing dependencies while enhancing documentation for optional audio features.

* fix: handle optional import of voice_mode_router in API initialization

- Updated the API router initialization to conditionally include the voice_mode_router if it is available, improving modularity and preventing import errors.
- Adjusted the import statement for voice_mode_router to handle ImportError gracefully, ensuring the application remains robust in its absence.

* feat: add voice mode flag to API configuration

- Introduced a new function `get_voice_mode_enabled` to check for the availability of the `webrtcvad` library, enhancing the API's capability to support voice mode features.
- Updated the `ConfigResponse` schema to include `voice_mode_enabled` flag, allowing clients to query the status of voice mode support.
- Integrated the voice mode feature flag into the `get_config` endpoint response, improving the configurability of the API.

* test: skip voice mode tests if webrtcvad is not installed

- Added a conditional import for the webrtcvad library in the voice mode test suite.
- Implemented a pytest marker to skip tests when webrtcvad is unavailable, enhancing test robustness and preventing unnecessary failures.

* feat: add voice mode state management to flows manager store

- Introduced `voiceModeEnabled` state and `setVoiceModeEnabled` action in the flows manager store to manage the voice mode feature.
- Updated the `ConfigResponse` interface and `useGetConfig` hook to integrate the new voice mode state, allowing for dynamic configuration updates.
- Enhanced the API's configurability by ensuring the voice mode state is properly set based on the API response.

* feat: conditionally render voice button based on voice mode state

- Integrated `voiceModeEnabled` from the flows manager store to control the visibility of the VoiceButton component.
- The VoiceButton will now return null if voice mode is not enabled, enhancing the user interface by preventing unnecessary rendering.

* fix: ensure boolean conversion for voice mode state in config hook

- Updated the `setVoiceModeEnabled` function in the `useGetConfig` hook to explicitly convert the `voice_mode_enabled` value to a boolean, ensuring consistent handling of the voice mode state based on API responses.

* refactor: conditionally import voice_mode_router in API initialization

- Updated the API router to conditionally include the voice_mode_router based on its availability, enhancing modularity and preventing import errors.
- Adjusted the __init__.py file to dynamically add voice_mode_router to the __all__ list if the import is successful, improving the API's robustness.

* chore: revert mcp version update

* refactor: reorder router initialization and add audio flag to backend install task

* feat: add audio module to backend installation command in VS Code tasks

* ci: add audio extras to uv sync in frontend tests
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request lgtm This PR has been approved by a maintainer size:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ERROR: webrtcvad [BUG] Unable to install langflow - Failed to build webrtcvad==2.0.10

2 participants