Skip to content

Feat/local omi scripts#129

Merged
AnkushMalaker merged 8 commits intomainfrom
feat/local-omi-scripts
Oct 20, 2025
Merged

Feat/local omi scripts#129
AnkushMalaker merged 8 commits intomainfrom
feat/local-omi-scripts

Conversation

@AnkushMalaker
Copy link
Collaborator

@AnkushMalaker AnkushMalaker commented Oct 20, 2025

Summary by CodeRabbit

  • New Features

    • Added OMI Bluetooth audio device connectivity with streaming support
    • Added Speaker Recognition service setup wizard with interactive configuration
  • Documentation

    • New comprehensive Getting Started guide for backend deployment
    • Reorganized documentation with API-first reference approach
    • Updated setup process guidance and references
  • Chores

    • Enhanced environment configuration handling
    • Updated Docker platform specifications for services
    • Updated package dependencies

- Introduced a comprehensive 'Getting Started' guide in `Docs/getting-started.md` to assist new users with setup and usage of the Friend-Lite backend.
- Updated initialization script references in `Docs/init-system.md` to reflect the new `wizard.py` instead of `init.py`.
- Added new local OMI Bluetooth connection scripts in `extras/local-omi-bt/` for improved audio streaming capabilities.
- Removed the deprecated `connect-laptop.py` script and replaced it with `connect-omi.py` for better functionality.
- Updated dependencies in `requirements.txt` to include new libraries for enhanced audio processing and backend communication.
- Added a new `start.sh` script for easier execution of the local OMI connection process.
@AnkushMalaker
Copy link
Collaborator Author

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 20, 2025

Important

Review skipped

Auto 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

This PR implements a comprehensive system reorganization: migrating from init.py to wizard.py as the root orchestrator, replacing inline environment configuration with template-based .env management via python-dotenv, removing the nginx configuration file, adding OMI Bluetooth device support with streaming modules, introducing an interactive setup system for speaker-recognition, and updating documentation accordingly.

Changes

Cohort / File(s) Summary
System Orchestration & Setup
wizard.py, backends/advanced/init.py
Migrated to python-dotenv for environment management; wizard.py updated to use get_key for env-file parsing and handle speaker-recognition setup via init.py; backends/advanced/init.py refactored to use template-based .env generation and dotenv.set_key for atomic updates
Root Setup Requirements
setup-requirements.txt, backends/advanced/setup-requirements.txt
Replaced dotenv with python-dotenv package; backends/advanced added python-dotenv dependency
Documentation Updates
CLAUDE.md, Docs/getting-started.md, Docs/init-system.md, quickstart.md
Reorganized CLAUDE.md to API-first orientation with quick reference; added comprehensive getting-started.md guide; updated init-system and quickstart references from init.py to wizard.py
Infrastructure Configuration
.gitignore, backends/advanced/nginx.conf
Added nginx.conf ignore patterns to .gitignore; removed entire backends/advanced/nginx.conf (reverse-proxy, TLS, upstreams, routes)
OMI Bluetooth Integration
extras/local-omi-bt/connect-omi.py, extras/local-omi-bt/send_to_adv.py, extras/local-omi-bt/requirements.txt, extras/local-omi-bt/start.sh, extras/local-omi-bt/README.md
Added new Bluetooth audio pipeline: connect-omi.py (BLE device discovery, Opus decoding, async audio streaming), send_to_adv.py (Wyoming-protocol backend streaming with JWT authentication), updated requirements with bleak, opuslib, asyncstdlib, websockets, and related dependencies
Speaker Recognition Setup
extras/speaker-recognition/init.py, extras/speaker-recognition/setup-requirements.txt, extras/speaker-recognition/docker-compose.yml, extras/speaker-recognition/webui/Dockerfile
Added interactive setup script with HF token, compute mode, Deepgram API, and HTTPS/SSL configuration; updated docker-compose to specify linux/amd64 platform; clarified Dockerfile EXPOSE with runtime port documentation

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant BLE as BLE Device<br/>(OMI)
    participant connect as connect-omi.py<br/>(Async Pipeline)
    participant send as send_to_adv.py<br/>(Backend Stream)
    participant backend as Friend-Lite<br/>Backend

    User->>connect: Start connect-omi.py
    connect->>connect: Initialize (load OMI_MAC or discover)
    connect->>BLE: Connect via BleakClient
    BLE-->>connect: Audio stream (Opus packets)
    
    rect rgb(100, 200, 100)
    Note over connect: Decode & Enqueue
    connect->>connect: Decode Opus→PCM (audio_queue)
    connect->>connect: RollingFileSink (local storage)
    end
    
    connect->>send: Pass AudioChunk stream
    send->>send: Authenticate (JWT via ADMIN_EMAIL/PASSWORD)
    send->>backend: POST /auth/login
    backend-->>send: JWT token
    
    rect rgb(100, 150, 255)
    Note over send: Stream via WebSocket
    send->>backend: WebSocket connect (token + device name)
    send->>backend: audio-start message
    loop Every AudioChunk
        send->>backend: audio-chunk (metadata + binary)
    end
    send->>backend: audio-stop message
    end
    
    backend-->>send: ✓ Audio processed
    send-->>User: Streaming complete
Loading
sequenceDiagram
    actor User
    participant wizard as wizard.py<br/>(Root Orchestrator)
    participant backend as backends/advanced<br/>init.py
    participant speaker as extras/speaker-<br/>recognition/init.py
    participant env as Environment<br/>(.env files)

    User->>wizard: python wizard.py [--https] [args]
    wizard->>wizard: Parse CLI arguments
    
    rect rgb(200, 150, 100)
    Note over wizard: Setup Each Service
    wizard->>backend: uv run --with-requirements init.py
    backend->>env: Load .env.template → .env
    backend->>env: Update via dotenv.set_key()
    backend-->>wizard: ✓ Backend configured
    end
    
    wizard->>speaker: uv run --with-requirements init.py [--https] [--compute-mode]
    
    rect rgb(150, 200, 150)
    Note over speaker: Interactive Setup
    speaker->>User: Prompt HF token
    speaker->>User: Prompt compute mode (cpu/gpu)
    speaker->>User: Prompt Deepgram API key
    speaker->>User: Prompt HTTPS (yes/no)
    User-->>speaker: Responses
    
    speaker->>env: Backup existing .env
    speaker->>env: Generate from template
    speaker->>env: Apply values via set_key()
    speaker->>env: chmod 0600
    end
    
    speaker-->>wizard: ✓ Speaker Recognition setup
    wizard->>User: Display summary & next steps
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

The changes span multiple interconnected systems with heterogeneous modifications: new async Bluetooth pipeline with audio codecs, interactive setup orchestration, environment configuration refactoring, and documentation reorganization. While individual cohorts follow consistent patterns (e.g., template-based .env generation), the diversity of affected subsystems and non-trivial logic density (async BLE handling, JWT authentication, device discovery, template rendering) demand separate reasoning for distinct components.

Possibly related PRs

  • Better onboarding #86: Conflicting changes to Docs/init-system.md regarding root orchestrator reference migration (init.py vs wizard.py); coordinate merge strategy.
  • Better init system #74: Related system setup reorganization affecting root orchestration, backend init scripts, and setup workflow; ensures consistency in wizard vs init nomenclature and .env management patterns.

Poem

🐰✨ A wizard once tamed the OMI so blue,
With Bluetooth and Opus, the audio flew,
Template-based .env, no more inline strife,
And speaker recognition now sparkles with life!
Setup flows smooth, the backend aligned,
A rabbit's delight—configuration refined! 🎵

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "Feat/local omi scripts" directly references the addition of new local OMI Bluetooth integration scripts in the extras/local-omi-bt/ directory, which is a significant and real component of this changeset. The PR includes multiple new files in this directory (connect-omi.py, send_to_adv.py, start.sh) that provide end-to-end OMI device discovery, Bluetooth audio streaming, and backend integration. While the PR also contains other substantial changes including documentation updates, setup refactoring, and infrastructure modifications, the title appropriately highlights a core feature addition that is clearly present and substantial in the changeset.
Docstring Coverage ✅ Passed Docstring coverage is 81.08% which is sufficient. The required threshold is 80.00%.

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

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 20, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
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: 14

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
extras/speaker-recognition/docker-compose.yml (1)

58-85: Port defaults and healthcheck scheme

  • Provide defaults to avoid empty env failures.
  • Healthcheck should respect REACT_UI_HTTPS.

Apply:

   web-ui:
     platform: linux/amd64
@@
-    ports:
-      - "${REACT_UI_PORT}:${REACT_UI_PORT}"
+    ports:
+      - "${REACT_UI_PORT:-5173}:${REACT_UI_PORT:-5173}"
@@
-    healthcheck:
-      test: ["CMD", "curl", "-f", "-k", "https://localhost:${REACT_UI_PORT}"]
+    healthcheck:
+      test: ["CMD-SHELL", "if [ \"${REACT_UI_HTTPS:-false}\" = \"true\" ]; then curl -f -k \"https://localhost:${REACT_UI_PORT:-5173}\"; else curl -f \"http://localhost:${REACT_UI_PORT:-5173}\"; fi"]
       interval: 30s
       timeout: 10s
       retries: 3

Optional: note that pinning linux/amd64 may run under emulation on Apple Silicon. Consider aarch64 builds later.

wizard.py (1)

152-177: Don’t pass secrets via CLI args; use env for subprocess

CLI args expose tokens (ps, logs). Pass via env and import os.

Apply:

@@
-from dotenv import get_key
+import os
+from dotenv import get_key
@@
-        if service_name == 'speaker-recognition':
+        if service_name == 'speaker-recognition':
             # Pass Deepgram API key from backend if available
             backend_env_path = 'backends/advanced/.env'
             deepgram_key = read_env_value(backend_env_path, 'DEEPGRAM_API_KEY')
-            if deepgram_key and deepgram_key != 'your_deepgram_api_key_here':
-                cmd.extend(['--deepgram-api-key', deepgram_key])
+            env = os.environ.copy()
+            if deepgram_key and deepgram_key != 'your_deepgram_api_key_here':
+                env['DEEPGRAM_API_KEY'] = deepgram_key
@@
             speaker_env_path = 'extras/speaker-recognition/.env'
             hf_token = read_env_value(speaker_env_path, 'HF_TOKEN')
-            if hf_token and hf_token != 'your_huggingface_token_here':
-                cmd.extend(['--hf-token', hf_token])
+            if hf_token and hf_token != 'your_huggingface_token_here':
+                env['HF_TOKEN'] = hf_token
@@
             compute_mode = read_env_value(speaker_env_path, 'COMPUTE_MODE')
             if compute_mode in ['cpu', 'gpu']:
-                cmd.extend(['--compute-mode', compute_mode])
+                env['COMPUTE_MODE'] = compute_mode
@@
-        result = subprocess.run(
+        result = subprocess.run(
             cmd, 
             cwd=service['path'],
             check=True,
-            timeout=300  # 5 minute timeout for service setup
+            timeout=300,  # 5 minute timeout for service setup
+            env=env if 'env' in locals() else None
         )

Downstream init.py should read from env. If it requires flags today, consider supporting both for transition.

Also applies to: 187-192, 12-12

🧹 Nitpick comments (13)
.gitignore (1)

76-76: Addition aligns with nginx removal in this PR.

Adding backends/advanced/nginx.conf to .gitignore is correct and ensures the removed configuration file won't be accidentally re-tracked.

Consider grouping this pattern with the related nginx ignore at line 54 (extras/speaker-recognition/nginx.conf) or with other backends/advanced/* patterns (lines 34–35, 51) for better organization.

extras/speaker-recognition/setup-requirements.txt (1)

3-3: Consider pinning the python-dotenv version for reproducibility.

The unpinned python-dotenv>=1.0.0 allows any future version, which could introduce breaking changes. Based on learnings, the latest stable version is 1.1.1 (released Jun 24, 2025).

Apply this diff to pin to a specific version or use a compatible release operator:

-python-dotenv>=1.0.0
+python-dotenv==1.1.1

Or use compatible release to allow patch updates:

-python-dotenv>=1.0.0
+python-dotenv~=1.1.0
backends/advanced/setup-requirements.txt (1)

4-4: Add version constraint for python-dotenv.

The dependency lacks any version specification, which could lead to compatibility issues. Based on learnings, python-dotenv 1.1.1 is the latest stable version (released Jun 24, 2025).

Apply this diff to add a version constraint:

-python-dotenv
+python-dotenv>=1.0.0

Or pin to a specific version for maximum reproducibility:

-python-dotenv
+python-dotenv==1.1.1
extras/local-omi-bt/requirements.txt (1)

1-10: Add version constraints to unpinned dependencies.

Several critical dependencies lack version constraints, which could lead to compatibility issues. Based on learnings:

  • httpx latest: 0.28.1
  • websockets latest: 15.0.1
  • python-dotenv latest: 1.1.1

Apply this diff to add version constraints:

 bleak==0.22.3
 numpy>=1.26.4
 scipy>=1.12.0
 opuslib>=3.0.1
 friend-lite-sdk
-easy_audio_interfaces
-python-dotenv
-asyncstdlib
-httpx
-websockets
+easy_audio_interfaces>=0.7.0
+python-dotenv>=1.0.0
+asyncstdlib>=3.12.0
+httpx>=0.28.0
+websockets>=15.0.0
Docs/getting-started.md (2)

317-317: Fix hyphenation for compound adjective.

The phrase "1.5 minute collection" should be hyphenated when used as a compound adjective modifying "collection."

Apply this diff:

-- **Collection timeout**: 1.5 minute collection for optimal online processing quality
+- **Collection timeout**: 1.5-minute collection for optimal online processing quality

46-46: Optional: Add language specifier to code block and convert bare URL.

Minor markdown style improvements flagged by static analysis:

  • Line 46: Fenced code block missing language identifier
  • Line 530: Bare URL should be formatted as link

These are nitpicks and can be addressed later if desired.

Also applies to: 530-530

extras/local-omi-bt/send_to_adv.py (3)

66-66: Use logging.exception for better error diagnostics.

When logging within exception handlers, logging.exception() automatically includes the stack trace, which is more helpful for debugging than logging.error().

Apply this diff:

     except httpx.TimeoutException:
-        logger.error("Authentication request timed out")
+        logger.exception("Authentication request timed out")
         return None
     except httpx.RequestError as e:
-        logger.error(f"Authentication request failed: {e}")
+        logger.exception(f"Authentication request failed: {e}")
         return None
     except Exception as e:
-        logger.error(f"Unexpected authentication error: {e}")
+        logger.exception(f"Unexpected authentication error: {e}")
         return None

Also applies to: 69-69, 72-72


71-73: Avoid catching bare Exception.

Catching Exception is too broad and can hide unexpected errors. Consider catching only specific exceptions or letting unexpected exceptions propagate.

If you need to catch unexpected errors, at least re-raise after logging:

-    except Exception as e:
-        logger.exception(f"Unexpected authentication error: {e}")
-        return None
+    except Exception:
+        logger.exception("Unexpected authentication error")
+        raise

Or remove this catch block if httpx exceptions are sufficient.


93-96: Add timeout to WebSocket receive operation.

The await websocket.recv() call could hang indefinitely if the backend doesn't send the ready message.

Apply this diff:

+    import asyncio
+
     logger.info(f"Connecting to WebSocket: {websocket_uri}")
     async with websockets.connect(uri_with_token) as websocket:
         # Wait for ready message from backend
-        ready_msg = await websocket.recv()
+        try:
+            ready_msg = await asyncio.wait_for(websocket.recv(), timeout=10.0)
+        except asyncio.TimeoutError:
+            logger.error("Timeout waiting for backend ready message")
+            return
         logger.info(f"Backend ready: {ready_msg}")
setup-requirements.txt (1)

3-3: Add version constraint for python-dotenv.

The dependency lacks any version specification, which could lead to compatibility issues in the future. Based on learnings, python-dotenv 1.1.1 is the latest stable version (released Jun 24, 2025).

Apply this diff to add a version constraint:

-python-dotenv
+python-dotenv>=1.0.0

Or pin to a specific version for maximum reproducibility:

-python-dotenv
+python-dotenv==1.1.1
backends/advanced/init.py (1)

342-372: Avoid double .env backups; keep a single backup step

generate_env_file() re-backs up after backup_existing_env() already did. Remove the second backup to reduce noise.

Apply:

-        # Backup existing .env if it exists
-        if env_path.exists():
-            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
-            backup_path = env_path.with_suffix(f'.backup.{timestamp}')
-            shutil.copy2(env_path, backup_path)
-            self.console.print(f"[blue][INFO][/blue] Backed up existing .env to {backup_path.name}")

Optional: don’t print generated passwords in cleartext; instead show a hint and write only to .env (permissions already 0600).

extras/speaker-recognition/init.py (2)

222-236: Deduplicate .env backup; use backup_existing_env().

You already have backup_existing_env(); call it here and remove the inline duplication.

@@
-        # Backup existing .env if it exists
-        if env_path.exists():
-            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
-            backup_path = env_path.with_suffix(f'.backup.{timestamp}')
-            shutil.copy2(env_path, backup_path)
-            self.console.print(f"[blue][INFO][/blue] Backed up existing .env to {backup_path.name}")
+        # Backup existing .env if it exists
+        self.backup_existing_env()

Optionally, if you keep the call above, consider removing the now-redundant backup_existing_env() method to avoid confusion.


319-325: Avoid bare except Exception; catch expected errors.

Narrow scope or re-raise unknowns; aligns with BLE001.

-        except Exception as e:
+        except (OSError, subprocess.SubprocessError, ValueError) as e:
             self.console.print(f"[red][ERROR][/red] Setup failed: {e}")
             sys.exit(1)

If broader coverage is desired, add a debug flag to re-raise unrecognized exceptions after logging.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1c3b996 and 8ac5a50.

⛔ Files ignored due to path filters (1)
  • backends/advanced/uv.lock is excluded by !**/*.lock
📒 Files selected for processing (19)
  • .gitignore (1 hunks)
  • CLAUDE.md (8 hunks)
  • Docs/getting-started.md (1 hunks)
  • Docs/init-system.md (3 hunks)
  • backends/advanced/init.py (2 hunks)
  • backends/advanced/nginx.conf (0 hunks)
  • backends/advanced/setup-requirements.txt (1 hunks)
  • extras/local-omi-bt/README.md (1 hunks)
  • extras/local-omi-bt/connect-omi.py (1 hunks)
  • extras/local-omi-bt/requirements.txt (1 hunks)
  • extras/local-omi-bt/send_to_adv.py (1 hunks)
  • extras/local-omi-bt/start.sh (1 hunks)
  • extras/speaker-recognition/docker-compose.yml (2 hunks)
  • extras/speaker-recognition/init.py (1 hunks)
  • extras/speaker-recognition/setup-requirements.txt (1 hunks)
  • extras/speaker-recognition/webui/Dockerfile (1 hunks)
  • quickstart.md (1 hunks)
  • setup-requirements.txt (1 hunks)
  • wizard.py (6 hunks)
💤 Files with no reviewable changes (1)
  • backends/advanced/nginx.conf
🧰 Additional context used
🪛 Gitleaks (8.28.0)
Docs/getting-started.md

[high] 226-226: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 362-368: Discovered a potential authorization token provided in a curl command header, which could compromise the curl accessed resource.

(curl-auth-header)

CLAUDE.md

[high] 320-326: Discovered a potential authorization token provided in a curl command header, which could compromise the curl accessed resource.

(curl-auth-header)

🪛 LanguageTool
Docs/getting-started.md

[style] ~9-~9: For conciseness, consider replacing this expression with an adverb.
Context: ...ort "AI wearable" agents/functionality. At the moment, the basic functionalities are: - Audio...

(AT_THE_MOMENT)


[grammar] ~317-~317: Use a hyphen to join words.
Context: ...processing - Collection timeout: 1.5 minute collection for optimal online pro...

(QB_NEW_EN_HYPHEN)


[style] ~476-~476: The double modal “needed Encrypted” is nonstandard (only accepted in certain dialects). Consider “to be Encrypted”.
Context: ...x.x.x) - no CORS configuration needed - Encrypted inter-service communication **Service ...

(NEEDS_FIXED)

CLAUDE.md

[grammar] ~7-~7: Use a hyphen to join words.
Context: ...at the very least audio capture, speaker specific transcription, memory extractio...

(QB_NEW_EN_HYPHEN)

🪛 markdownlint-cli2 (0.18.1)
Docs/getting-started.md

46-46: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


530-530: Bare URL used

(MD034, no-bare-urls)

CLAUDE.md

99-99: Bare URL used

(MD034, no-bare-urls)

🪛 Ruff (0.14.0)
extras/local-omi-bt/send_to_adv.py

60-60: Do not use bare except

(E722)


60-61: try-except-pass detected, consider logging the exception

(S110)


66-66: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


69-69: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


71-71: Do not catch blind exception: Exception

(BLE001)


72-72: Use logging.exception instead of logging.error

Replace with exception

(TRY400)

extras/local-omi-bt/connect-omi.py

72-72: Unused function argument: sender

(ARG001)


77-77: Do not catch blind exception: Exception

(BLE001)


78-78: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


98-98: Local variable test_client is assigned to but never used

Remove assignment to unused variable test_client

(F841)


100-100: Do not catch blind exception: Exception

(BLE001)


101-101: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


102-102: Use logging.exception instead of logging.error

Replace with exception

(TRY400)

extras/speaker-recognition/init.py

172-172: subprocess call: check for execution of untrusted input

(S603)


196-196: Do not catch blind exception: Exception

(BLE001)


203-203: f-string without any placeholders

Remove extraneous f prefix

(F541)


204-204: f-string without any placeholders

Remove extraneous f prefix

(F541)


205-205: f-string without any placeholders

Remove extraneous f prefix

(F541)


206-206: f-string without any placeholders

Remove extraneous f prefix

(F541)


257-257: f-string without any placeholders

Remove extraneous f prefix

(F541)


323-323: Do not catch blind exception: Exception

(BLE001)

wizard.py

163-163: Possible hardcoded password assigned to: "hf_token"

(S105)


332-332: f-string without any placeholders

Remove extraneous f prefix

(F541)

🪛 Shellcheck (0.11.0)
extras/local-omi-bt/start.sh

[error] 1-1: Tips depend on target shell and yours is unknown. Add a shebang or a 'shell' directive.

(SC2148)

🔇 Additional comments (5)
Docs/init-system.md (1)

15-15: LGTM! Documentation accurately reflects the wizard.py migration.

The updates correctly reference wizard.py as the root orchestrator, replacing previous init.py references. This aligns with the PR's architectural changes.

Also applies to: 25-25, 42-42

Docs/getting-started.md (1)

1-730: Comprehensive getting started guide!

This documentation provides excellent end-to-end guidance covering setup, configuration, deployment patterns, and troubleshooting. The content is well-organized and includes practical examples.

quickstart.md (1)

96-96: Command switch to wizard.py looks good

Matches the new orchestrator and uv flow.

wizard.py (1)

332-333: Remove extraneous f-string prefix

The issue is confirmed. Line 332 contains an f-string with no placeholders—only literal text and rich formatting markup. Removing the f prefix will clear the F541 linting error.

-    console.print(f"[dim]   cd extras/speaker-recognition && uv run --with-requirements setup-requirements.txt python init.py[/dim]")
+    console.print("[dim]   cd extras/speaker-recognition && uv run --with-requirements setup-requirements.txt python init.py[/dim]")
CLAUDE.md (1)

320-327: The stated concern about gitleaks false positives is not applicable to this repository.

This codebase has no gitleaks configuration, custom rules, or secret scanning workflows configured. The placeholder YOUR_TOKEN will not be flagged by default gitleaks or any active scanning. However, for consistency with the rest of the codebase (which uses shell variables like $ADMIN_TOKEN, $USER_TOKEN, and $TOKEN in other documentation files), using ${TOKEN} would improve uniformity.

Likely an incorrect or invalid review comment.

## Project Overview

Friend-Lite is at the core an AI-powered personal system - various devices, incuding but not limited to wearables from OMI can be used for at the very least audio capture, speaker specific transcription, memory extraction and retriaval.
Friend-Lite is at the core an AI-powered personal system - various devices, including but not limited to wearables from OMI can be used for at the very least audio capture, speaker specific transcription, memory extraction and retrieval.
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Hyphenation nit

Use “speaker‑specific transcription”.

Apply:

-Friend-Lite is at the core an AI-powered personal system - various devices, including but not limited to wearables from OMI can be used for at the very least audio capture, speaker specific transcription, memory extraction and retrieval.
+Friend-Lite is at the core an AI-powered personal system — various devices, including but not limited to wearables from OMI, can be used for at the very least audio capture, speaker-specific transcription, memory extraction, and retrieval.
📝 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
Friend-Lite is at the core an AI-powered personal system - various devices, including but not limited to wearables from OMI can be used for at the very least audio capture, speaker specific transcription, memory extraction and retrieval.
Friend-Lite is at the core an AI-powered personal system various devices, including but not limited to wearables from OMI, can be used for at the very least audio capture, speaker-specific transcription, memory extraction, and retrieval.
🧰 Tools
🪛 LanguageTool

[grammar] ~7-~7: Use a hyphen to join words.
Context: ...at the very least audio capture, speaker specific transcription, memory extractio...

(QB_NEW_EN_HYPHEN)

Comment on lines +81 to +87
async def find_and_set_omi_mac() -> None:
devices = await list_devices()
assert len(devices) == 1, "Expected 1 Omi device, got %d" % len(devices)
OMI_MAC = devices[0].address
set_key(env_path, "OMI_MAC", OMI_MAC)
logger.info("OMI_MAC set to %s and saved to .env" % OMI_MAC)

Copy link
Contributor

@coderabbitai coderabbitai bot Oct 20, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix OMI_MAC scoping bug and improve robustness/logging

  • Ensure discovered MAC is used after discovery.
  • Replace assert with guards.
  • Use logging.exception for tracebacks.
  • Drop unused variable in BleakClient test.

Apply:

@@
-    async def find_and_set_omi_mac() -> None:
-        devices = await list_devices()
-        assert len(devices) == 1, "Expected 1 Omi device, got %d" % len(devices)
-        OMI_MAC = devices[0].address
-        set_key(env_path, "OMI_MAC", OMI_MAC)
-        logger.info("OMI_MAC set to %s and saved to .env" % OMI_MAC)
+    async def find_and_set_omi_mac() -> str:
+        devices = await list_devices()
+        if not devices:
+            raise RuntimeError("No OMI devices found. Ensure the device is on and in range.")
+        if len(devices) > 1:
+            logger.warning("Multiple OMI-like devices found; selecting the first: %s", devices[0].name)
+        mac = devices[0].address
+        set_key(env_path, "OMI_MAC", mac)
+        logger.info("OMI_MAC set to %s and saved to .env", mac)
+        return mac
@@
-        logger.info("Starting OMI Bluetooth connection and audio streaming")
-        if not OMI_MAC:
-            await find_and_set_omi_mac()
+        logger.info("Starting OMI Bluetooth connection and audio streaming")
+        omi_mac = OMI_MAC
+        if not omi_mac:
+            omi_mac = await find_and_set_omi_mac()
         else:
-            logger.info("using OMI_MAC from .env: %s" % OMI_MAC)
+            logger.info("using OMI_MAC from .env: %s", omi_mac)
@@
-            async with BleakClient(OMI_MAC) as test_client:
-                logger.info(f"Successfully connected to device {OMI_MAC}")
-        except Exception as e:
-            logger.error(f"Failed to connect to device {OMI_MAC}: {e}")
-            logger.error("Exiting without creating audio sink or backend connection")
+            async with BleakClient(omi_mac):
+                logger.info("Successfully connected to device %s", omi_mac)
+        except Exception:
+            logger.exception("Failed to connect to device %s", omi_mac)
+            logger.error("Exiting without creating audio sink or backend connection")
             return
@@
-                    listen_to_omi(OMI_MAC, OMI_CHAR_UUID, handle_ble_data),
+                    listen_to_omi(omi_mac, OMI_CHAR_UUID, handle_ble_data),

Also improve callback logging and remove unused function:

@@
-    def handle_ble_data(sender: Any, data: bytes) -> None:
+    def handle_ble_data(_sender: Any, data: bytes) -> None:
         decoded_pcm: bytes = decoder.decode_packet(data)
         if decoded_pcm:
             try:
                 audio_queue.put_nowait(decoded_pcm)
-            except Exception as e:
-                logger.error("Queue Error: %s", e)
+            except Exception:
+                logger.exception("Queue put error")
@@
-async def as_audio_chunks(it) -> AsyncGenerator[AudioChunk, None]:
-    """Convert bytes iterator to AudioChunk iterator."""
-    async for data in it:
-        yield AudioChunk(audio=data, rate=16000, width=2, channels=1)
+# (unused) as_audio_chunks helper can be removed or used where needed

Also applies to: 88-104, 98-101, 72-79, 47-51

🤖 Prompt for AI Agents
In extras/local-omi-bt/connect-omi.py around lines 81-87 (also review ranges
47-51, 72-79, 88-104, 98-101): fix the OMI_MAC scoping bug by not creating a
local-only OMI_MAC — either assign to the module/global variable or return the
discovered MAC from find_and_set_omi_mac and use that caller-side; replace the
assert with a runtime guard that logs an error and safely returns or raises a
descriptive exception when the device count is not exactly one; wrap device
discovery and client operations in try/except and use logger.exception(...) to
log full tracebacks on failures; remove any unused variables (e.g., the unused
BleakClient test variable) and delete the unused callback helper function, and
while here improve callback logging by emitting clear debug/info messages
showing address and payload instead of silent or minimal prints.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@coderabbitai try:
audio_queue.put_nowait(decoded_pcm)

  •        except Exception as e:
    
  •            logger.error("Queue Error: %s", e)
    
  •        except Exception:
    
  •            logger.exception("Queue put error")
    

@@
doesnt this mean it becomes harder to know the error?

Copy link
Contributor

Choose a reason for hiding this comment

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

@AnkushMalaker Actually, it's the opposite! logger.exception() provides more information, not less.

Here's the difference:

Current code:

except Exception as e:
    logger.error("Queue Error: %s", e)

This only logs the exception's string representation (e.g., "Queue Error: list index out of range").

Suggested code:

except Exception:
    logger.exception("Queue put error")

This logs the full stack trace automatically, showing exactly where the error occurred and the complete call chain. It's equivalent to:

except Exception as e:
    logger.error("Queue put error: %s", e, exc_info=True)

Example output comparison:

With logger.error():

ERROR: Queue Error: Full

With logger.exception():

ERROR: Queue put error
Traceback (most recent call last):
  File "connect-omi.py", line 76, in handle_ble_data
    audio_queue.put_nowait(decoded_pcm)
  File "/usr/lib/python3.11/asyncio/queues.py", line 196, in put_nowait
    ...
asyncio.QueueFull: ...

The logger.exception() method is specifically designed for use in exception handlers and automatically captures the full traceback context, making debugging much easier. This is a Python logging best practice! 🐛

Comment on lines +1 to +3
# Usage
Run using `uv run --with-requirements requirements.txt python connect-laptop.py`
from this directory. No newline at end of file
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fix script name mismatch

Use connect-omi.py to match the added script.

Apply:

-Run using `uv run --with-requirements requirements.txt python connect-laptop.py`  
+Run using `uv run --with-requirements requirements.txt python connect-omi.py`  
📝 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
# Usage
Run using `uv run --with-requirements requirements.txt python connect-laptop.py`
from this directory.
# Usage
Run using `uv run --with-requirements requirements.txt python connect-omi.py`
from this directory.
🤖 Prompt for AI Agents
In extras/local-omi-bt/README.md around lines 1 to 3, the usage example
references the wrong script name (connect-laptop.py); update the command to use
the actual script added (connect-omi.py) so the README matches the repository.
Replace the referenced filename in the run command with connect-omi.py and
ensure the working-directory note remains correct.

Comment on lines +12 to +14
websocket_uri = "ws://100.83.66.30:8000/ws_pcm"
backend_url = "http://100.83.66.30:8000"
env_path = ".env"
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Hardcoded configuration should be environment-based.

The WebSocket URI and backend URL are hardcoded with a specific IP address (100.83.66.30). This reduces portability and makes the code environment-specific.

Apply this diff to use environment variables:

 # Configuration
-websocket_uri = "ws://100.83.66.30:8000/ws_pcm"
-backend_url = "http://100.83.66.30:8000"
+websocket_uri = os.getenv("BACKEND_WEBSOCKET_URL", "ws://localhost:8000/ws_pcm")
+backend_url = os.getenv("BACKEND_URL", "http://localhost:8000")
 env_path = ".env"

Then document these environment variables in the .env.template or README.

📝 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
websocket_uri = "ws://100.83.66.30:8000/ws_pcm"
backend_url = "http://100.83.66.30:8000"
env_path = ".env"
websocket_uri = os.getenv("BACKEND_WEBSOCKET_URL", "ws://localhost:8000/ws_pcm")
backend_url = os.getenv("BACKEND_URL", "http://localhost:8000")
env_path = ".env"
🤖 Prompt for AI Agents
In extras/local-omi-bt/send_to_adv.py around lines 12 to 14, the websocket_uri
and backend_url are hardcoded to a specific IP; replace them with values loaded
from environment variables (e.g., WEBSOCKET_URI and BACKEND_URL) using
os.environ.get with sensible defaults, and load .env via python-dotenv if
currently used (env path variable can remain for loading). Ensure the code
validates presence or falls back to a reasonable default and does not leak
secrets, then add WEBSOCKET_URI and BACKEND_URL entries (with example values) to
.env.template or document them in the README.

Comment on lines +60 to +61
except:
pass
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Replace bare except with specific exception handling.

The bare except clause can catch system-exiting exceptions like KeyboardInterrupt and SystemExit, which should not be suppressed.

Apply this diff:

             try:
                 error_data = response.json()
                 error_msg = error_data.get('detail', error_msg)
-            except:
-                pass
+            except (ValueError, KeyError):
+                # JSON parsing failed or 'detail' key missing - use default error_msg
+                pass
📝 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
except:
pass
try:
error_data = response.json()
error_msg = error_data.get('detail', error_msg)
except (ValueError, KeyError):
# JSON parsing failed or 'detail' key missing - use default error_msg
pass
🧰 Tools
🪛 Ruff (0.14.0)

60-60: Do not use bare except

(E722)


60-61: try-except-pass detected, consider logging the exception

(S110)

🤖 Prompt for AI Agents
In extras/local-omi-bt/send_to_adv.py around lines 60 to 61, replace the bare
"except:" with a specific exception handler so system-exiting exceptions
(KeyboardInterrupt, SystemExit) are not swallowed; change it to catch Exception
(or the precise exceptions you expect, e.g., OSError, ValueError) like "except
Exception as e:" and handle or log the error (or re-raise for non-recoverable
cases) instead of silently passing.

Comment on lines +181 to +200
# Generate nginx.conf from template
self.console.print("[blue][INFO][/blue] Creating nginx configuration...")
nginx_template = Path("nginx.conf.template")
if nginx_template.exists():
try:
with open(nginx_template, 'r') as f:
nginx_content = f.read()

# Replace TAILSCALE_IP with server_ip
nginx_content = nginx_content.replace('TAILSCALE_IP', server_ip)

with open('nginx.conf', 'w') as f:
f.write(nginx_content)

self.console.print(f"[green][SUCCESS][/green] nginx.conf created for: {server_ip}")
except Exception as e:
self.console.print(f"[yellow][WARNING][/yellow] nginx.conf generation failed: {e}")
else:
self.console.print("[yellow][WARNING][/yellow] nginx.conf.template not found")

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Make nginx paths robust and narrow exception scope.

Resolve paths from repo root; avoid catching bare Exception (BLE001).

@@
-            self.console.print("[blue][INFO][/blue] Creating nginx configuration...")
-            nginx_template = Path("nginx.conf.template")
+            self.console.print("[blue][INFO][/blue] Creating nginx configuration...")
+            # Resolve from repo root
+            nginx_template = (repo_root / "nginx.conf.template")
             if nginx_template.exists():
                 try:
-                    with open(nginx_template, 'r') as f:
+                    with open(nginx_template, 'r') as f:
                         nginx_content = f.read()
@@
-                    nginx_content = nginx_content.replace('TAILSCALE_IP', server_ip)
+                    nginx_content = nginx_content.replace('TAILSCALE_IP', server_ip)
@@
-                    with open('nginx.conf', 'w') as f:
+                    with open(repo_root / 'nginx.conf', 'w') as f:
                         f.write(nginx_content)
@@
-                except Exception as e:
+                except OSError as e:
                     self.console.print(f"[yellow][WARNING][/yellow] nginx.conf generation failed: {e}")
             else:
                 self.console.print("[yellow][WARNING][/yellow] nginx.conf.template not found")

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 Ruff (0.14.0)

196-196: Do not catch blind exception: Exception

(BLE001)

Comment on lines +201 to +207
self.console.print()
self.console.print("📋 [bold]HTTPS Mode URLs:[/bold]")
self.console.print(f" 🌐 HTTPS Access: https://localhost:8444/")
self.console.print(f" 🌐 HTTP Redirect: http://localhost:8081/ → HTTPS")
self.console.print(f" 📱 Service API: https://localhost:8444/api/")
self.console.print(f" 💡 Accept SSL certificate in browser")
else:
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix F541 and show actual host in HTTPS URLs.

Use server_ip instead of hardcoded localhost; drop unused f-strings.

@@
-            self.console.print("📋 [bold]HTTPS Mode URLs:[/bold]")
-            self.console.print(f"   🌐 HTTPS Access: https://localhost:8444/")
-            self.console.print(f"   🌐 HTTP Redirect: http://localhost:8081/ → HTTPS")
-            self.console.print(f"   📱 Service API: https://localhost:8444/api/")
-            self.console.print(f"   💡 Accept SSL certificate in browser")
+            self.console.print("📋 [bold]HTTPS Mode URLs:[/bold]")
+            self.console.print(f"   🌐 HTTPS Access: https://{server_ip}:8444/")
+            self.console.print(f"   🌐 HTTP Redirect: http://{server_ip}:8081/ → HTTPS")
+            self.console.print(f"   📱 Service API: https://{server_ip}:8444/api/")
+            self.console.print("   💡 Accept SSL certificate in browser")
📝 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
self.console.print()
self.console.print("📋 [bold]HTTPS Mode URLs:[/bold]")
self.console.print(f" 🌐 HTTPS Access: https://localhost:8444/")
self.console.print(f" 🌐 HTTP Redirect: http://localhost:8081/ → HTTPS")
self.console.print(f" 📱 Service API: https://localhost:8444/api/")
self.console.print(f" 💡 Accept SSL certificate in browser")
else:
self.console.print()
self.console.print("📋 [bold]HTTPS Mode URLs:[/bold]")
self.console.print(f" 🌐 HTTPS Access: https://{server_ip}:8444/")
self.console.print(f" 🌐 HTTP Redirect: http://{server_ip}:8081/ → HTTPS")
self.console.print(f" 📱 Service API: https://{server_ip}:8444/api/")
self.console.print(" 💡 Accept SSL certificate in browser")
else:
🧰 Tools
🪛 Ruff (0.14.0)

203-203: f-string without any placeholders

Remove extraneous f prefix

(F541)


204-204: f-string without any placeholders

Remove extraneous f prefix

(F541)


205-205: f-string without any placeholders

Remove extraneous f prefix

(F541)


206-206: f-string without any placeholders

Remove extraneous f prefix

(F541)

🤖 Prompt for AI Agents
In extras/speaker-recognition/init.py around lines 201 to 207, the HTTPS URLs
use hardcoded "localhost" and several print() calls use unnecessary f-strings
(F541); replace "localhost" with the actual server_ip variable in the URL
strings and remove the leading f from any string literals that do not
interpolate variables, or convert the strings to proper f-strings only when
including server_ip (e.g., use f"https://{server_ip}:8444/" and
f"http://{server_ip}:8081/ → HTTPS"), and ensure the Service API and certificate
hint also reference server_ip where appropriate.

Comment on lines +217 to +247
def generate_env_file(self):
"""Generate .env file from template and update with configuration"""
env_path = Path(".env")
env_template = Path(".env.template")

# Backup existing .env if it exists
if env_path.exists():
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
backup_path = env_path.with_suffix(f'.backup.{timestamp}')
shutil.copy2(env_path, backup_path)
self.console.print(f"[blue][INFO][/blue] Backed up existing .env to {backup_path.name}")

# Copy template to .env
if env_template.exists():
shutil.copy2(env_template, env_path)
self.console.print("[blue][INFO][/blue] Copied .env.template to .env")
else:
self.console.print("[yellow][WARNING][/yellow] .env.template not found, creating new .env")
env_path.touch(mode=0o600)

# Update configured values using set_key
env_path_str = str(env_path)
for key, value in self.config.items():
if value: # Only set non-empty values
set_key(env_path_str, key, value)

# Ensure secure permissions
os.chmod(env_path, 0o600)

self.console.print("[green][SUCCESS][/green] .env file configured successfully with secure permissions")

Copy link
Contributor

@coderabbitai coderabbitai bot Oct 20, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Check-in hygiene: ensure .env not tracked; verify required files exist.

Automate verification to reduce surprises at runtime and avoid secrets in Git.


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Verify expected files =="
fd -a 'generate-ssl.sh' 2>/dev/null || true
fd -a 'nginx.conf.template' 2>/dev/null || true

echo "== Verify .gitignore has .env (not tracked) =="
if fd -a .gitignore >/dev/null 2>&1; then
  fd -a .gitignore | xargs -I{} rg -nP '^\s*\.env($|/)' {} || echo "MISSING: add '.env' to .gitignore"
else
  echo "No .gitignore found at repo root; consider adding one with '.env'."
fi

if git ls-files --error-unmatch .env >/dev/null 2>&1; then
  echo "WARNING: .env is tracked in Git! Remove and add to .gitignore."
fi

Length of output: 197


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Check .gitignore at root =="
if [ -f .gitignore ]; then
  echo "FOUND .gitignore"
  echo "--- Contents (with line numbers) ---"
  rg -n '\.env' .gitignore || echo "No .env patterns found in .gitignore"
  echo "--- Full .gitignore (first 30 lines) ---"
  head -30 .gitignore
else
  echo "NOT FOUND: .gitignore does not exist at repo root"
fi

echo ""
echo "== Check .env file status in Git =="
git status .env 2>&1 || true

echo ""
echo "== Check if .env is in git index (tracked) =="
git ls-files --stage .env 2>&1 || echo ".env not in index (good)"

echo ""
echo "== Search for generate-ssl.sh references in extras/speaker-recognition/ =="
rg -n 'generate-ssl' extras/speaker-recognition/ -g '*.py' -g '*.sh' 2>/dev/null || echo "No references to generate-ssl.sh found"

echo ""
echo "== Check if generate-ssl.sh is actually needed in extras/speaker-recognition/ =="
if [ -f extras/speaker-recognition/generate-ssl.sh ]; then
  echo "FOUND: extras/speaker-recognition/generate-ssl.sh exists"
else
  echo "NOT FOUND: extras/speaker-recognition/generate-ssl.sh"
fi

Length of output: 578


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Inspect init.py to understand generate_env_file() dependencies =="
if [ -f extras/speaker-recognition/init.py ]; then
  echo "File: extras/speaker-recognition/init.py"
  echo "--- Lines 217-247 (generate_env_file method) ---"
  sed -n '217,247p' extras/speaker-recognition/init.py
  echo ""
  echo "--- Check for .env.template reference =="
  rg -n 'env.*template' extras/speaker-recognition/init.py -A2 -B2
else
  echo "init.py not found"
fi

echo ""
echo "== Check if .env.template exists in extras/speaker-recognition/ =="
if [ -f extras/speaker-recognition/.env.template ]; then
  echo "FOUND: .env.template"
  head -10 extras/speaker-recognition/.env.template
else
  echo "NOT FOUND: .env.template in extras/speaker-recognition/"
fi

echo ""
echo "== Check repo root for .env.template =="
if [ -f .env.template ]; then
  echo "FOUND: .env.template at root"
else
  echo "NOT FOUND: .env.template at root"
fi

Length of output: 2716


Add .env to .gitignore at repository root to prevent secrets from being tracked in Git.

The generate_env_file() function is well-implemented with secure permissions (0o600) and graceful fallback handling. However, .env.template exists in the repo and the code properly handles cases where it's missing. The real concern is that no .gitignore exists at the repository root, which means .env (containing sensitive values like HF_TOKEN) can be accidentally committed. Add an entry for .env to prevent this security risk.

🤖 Prompt for AI Agents
extras/speaker-recognition/init.py lines 217-247: add a root-level .gitignore
entry for .env to prevent secrets from being committed; create or update
.gitignore at the repository root to include a single line ".env" (and optional
".env.*" if you want backups ignored), save and commit the change alongside the
PR so generated .env files (and any backups like .env.backup.*) are not tracked
by git while leaving .env.template in the repo.

Copy link
Collaborator Author

@AnkushMalaker AnkushMalaker Oct 20, 2025

Choose a reason for hiding this comment

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

no .gitignore exists at the repository root.

it does, are you stupid?
❯ ls -a | grep ignore
.gitignore

Copy link
Contributor

Choose a reason for hiding this comment

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Display .gitignore content =="
cat .gitignore

echo ""
echo "== Check for .env patterns in .gitignore =="
rg -n '\.env' .gitignore || echo "No .env patterns found"

echo ""
echo "== Check if .env files are tracked =="
git ls-files | rg '\.env$' || echo "No .env files in git index (good)"

Length of output: 1445


@AnkushMalaker I apologize for the error. You're absolutely right—.gitignore exists and is properly configured.

Looking at the actual content:

  • Line 3: **/*.env correctly ignores all .env files
  • Line 4: !**/.env.template properly allows template files
  • Line 74: .env.backup.* ignores backup files
  • Confirmed: No .env files are tracked in git

The security concern I raised was invalid. The generate_env_file() implementation is solid and your .gitignore is properly protecting secrets. My mistake—no changes needed here.

  (\___/)
  (o  .  )  
  (  > <  ) 
 (_______) 

[feeling foolish]

self.console.print(f"✅ Compute Mode: {self.config.get('COMPUTE_MODE', 'Not configured')}")
self.console.print(f"✅ HTTPS Enabled: {self.config.get('REACT_UI_HTTPS', 'false')}")
if self.config.get('DEEPGRAM_API_KEY'):
self.console.print(f"✅ Deepgram API Key: Configured")
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix F541 (f-string without placeholders).

Remove the unnecessary f-prefix.

-            self.console.print(f"✅ Deepgram API Key: Configured")
+            self.console.print("✅ Deepgram API Key: Configured")
📝 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
self.console.print(f"✅ Deepgram API Key: Configured")
self.console.print("✅ Deepgram API Key: Configured")
🧰 Tools
🪛 Ruff (0.14.0)

257-257: f-string without any placeholders

Remove extraneous f prefix

(F541)

🤖 Prompt for AI Agents
In extras/speaker-recognition/init.py around line 257, there's an f-string with
no placeholders ("f" prefix) causing F541; remove the unnecessary "f" prefix so
the string is a normal literal (e.g., change f"✅ Deepgram API Key: Configured"
to "✅ Deepgram API Key: Configured") to satisfy linting.

Comment on lines +19 to 21
# Note: Port is determined at runtime by REACT_UI_PORT env var
# Actual port binding is configured in docker-compose.yml

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Ensure Vite binds to REACT_UI_PORT

Without passing --port, non-5173 ports will fail healthchecks/bindings.

Apply:

-# Start development server
-CMD ["npm", "run", "dev", "--", "--host", "0.0.0.0"]
+# Start development server with configurable port
+CMD ["sh", "-c", "npm run dev -- --host 0.0.0.0 --port ${REACT_UI_PORT:-5173}"]

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
extras/speaker-recognition/webui/Dockerfile lines 19-21: the Dockerfile
currently doesn't pass the REACT_UI_PORT env var into the Vite dev server
command, so Vite will bind to the default port and non-5173 ports will fail
healthchecks; update the Dockerfile to invoke Vite (or the npm start script)
with the --port flag using the environment variable (e.g. --port
${REACT_UI_PORT:-5173}) and also include --host 0.0.0.0 so it is reachable from
outside the container; modify the CMD/ENTRYPOINT line to use that flag (or
update the start script to forward the flag) and ensure REACT_UI_PORT is
documented or defaulted if not set.

@AnkushMalaker AnkushMalaker merged commit 3259d4c into main Oct 20, 2025
9 of 10 checks passed
@AnkushMalaker AnkushMalaker deleted the feat/local-omi-scripts branch October 20, 2025 20:43
This was referenced Oct 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant