report: Combined verification + architecture analysis (#5441, #5442, #5443)#5445
report: Combined verification + architecture analysis (#5441, #5442, #5443)#5445
Conversation
Legacy Firestore person documents may lack these fields, causing ResponseValidationError (500) on /v1/users/people for 8 users. Make both fields Optional with None default. Fixes part of #5423 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
get_people(), get_person(), get_person_by_name(), get_people_by_ids() all returned raw to_dict() without the document ID. Legacy docs missing the 'id' field caused ResponseValidationError on Person model. Fixes #5423 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Enables the list endpoint to use this lighter function that skips loading full base64 photo content per conversation. Fixes part of #5424 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
GET /v1/conversations was loading full base64 photos for every conversation via @with_photos decorator. 50 convos x 1.2MB = 57MB exceeded Cloud Run 32MB response limit. The list endpoint doesn't need photo content — individual conversation GET still loads them. Fixes #5424 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ingApiCall Port the 401→refresh→retry→signout pattern from makeApiCall() into both multipart methods. Extract _buildMultipartRequest() helper to rebuild requests for retry (streams are single-use). Fixes #5414
Reviewer feedback: where("id", "in", ...) misses legacy docs that
don't have a stored 'id' field. Use db.get_all() with doc refs instead.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
10 tests covering: - Person model resilience with missing created_at/updated_at (#5423) - Doc ID injection in get_people, get_person, get_people_by_ids (#5423) - Conversations list endpoint uses without-photos function (#5424) - get_conversations_without_photos supports folder_id/starred (#5424) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Large batch test for get_people_by_ids (>30 IDs, old where-in limit) - Empty list boundary test - Verify get_conversations_without_photos lacks @with_photos decorator - Verify get_conversations retains @with_photos for individual use Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
7 tests covering all code paths: - Non-401 returns directly (no refresh, no signout) - 401 → refresh succeeds → retry succeeds (200) - 401 → refresh succeeds → retry still 401 → signs out - 401 → refresh fails (empty token) → signs out without retry - requireAuthCheck=false skips 401 handling - Request rebuilt with fresh headers for retry - 500 does not trigger auth retry
… retry test Adds STAGING_API_URL= to generated .dev.env (fixes pre-existing envied compilation failure that blocked all tests on clean checkout).
Test Evidence — PR #5441 (People/Conversations 500s Fix)14/14 tests pass by AI for @beastoin |
Test Evidence — PR #5443 (Firestore Read Ops Cache)19/19 tests pass by AI for @beastoin |
Test Evidence — PR #5442 (Multipart 401 Retry)7/7 Flutter unit tests pass Test coverage:
by AI for @beastoin |
Combined Verification SummaryTest Results: 40/40 PR-specific tests PASS
Merge Compatibility
Pre-existing test failures (NOT from these PRs)
Codex Quality Audit
by AI for @beastoin |
User Acceptance Test (UAT) Report — Combined PR1. Full Test Suite ResultsBackend (test.sh): All 45 test files executed
App (test.sh): All test files pass
2. APK Build
3. Architecture ReviewImport Compliance: PASS — No violations of module hierarchy ( Circular Imports: PASS — No circular dependencies detected Logging Security: PASS — No raw sensitive data logged in any new code Thread Safety: Generally sound — Decorator Correctness: PASS — 4. Issues FoundWARNING:
|
| Check | Result |
|---|---|
| Cache changes affect conversation fetches | No interaction — different code paths |
| People changes affect cache layer | No interaction — people not cached |
| Multipart retry affects backend behavior | No interaction — client-side only |
| Shared state conflicts | None found — atomic cache operations |
| Module hierarchy preserved | Yes — all imports follow database/ → utils/ → routers/ |
6. Verdict
SAFE TO MERGE with one recommended fix:
- Fix the
_fetch_locksunbounded growth incache_manager.py(non-blocking but should be addressed) - No regressions detected
- Architecture is sound — clean separation of concerns, correct decorator usage, proper thread safety
by AI for @beastoin
Per kelvin UAT review: callers must not assume result order matches input person_ids order. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
E2E Physical Device Test — PASS (2/2)Test Setup
Test 1 — Short Clip (4m 16s): PASS
Test 2 — 15min Podcast (16m 30s): PASS
Verified Features
Notes
by AI for @beastoin |
…nto verify/combined-5441-5442-5443
…to verify/combined-5441-5442-5443
…9' into verify/combined-5441-5442-5443 # Conflicts: # backend/test.sh
43321ad to
8c2bdb5
Compare
Re-Test Report — Updated Combined Branch (all 3 sub-PRs at latest)Branch fix: Remote PR branch was missing 14 commits from #5443 (only had early snapshot). Pushed local re-merge — now includes all 19 commits from #5443 including Test Results: 72/72 PASS
APK Build
agent-flutter Widget-Level TestingConnected agent-flutter to Omi debug app via Dart VM Service:
Sub-PR Commit VerificationAll 3 sub-PRs at their latest tips are included:
Re-tested by AI for @beastoin |
agent-flutter E2E Test — Logged In + Local BackendSetup
agent-flutter Test FlowBackend API Log — Zero 500sAll endpoints returned 200 OK including the critical fixes:
Evidence
Recipe for Team# 1. Local backend
cd backend && export $(grep -v '^#' .env | xargs) && \
export GOOGLE_APPLICATION_CREDENTIALS=~/.config/omi/dev/backend/google-credentials.json && \
python3 -m uvicorn main:app --port 8000
# 2. App .dev.env
API_BASE_URL=http://10.0.2.2:8000/
STAGING_API_URL=
USE_WEB_AUTH=false
USE_AUTH_CUSTOM_TOKEN=false
# 3. Rebuild envied + launch
cd app && rm -rf .dart_tool/build/ lib/env/dev_env.g.dart && \
dart run build_runner build --delete-conflicting-outputs && \
flutter run -d emulator-5554 --flavor dev --debug
# 4. Sign in (VM Service eval)
# Generate custom token via Firebase Admin SDK, then:
# FirebaseAuth.instance.signInWithCustomToken("<token>")
# 5. agent-flutter
agent-flutter connect # auto-detects VM service
agent-flutter find text "English" press
agent-flutter find text "Confirm" press
agent-flutter snapshot -i -c # verify widgets
agent-flutter screenshot /tmp/evidence.pngTested by AI for @beastoin |
Physical Device E2E Evidence — Pixel 7aDevice: Pixel 7a (Android 14, 1080x2400 @ 420dpi) Screens Verified
Findings
Notes
Physical device E2E: PASS |
Core Flow E2E — Record + Transcribe (30s Podcast) — Pixel 7a + Omi DeviceDevice: Pixel 7a (Android 14) + Omi BLE device (100% battery) Setup
Results
Pipeline Verified
Backend Logs Confirm
Core flow 30s recording: PASS |
5-Minute Core Flow E2E — Pixel 7a + Omi BLE DeviceTest: Record and transcribe 5-minute audio podcast via Omi BLE device PipelineEvidence (5 checkpoints across the recording)
Backend Logs Confirm
VerdictPASS — 5-minute sustained audio recording + live transcription via Omi BLE device on Pixel 7a physical device. Core flow (BLE → app → WebSocket → Deepgram STT → live transcript) works continuously without drops. Combined with the 30s test (previous comment), both core flow tests pass. |
|
Hey @beastoin 👋 Thank you so much for taking the time to contribute to Omi! We truly appreciate you putting in the effort to submit this pull request. After careful review, we've decided not to merge this particular PR. Please don't take this personally — we genuinely try to merge as many contributions as possible, but sometimes we have to make tough calls based on:
Your contribution is still valuable to us, and we'd love to see you contribute again in the future! If you'd like feedback on how to improve this PR or want to discuss alternative approaches, please don't hesitate to reach out. Thank you for being part of the Omi community! 💜 |









Summary
Report-only PR documenting combined verification results for PRs #5441, #5442, #5443 and architecture analysis of recent shifts in the Omi project. No code changes — this PR serves as a reviewable artifact for manager approval.
Updated 2026-03-09: Reflects all review-cycle fixes (credits invalidation #5446, real Redis integration tests, pub/sub unit tests, _fetch_locks refcounted cleanup).
Verification Report
PR #5441 — People/Conversations 500s Fix (yuki)
Unit Tests: 14/14 pass
Live API Verification (dev Firestore, real data):
GET /v1/users/people→ 200 (tested with fresh user + user with existing data)GET /v1/conversations?limit=50→ 200, 141KB response (previously 57MB with embedded photos)GET /v1/conversations/{id}→ 200 (individual conversation detail)created_at/updated_attimestamps) → all return 200Key Changes Verified:
get_conversations_without_photos()— new function that skips photo subcollection loads for list endpointsget_people()— injects doc ID viadata.setdefault('id', person.id)for legacy doc compatibilityget_people_by_ids()— usesdb.get_all(doc_refs)batch fetch instead ofwhere("id","in",...)Codex Audit: 6 gaps identified, 5 resolved with evidence, 1 accepted risk (cross-pod 30s TTL for non-critical paths)
Cache Impact: None — endpoints hit Firestore directly (confirmed by yuki).
PR #5442 — Multipart 401 Retry (kenji)
Unit Tests: 7/7 pass
Emulator Verification:
Code Review — 3 retry handlers verified in
app/lib/backend/http/shared.dart:makeApiCall()(line 116) — standard HTTP 401 retrymakeMultipartApiCall()(line 219) — multipart with full request rebuild (file streams can't be reused)makeMultipartStreamingApiCall()(line 341) — streaming multipart with same rebuild patternPattern: detect 401 → refresh token → rebuild headers AND request body → retry once → force sign-out on second failure
Cache Impact: None — client-side only (confirmed by kenji).
PR #5443 — Firestore Read Ops Cache (hiro)
Unit Tests: 51/51 pass (updated from initial 19 after review-cycle additions)
Test breakdown:
TestMentorFrequencyCache(5) — local cache behaviorTestTesterAndAppSliceCache(6) — local invalidationTestCreditCacheLogic(9) — passive refresh timingTestFetchLockCleanup(4) — singleflight refcounted lock cleanupTestRedisCreditsInvalidationSignal(3) — Redis signal set/checkTestWebhookInvalidationCoverage(8) — source-code scanning for function coverageTestRedisPubSubManager(12) — pub/sub callback logic (unit)TestRedisPubSubIntegration(3) — real Redis (localhost:6379, separate clients simulating pods)Latency Verification (live API, 3 sequential requests each):
Cache Invalidation — 3 strategies verified:
PATCH→cache.delete(key)→ nextGETfetches fresh from FirestoreReview-Cycle Fixes (post-initial-review):
remaining_transcript_secondshad a 15min passive refresh. If a user upgraded mid-stream, transcripts were silently dropped for up to 15 minutes (gated byuser_has_creditsattranscribe.py:1845). Fixed: Active invalidation via Redis signal —set_credits_invalidation_signal(uid)on 4 Stripe webhook points,check_credits_invalidation(uid)in transcribe loop every 60s,GET-not-GETDELfor multi-stream safety.redis.Redis(localhost:6379)with separate clients._fetch_locksrefcounted cleanup: Fixed potential memory leak where per-key singleflight locks accumulated. Now uses refcount tracking — lock deleted only when no waiters remain.New Files:
database/cache.py— global singleton init, atexit cleanup, Redis pub/sub callbacksdatabase/cache_manager.py—InMemoryCacheManagerwith LRU eviction, per-entry TTL, singleflight pattern, thread-safe (RLock), 100MB default limitCombined Results
test.shconflict resolved — both PRs added test entries)verify/combined-5441-5442-5443E2E Physical Device Test — PASS (2/2)
Test Setup
Test 1 — Short Clip (4m 16s): PASS
Test 2 — 15min Podcast (16m 30s): PASS
Verified Features
Conversation creation, real-time transcription (Deepgram STT), speaker diarization, AI summarization, timestamp generation, translation tags, conversation history.
Evidence screenshots: see PR comment below
Architecture Analysis — Recent Shifts
1. New: Two-Tier Caching Layer (PR #5443, PR #5378)
The Omi backend has shifted from a single-tier caching model (Redis only) to a two-tier architecture:
New files:
database/cache.py,database/cache_manager.pyKey decisions:
Impact: Firestore LOOKUP reads reduced 18-29% sustained (PR #5378 monitoring data at T+20h)
2. New: Photo-Less List Endpoints (PR #5441)
Shift: API list endpoints now explicitly separate "list" from "detail" data shapes.
get_conversations_without_photos()skips the Firestore photo subcollection entirelyGET /v1/conversations(list endpoint), whileGET /v1/conversations/{id}(detail) still loads photosKey decision: Separate function rather than conditional flag — cleaner separation, no risk of breaking existing callers that depend on photos being present.
3. New: Multipart 401 Resilience (PR #5442)
Shift: The Flutter HTTP client now handles token expiration consistently across ALL request types, including multipart.
Challenge: Multipart requests use file streams that can only be read once — you can't simply "retry" with new headers. The solution rebuilds the entire request (headers + body + file streams) from scratch.
Key decision: Force sign-out after second 401 failure — prevents infinite retry loops and surfaces auth issues to the user immediately.
4. Shifted: Firestore Cost Model (PR #5378)
Before: Every backend read hit Firestore directly. High-frequency paths (mentor notifications at 1 read/second/stream) accumulated significant cost.
After: Targeted field projections + in-memory caching for hot paths. Firestore reads are now budgeted — high-frequency reads go through cache, low-frequency reads still hit Firestore directly.
Key decision: Only cache endpoints with measurable hot-path cost, not blanket caching. This keeps the system simple and debuggable.
5. Shifted: Database Module Scope
Before:
database/contained only Firestore and Redis connection code.After:
database/now includes caching infrastructure (cache.py,cache_manager.py). This follows the module hierarchy — caching is a data-access concern that sits at the lowest level, imported byutils/androuters/.The module hierarchy remains enforced:
database/→utils/→routers/→main.py6. Stable: Service Map Unchanged
The inter-service architecture is unchanged by these PRs:
All changes are within backend internals (data access layer + Flutter client). No new services, no new inter-service calls, no new environment variables.
PR Links
Verification performed by kelvin on VPS with combined branch, live API testing, Android emulator, physical device E2E test (Pixel 7a), and Codex quality audit.
by AI for @beastoin