Skip to content

fix(desktop): transcription not auto-starting after proxy migration#6211

Merged
kodjima33 merged 2 commits into
mainfrom
worktree-fix-transcription-autostart
Mar 31, 2026
Merged

fix(desktop): transcription not auto-starting after proxy migration#6211
kodjima33 merged 2 commits into
mainfrom
worktree-fix-transcription-autostart

Conversation

@kodjima33
Copy link
Copy Markdown
Collaborator

Summary

  • After the Deepgram/Gemini proxy migration (d0a5939), APIKeyService.keysAvailable was permanently false because the backend no longer returns Deepgram/Gemini API keys and loadEnvironment() skips them from .env
  • This blocked the transcription auto-start in DesktopHomeView.onAppear, and the onChange(of: isLoaded) fallback has a race condition where it doesn't fire if fetchKeys() completes before the view appears
  • Fix: include OMI_API_URL in the keysAvailable check, since proxy mode only needs the backend URL (set synchronously by loadEnvironment())

Root cause

keysAvailable checked getenv("GEMINI_API_KEY") != nil || getenv("DEEPGRAM_API_KEY") != nil, but since the proxy migration:

  1. Backend /v1/config/api-keys no longer returns these keys
  2. loadEnvironment() skips them from .env (marked as backend-served)
  3. So neither env var is ever set → keysAvailable is always false
  4. onAppear defers transcription → onChange may never fire (race condition) → transcription never starts

Test plan

  • Build and launch desktop app
  • Verify transcription auto-starts on login (check logs for "Auto-starting transcription" instead of "Deferring transcription")
  • Verify screen analysis also auto-starts if enabled
  • Verify transcription WebSocket connects to proxy and produces transcript segments

🤖 Generated with Claude Code

kodjima33 and others added 2 commits March 31, 2026 14:56
…ble check

After the Deepgram/Gemini proxy migration (d0a5939), the backend no
longer serves these API keys and loadEnvironment() skips them from .env.
This left APIKeyService.keysAvailable permanently false, blocking the
transcription auto-start in DesktopHomeView.onAppear. The onChange
fallback for isLoaded has a race condition (fires only on change, not
initial value), so if fetchKeys() completes before the view appears,
transcription never starts.

Fix: include OMI_API_URL in the keysAvailable check since proxy mode
only needs the backend URL (set synchronously by loadEnvironment).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Mar 31, 2026

Greptile Summary

This PR fixes a regression introduced by the Deepgram/Gemini proxy migration where transcription (and screen analysis) would never auto-start on app launch. The root cause was that APIKeyService.keysAvailable gated transcription on the presence of GEMINI_API_KEY or DEEPGRAM_API_KEY in the process environment — but since the proxy migration, these keys are neither returned by the backend nor loaded from .env, so the check always returned false.

The fix extends keysAvailable to also return true when OMI_API_URL is set, which is synchronously guaranteed by loadEnvironment() in AppState.init() before any view appears. This is the correct semantic: in proxy mode the backend URL is the only prerequisite for transcription.

  • OMI_API_URL is set synchronously during AppState.init() via loadEnvironment(), so keysAvailable is true by the time DesktopHomeView.onAppear fires — the race condition is resolved.
  • The onChange(of: isLoaded) fallback remains intact; once isTranscribing is true, it correctly becomes a no-op.
  • clear() correctly does not unset OMI_API_URL (infrastructure config, not a user key).
  • One minor P2: the nil-presence check doesn't validate non-empty, unlike APIClient.baseURL's !url.isEmpty guard."

Confidence Score: 5/5

Safe to merge — the fix is minimal, correctly targeted, and resolves a real regression with no behaviour change for properly configured environments.

The single changed line is a surgical extension of a boolean predicate. Root cause analysis is accurate and the fix matches. Only remaining finding is a P2 style suggestion that would only manifest in a misconfigured .env.

No files require special attention — APIKeyService.swift has one minor style suggestion but is otherwise correct.

Important Files Changed

Filename Overview
desktop/Desktop/Sources/APIKeyService.swift Expands keysAvailable to return true when OMI_API_URL is set, fixing transcription auto-start in proxy mode; minor edge case where a whitespace-only OMI_API_URL value passes the nil check but fails in APIClient
desktop/CHANGELOG.json Adds unreleased changelog entry for the transcription auto-start fix

Sequence Diagram

sequenceDiagram
    participant App as App Launch
    participant AS as AppState.init()
    participant LE as loadEnvironment()
    participant AKS as APIKeyService
    participant DHV as DesktopHomeView.onAppear
    participant TS as TranscriptionService

    App->>AS: init()
    AS->>LE: loadEnvironment() [synchronous]
    LE-->>AS: setenv("OMI_API_URL", url, 1)
    Note over AS,LE: OMI_API_URL set before any view appears

    App->>AKS: startFetchingKeys() [async]
    Note over AKS: fetchKeys() starts in background

    App->>DHV: onAppear fires
    DHV->>AKS: keysAvailable? [Before fix: checks GEMINI/DEEPGRAM → false]
    Note over DHV,AKS: After fix: also checks OMI_API_URL → true (already set)
    DHV->>TS: startTranscription() ✅

    AKS-->>AKS: fetchKeys() completes → isLoaded = true
    Note over DHV: onChange(isLoaded): isTranscribing already true → no-op ✅
Loading

Reviews (1): Last reviewed commit: "chore(desktop): add changelog entry for ..." | Re-trigger Greptile

/// In proxy mode (OMI_API_URL set), no client-side Deepgram/Gemini keys are needed.
nonisolated static var keysAvailable: Bool {
getenv("GEMINI_API_KEY") != nil || getenv("DEEPGRAM_API_KEY") != nil
getenv("GEMINI_API_KEY") != nil || getenv("DEEPGRAM_API_KEY") != nil || getenv("OMI_API_URL") != nil
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.

P2 Presence check doesn't guard against empty/whitespace OMI_API_URL

getenv("OMI_API_URL") != nil returns a non-nil pointer even when the env var is set to a whitespace-only value (e.g. OMI_API_URL= in .env). In that case keysAvailable returns true, transcription auto-starts, but APIClient.baseURL falls through to its !url.isEmpty guard and logs "OMI API: OMI_API_URL not set — API calls will fail" — a silent mismatch.

The existing GEMINI_API_KEY / DEEPGRAM_API_KEY paths are safe because those keys are only written by applyToEnvironment() when the value is non-nil, but OMI_API_URL comes from loadEnvironment() which calls setenv after only a whitespace-trim.

Consider aligning with the non-empty check already used in APIClient.swift:

Suggested change
getenv("GEMINI_API_KEY") != nil || getenv("DEEPGRAM_API_KEY") != nil || getenv("OMI_API_URL") != nil
getenv("GEMINI_API_KEY") != nil || getenv("DEEPGRAM_API_KEY") != nil
|| (getenv("OMI_API_URL").flatMap { String(validatingUTF8: $0) }.map { !$0.trimmingCharacters(in: .whitespaces).isEmpty } ?? false)

@kodjima33
Copy link
Copy Markdown
Collaborator Author

Test Results ✅

Branch: worktree-fix-transcription-autostart
Build: OMI_APP_NAME="test-transcription-fix" ./run.sh --yolo (ad-hoc signed)
Platform: macOS 26.4 (Darwin 25.4.0)

Test Setup

Simulated signed-in user by setting UserDefaults (auth_isSignedIn=true, hasCompletedOnboarding=true), relaunched with --skip-onboarding.

Key Log Results

PASS — transcription auto-starts:

[18:12:47.064] DesktopHomeView: View appeared - isSignedIn=true, hasCompletedOnboarding=true
[18:12:47.160] DesktopHomeView: Auto-starting transcription          ← FIX WORKING
[18:12:47.377] DesktopHomeView: API keys loaded — retrying deferred services
[18:12:47.377] DesktopHomeView: Starting deferred transcription

No FAIL lines:

  • "Deferring transcription — API keys not yet loaded" → not present

Root cause confirmed

Before the fix: DEEPGRAM_API_KEY and GEMINI_API_KEY were never set (skipped by loadEnvironment() after proxy migration) → keysAvailable permanently false → transcription never started.

After the fix: getenv("OMI_API_URL") != nil returns true (URL is set synchronously at startup from .env) → keysAvailable returns truestartTranscription() called immediately on app launch.

@kodjima33 kodjima33 merged commit 40cd293 into main Mar 31, 2026
3 checks passed
@kodjima33 kodjima33 deleted the worktree-fix-transcription-autostart branch March 31, 2026 22:15
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