577 Commits

Author SHA1 Message Date
Accusys 5d0c771a2b fix: use environment variables for VLM/LLM endpoints and models
- trace_vlm_caption.py: use MOMENTRY_LLM_VISION_URL and MOMENTRY_LLM_VISION_MODEL
- scene_vlm_caption.py: use MOMENTRY_LLM_VISION_URL and MOMENTRY_LLM_VISION_MODEL
- Changed from Ollama /api/generate to OpenAI-compatible /v1/chat/completions format
- Added embedding server environment variables
2026-07-27 13:49:08 +08:00
Accusys 39a2cbc65b fix: face group name read consistency, sync_file_status fix, cleanup ghost records, identity_agent replaced with face_dedup
- get_face_groups_handler: COALESCE(tp.name, tn.label) for name consistency
- sync_file_status: compare JSON vs pre_chunks (not chunk table)
- face consistency: compare frames.len() not total_faces
- cleanup 2 ghost records with NULL file_name/file_path
- replace identity_agent with face_dedup in pipeline stages
- remove identity_agent_api.rs and all references
- update required_processors to match actual processors
- update AGENTS.md with team responsibilities
- add Studio pipeline changes documentation
2026-07-27 02:15:51 +08:00
Accusys fcdeab82e6 feat: save key_frame.jpg and key_face.jpg for each face trace during TKG build
- Extract full frame at best confidence frame using ffmpeg
- Crop face region using average bbox coordinates
- Save to .faces/{file_uuid}/{trace_id}/key_frame.jpg and key_face.jpg
- TKG node properties store full file paths for UI display
- Supports future extension for cut representative frames
2026-07-21 04:08:16 +08:00
Accusys 6f5d71763f feat: auto-populate key_frame and key_face in TKG face_track nodes
- FacePoint now includes confidence and point_id from Qdrant
- TKG build tracks best face (highest confidence) per trace
- key_frame = frame number of best face, key_face = Qdrant point ID
- Every face trace now has representative frame and face for UI display
2026-07-21 03:56:25 +08:00
Accusys 644516769c docs: update TKG, profile, identity module docs with trace management and external_id fix
- 15_tkg.md: external_id format changed to trace_N, added trace management section (delete/restore/merge)
- 18_profile.md: note external_id matching fix
- 07_identity.md: note bind/unbind external_id fix
- regenerated user docs (doc/)
2026-07-21 03:22:09 +08:00
Accusys 52bec30c90 feat: unify TKG face_track external_id to trace_N, add trace restore endpoint
- tkg.rs: use trace_N as primary external_id, delete duplicate face_track_N nodes
- trace_agent_api.rs: add POST /trace/:id/restore to undo soft delete
- ensure 1:1 mapping: each trace has exactly one TKG node with full profile
2026-07-21 03:19:17 +08:00
Accusys 0ad6927d25 feat: fix TKG external_id mismatch, add trace delete/merge endpoints, fix no-faces progress update
- identity_binding.rs: match both face_track_N and trace_N external_id formats in bind/unbind/create_pending_person
- profile.rs: fix external_id matching and parameter ordering in trace profile updates
- trace_agent_api.rs: add DELETE /trace/:id (soft/hard delete) and POST /trace/:src/merge/:target endpoints
- qdrant_db.rs: add delete_points_by_filter method
- tkg.rs: add debug logging for scroll and parse operations
- job_worker.rs: update progress for no-faces TKG path
2026-07-21 02:45:34 +08:00
Accusys d25b5921f7 fix: delete all progress keys on unregister
- Delete progress:{uuid}:pipeline, :agent, :tkg
- Prevents stale Redis data from blocking new registrations
2026-07-20 23:45:48 +08:00
Accusys 29d2cc7315 fix: wait for Qdrant face points before TKG build
- Add Qdrant count verification before TKG build
- Wait up to 30 seconds for Qdrant to have all face embeddings
- Prevents race condition where TKG reads incomplete data
- Add count_points method to QdrantDb
2026-07-20 23:26:42 +08:00
Accusys bc914ce3df fix: handle no_faces case - TKG proceeds without waiting for face_traced.json
- Check face.json status before waiting for face_traced.json
- If status='no_faces', spawn TKG immediately (no face_track nodes)
- If status='has_faces', wait for face_traced.json
- Prevents infinite wait when video has no faces
2026-07-20 22:34:09 +08:00
Accusys 5ec18ed520 fix: TKG build waits for face_traced.json to ensure face_track nodes are built
- Add check for face_traced.json existence before spawning TKG build
- Prevents race condition where TKG reads Qdrant before trace_id is written
- Logs clearly when waiting and when proceeding
2026-07-20 22:20:33 +08:00
Accusys 70e6915246 fix: remove error status handling that caused TKG to build without face_track nodes 2026-07-20 21:59:53 +08:00
Accusys 7dc910e2de feat: add cluster-agent endpoint, fix OCR labeling, fix ingestion blocking
- Add POST /api/v1/file/:file_uuid/cluster-agent endpoint for on-demand face clustering
- Fix OCR chunks being labeled as ASRX: use ChunkType.as_str() instead of {:?}
- Rule 1 now deletes old chunks before re-inserting to avoid stale data
- Add fallback face_traced.json when store_traced_faces.py fails
- ingestion_complete now handles status='error' to unblock jobs
- VLM describe tool now uses trace_id with pre-extracted face crops
2026-07-20 21:48:27 +08:00
Accusys 244af51edf docs: add file status sync API delivery document for Studio team 2026-07-19 22:00:34 +08:00
Accusys 2ed56b7973 feat: add sync-status endpoint to update file status based on processor files 2026-07-19 21:58:03 +08:00
Accusys 84dda3b5bc docs: add full file paths section to pose/appearance POC document 2026-07-19 20:45:42 +08:00
Accusys d0b7bfa56b feat: add pose and appearance endpoints for face detail display
- Add GET /api/v1/file/:file_uuid/pose endpoint
- Add GET /api/v1/file/:file_uuid/appearance endpoint
- Keypoint deduplication (highest confidence)
- Add face-groups endpoint for Studio integration
- Add profile routes (trace-profile, file-profile)
- Update API documentation
2026-07-19 20:43:35 +08:00
Accusys 455c6b81e6 fix: improve search ranking for semantic results
- Lower title match score: 0.9 → 0.75
- Boost keyword score: capped 0.1 → boosted 0.1-0.85
- Skip text_match filter for semantic-only results
- Semantic results now rank higher than title/keyword

Verification:
- 'unfamiliar technology': 5 semantic results (score 0.68-0.74)
- 'storage': 60 semantic + 45 keyword + 60 title merged
- 'AUDIO MONITORING': 2 keyword (0.94) + 3 semantic (0.66-0.69)
2026-07-19 14:40:58 +08:00
Accusys 3c924e1f83 fix: agent search uses smart_search for content queries
- Update system prompt to prioritize smart_search over find_file
- Add source prefix ([OCR], [ASRX], [ASRX+OCR]) to exec_smart_search results
- Users can now see chunk content with source indicators
2026-07-19 14:21:12 +08:00
Accusys 87aa7e0c40 feat: add source prefix to search results
- Add [OCR], [ASRX], [ASRX+OCR] prefix to text_content
- Add content field to SemanticSearchResult struct
- Update SQL queries to include content field
- Helps users distinguish the source of search results
2026-07-19 14:06:06 +08:00
Accusys 5e83ee7dac fix: keyword search for OCR text
- Fix OCR confidence threshold: 0.5 → 0.2
- Fix fetch_ocr_texts frame calculation (use start_frame from DB)
- Fix text_match filter to use text_content instead of summary
- Process all merged results (not just top 30) to include keyword results
- Add logging for keyword search debugging

Fixes issue where OCR text like 'AUDIO MONITORING' and 'thunderbolt'
could not be found via keyword search.
2026-07-19 13:33:31 +08:00
Accusys 0d9aa7b470 refactor: agent search defaults to all files, no longer asks which file
- Removed '先確認用戶在問哪部影片' — no longer asks users to identify file
- Removed all 反问/ask-back rules (specify-type, ask-type, suggestions)
- Rule 1 now: 所有查詢都預設搜尋全部影片
- Smart/universal search file_uuid parameter noted as optional
- Simplified system prompt by ~40% (19 lines vs 47 lines)
2026-07-11 09:26:55 +08:00
Accusys e4b8e9924c fix: agent_search tool descriptions + HAND_OBJECT casing + rule2 edge types
- agent_search.rs: text_region -> text_trace, removed skin_tone_trace
  (never built, misleading LLM agents)
- tkg.rs + scan.rs: hand_object -> HAND_OBJECT for consistency with
  all other edge types (CO_OCCURS_WITH, SPEAKS_AS, etc.)
- rule2_ingest.rs: added LIP_SYNC and HAND_OBJECT to edge_type
  priority list so these edges generate relationship chunks
2026-07-11 08:50:38 +08:00
Accusys 7635ad8d4d fix: correct TKG node_type/edge_type mismatches in scan.rs and tkg.rs
Mismatches fixed (builder vs query layer):
- 'text_region' -> 'text_trace' (3 locations: scan.rs count + tkg.rs LIP_SYNC
  query + tkg.rs LIP_SYNC node ID lookup)
- 'yolo_object' -> 'object' (2 queries in scan.rs)
- 'FACE_TO_FACE' -> CO_OCCURS_WITH + JOIN with tkg_nodes (2 queries in
  scan.rs, so face_face_edges correctly counts only face-track pairs)
- 'HAND_OBJECT' -> 'hand_object' (2 queries in scan.rs)

Impact: ingestion_status and file stats endpoints now return accurate
counts; LIP_SYNC edges now resolve their target text nodes.
2026-07-11 08:40:24 +08:00
Accusys b98a362de5 refactor: cargo fmt across 27 files + behavioral fixes
Behavioral changes:
- postgres_db: reorder default processors (cut first), prevent overwriting completed status
- qdrant_db: fix scroll pagination exit condition (next.is_none())
- job_worker: add idempotency guards for face trace / TKG build; better error logging
- main.rs: add LineWriter for stdout buffering

Remaining diff is cargo fmt reformatting (line wrapping, import ordering).
2026-07-11 02:03:28 +08:00
Accusys 701727fd08 test(tkg): add integration tests for TKG optimizations
- test_build_frame_index: verifies frame-to-point index creation
- test_build_frame_index_empty: edge case for empty input
- test_find_trace_in_frame_exact_match: exact bbox match
- test_find_trace_in_frame_closest_match: closest bbox match
- test_find_trace_in_frame_no_matching_frame: missing frame returns None
- test_find_trace_in_frame_empty_index: empty index returns None
- test_node_id_map_lookup: verifies HashMap-based node ID lookup

Note: Pre-existing test compilation errors (E0063 in asr/asrx/face) block test execution.
2026-07-10 07:44:37 +08:00
Accusys 01f8b89636 perf(tkg): batch INSERT for co_occurrence edges using QueryBuilder
- Collect edges in Vec first, then batch insert in chunks of 100
- Uses sqlx::query_builder::QueryBuilder for efficient batch INSERT
- Reduces SQL round-trips from O(edges) to O(edges/100)
- Maintains ON CONFLICT DO UPDATE semantics
2026-07-10 01:10:14 +08:00
Accusys 2da0ada34a feat(tkg): add Qdrant retry logic with exponential backoff
- scroll_face_points now retries up to 3 times on failure
- Exponential backoff: 1s, 2s, 4s between attempts
- Logs retry attempts for debugging
- Prevents silent failures when Qdrant is temporarily unavailable
2026-07-10 00:58:30 +08:00
Accusys 13abb15595 refactor(tkg): extract shared t() function to schema module
- Create src/core/db/schema.rs with t() and table_name() functions
- Remove duplicate t() from tkg.rs and trace_agent_api.rs
- postgres_db.rs uses schema::table_name() from shared module
2026-07-10 00:08:12 +08:00
Accusys 5a941f857c refactor(tkg): remove dead code - FaceTraceRow, FaceDetectionRow, build_face_track_nodes_from_face_json 2026-07-09 23:45:59 +08:00
Accusys 1ac0a144d5 perf(tkg): pre-build node ID map to eliminate N+1 SQL in edge builders
- Add build_node_id_map() to fetch all node IDs once into HashMap
- Pass node_id_map to all 6 edge builders
- build_co_occurrence_edges: uses map lookup instead of per-face SELECT
- build_speaker_face_edges: uses map lookup instead of per-trace SELECT
- Remove redundant Qdrant scroll in build_co_occurrence_edges
- Expected: 90%+ reduction in SQL queries during edge building
2026-07-09 23:44:06 +08:00
Accusys 356edb16be perf(tkg): eliminate N+1 Qdrant HTTP requests in TKG builders
- Add build_frame_index() for O(1) frame-to-face lookup
- Add find_trace_in_frame() for in-memory bbox matching
- Share face_points across all 6 TKG builders (was 4x redundant fetches)
- Remove get_trace_for_face() (31,666 HTTP requests → 0)
- Change Qdrant scroll error from warn to error
- build_lip_track_nodes_from_pg: uses frame_index instead of per-face HTTP
- build_appearance_trace_nodes: uses frame_index instead of per-face HTTP
- Expected speedup: ~200-600x for lip_track build on large files
2026-07-09 16:05:50 +08:00
Accusys 149226ff17 fix: production service auto-start via launchd + fix worker retry loop
- Replaced old wrapper script with two launchd plists (server + worker)
- Both services auto-start at login via RunAtLoad + KeepAlive
- Removed MOMENTRY_FORCE_RETRY=true from .env to prevent infinite retries
- Added REDIS_URL and all required env vars to launchd plists
- Truncated 3.6GB of oversized logs (2.3G worker + 1.3G launchd stdout)
- Deleted 1214 orphaned processor_results records
- Updated run-server-3002.sh to manage via launchctl
- Removed obsolete wrapper scripts
2026-07-07 05:41:09 +08:00
Accusys bb606f52f5 feat: independent TKG processing mechanism with operation logging
- Created src/core/tkg/ module with service, log, and models
- Added tkg_operation_log table for tracking all TKG operations
- TkgService: build, rebuild (with force option), delete, get_operations
- Updated rebuild endpoint with force parameter
- Added GET /api/v1/file/:file_uuid/tkg for operation history
- Added DELETE /api/v1/file/:file_uuid/tkg for TKG deletion
- TKG rebuild now always triggers Rule 2 (even with 0 edges)
- Full audit trail for all TKG operations (create/update/delete/rebuild)
2026-07-07 03:15:59 +08:00
Accusys f56bdb7fbb fix: ingestion-status shows all steps done when job is completed
When a job status is 'completed', all ingestion steps are marked as 'done'
regardless of node/edge counts. This fixes the UI showing pending steps
for completed jobs with 0 nodes/edges (e.g., videos with single speaker).
2026-07-06 22:08:11 +08:00
Accusys 3067896b0f fix: comprehensive fixes for ingestion and TKG
1. Add Redis PipelineProgress cleanup in unregister_internal
   - Deletes progress key when file is unregistered

2. Fix ingestion_complete asr_status query
   - Query ASRX first, then ASR
   - Filter out NULL values to avoid false negatives

3. Fix TKG 0 nodes/edges support
   - TKG is considered done if face traces are complete
   - TKG may create 0 nodes/edges for videos with minimal content

4. Add ASRX fallback to ASR segments
   - When ASRX has 0 segments but ASR has segments, use ASR segments
   - Ensures at least one ASRX output when ASR has content
2026-07-06 21:14:08 +08:00
Accusys 146d3cedb2 fix: update PipelineProgress when job completes via essential_completed path
When a job completes via the essential_completed branch (all essential
processors done but some non-essential failed), the PipelineProgress
was not being updated to 100%, causing a discrepancy where the status
showed 'completed' but progress showed <100%.

Now PipelineProgress.mark_completed() is called in both completion paths.
2026-07-06 19:08:56 +08:00
Accusys 765db8ae9f fix: TKG FPS calculation for ASRX fallback segments
When ASRX falls back to ASR segments, end_frame may be 0.
The FPS calculation now handles this case correctly by checking
both end_frame > 0 and end_time > 0 before dividing.

This prevents division by zero and incorrect FPS values when
processing videos with ASRX fallback segments.
2026-07-06 16:08:32 +08:00
Accusys dd63dbff9b fix: support videos with no audio or no faces
1. trace_done now checks for 'no_faces' status in face_traced.json
   - Videos with no detected faces now complete correctly
   - Previously stuck because trace_count=0 returned false

2. ASRX fallback to ASR segments includes start_frame/end_frame
   - Added _convert_asr_segments_to_asrx helper function
   - TKG can now process fallback segments correctly

This allows processing of:
- Videos with no audio track (ASR: no_audio_track)
- Videos with no faces (face_traced.json: no_faces)
2026-07-06 15:54:14 +08:00
Accusys 27660f48e4 fix: ASRX fallback to ASR segments on error instead of silent_audio
When ASRX processing fails (error or exception), it now falls back to
ASR segments instead of returning silent_audio with 0 segments.

This ensures asrx_segments >= asr_segments always, fixing the bug where
ASRX would report 0 segments even when ASR detected speech.
2026-07-06 14:23:53 +08:00
Accusys e4fdbbc18a fix: clear stale PipelineProgress when new job starts
When a file is re-registered, the old PipelineProgress in Redis
was not cleared, causing the Portal to show 'completed' status
even though the new job was still running.

Now PipelineProgress is deleted when a new job starts processing.
2026-07-06 13:07:53 +08:00
Accusys 004ff9ad48 fix: ingestion_complete now checks TKG nodes before completing job
Previously, ingestion_complete only checked ASR and face traces,
causing jobs to complete before TKG was triggered. This resulted in
TKG nodes being 0 even after job completion.

Now ingestion_complete also checks if TKG nodes exist for the file.
The job stays in 'running' status until TKG completes.
2026-07-06 12:24:25 +08:00
Accusys 552f539bdf fix: remove TKG guard to prevent deadlock
The TKG guard was checking if nodes exist before spawning TKG build.
This caused a deadlock when:
1. TKG spawns (async)
2. ingestion_complete checks → false (nodes not yet created)
3. Returns Ok(false), job stays running
4. Next poll: guard checks nodes → no nodes yet (TKG still running)
5. Skips TKG → deadlock!

Since build_tkg uses ON CONFLICT (idempotent), it's safe to call
multiple times. Removed the guard to fix the deadlock.
2026-07-06 11:28:40 +08:00
Accusys 221aa4c4cc fix: worker detects deleted jobs and skips them
When a job is deleted from the database (e.g., by unregister),
the worker now checks if the job still exists before processing.
If the job no longer exists, it skips it instead of getting stuck.

This fixes the issue where unregistering a file would leave the
worker stuck trying to process a non-existent job.
2026-07-06 10:28:11 +08:00
Accusys 799ede5a0e feat: OCR independent chunks + TMDb seed with file_uuid
- Rule 1 now creates OCR-only chunks instead of merging into ASRX
- generate_seed_embeddings.py supports --file-uuid parameter
- get_seeds() filters by file_uuid
- identity_matcher.py uses file_uuid for seed matching
- Push QDRANT_API_KEY to Python subprocesses
- Face clustering uses frame+bbox matching instead of face_id
- Portal uses JWT authentication
- FilesView filter logic fixed
2026-07-06 08:56:56 +08:00
Accusys cb604b74ec docs: update Rule 1 OCR independent chunks documentation
Updated Searchable_Chunk_Rules.md and pipeline.md to reflect:
- Phase 1: ASRX segments (pure speech, NO OCR merge)
- Phase 2: OCR-only chunks (all OCR frames grouped by proximity)
- New stats API steps: rule1_ocr, rule1_ocr_chunks
2026-07-05 23:36:56 +08:00
Accusys e91d51cc5e feat: OCR independent chunks (方案 A) + stats API
Rule 1 now creates OCR chunks separately from ASRX segments:
- Phase 1: ASRX segments (pure speech, NO OCR merge)
- Phase 2: OCR-only chunks (all OCR frames grouped by proximity)

Added OCR statistics to ingestion status API:
- rule1_ocr: shows OCR pre_chunks count
- rule1_ocr_chunks: shows OCR-only chunks count

Example: FilmRiot_test now has 32 ASRX + 3 OCR-only = 35 chunks
Stats: rule1_sentence: 35, rule1_ocr: 30, rule1_ocr_chunks: 3
2026-07-05 23:31:06 +08:00
Accusys 5a3f791ecd feat: only run identity agent if file has seed identities
Identity agent now checks Qdrant _seeds collection for the file's
seed identity photos before running. If no seeds exist, the agent
is skipped to avoid unnecessary processing.

Flow:
1. Job completes (Face + ASRX done)
2. Check Qdrant _seeds for file_uuid
3. If seeds exist → run identity agent
4. If no seeds → skip identity agent
2026-07-05 22:22:22 +08:00
Accusys 0b82aa875c feat: Rule 1 now creates chunks for OCR-only text
Previously Rule 1 only created chunks from ASRX segments, merging OCR
text where frame ranges overlapped. OCR text that didn't overlap with
any ASRX segment was ignored.

Now Rule 1 has two phases:
1. Process ASRX segments (merge OCR where overlapping) - existing behavior
2. Create chunks for OCR-only text (frames not covered by ASRX)

OCR-only chunks are grouped by consecutive frames (within 5 frames)
to avoid creating too many single-frame chunks.

Example: ASRX 819 + OCR-only 4 = 823 sentence chunks
2026-07-05 22:06:35 +08:00
Accusys 465552f8b2 fix: remove duplicate 'asr' pre_chunks storage in ASRX handler
Bug: ASRX handler stored pre_chunks as BOTH 'asrx' and 'asr' types.
This caused confusion because Rule 1 queries 'asrx' type, but only
'asr' type existed in the database (asrx type was deleted or never stored).

Fix: Remove the duplicate 'asr' storage (lines 530-542).
ASRX handler now only stores 'asrx' type pre_chunks to workspace SQLite.
PostgreSQL pre_chunks are stored by processor.rs with correct 'asrx' type.

This ensures Rule 1 can find ASRX pre_chunks correctly.
2026-07-05 19:49:03 +08:00