Compare commits

...

22 Commits

Author SHA1 Message Date
Accusys
3d13d1390e Merge branch 'main' of http://192.168.110.200:3000/admin/momentry_core 2026-05-29 23:14:14 +08:00
Accusys
04cbb71ca0 docs: save handoff - library page flash & filter fix 2026-05-29 23:12:09 +08:00
Accusys
e96cc8c8de docs: record WordPress API URL update session progress 2026-05-29 19:06:15 +08:00
Accusys
127d646ef1 fix: worker processor_results + rule3 SQL + unregister cleanup bugs
- job_worker.rs: add upsert_processor_result when output file exists
- job_worker.rs: add load JSON and store to pre_chunks when output exists
- rule3_ingest.rs: fix SQL bind order (scene_number was occupying chunk_type slot)
- files.rs: fix unregister WHERE clause (uuid -> file_uuid) + add pre_chunks delete
- asrx_self/main_fixed.py: fix KeyError (s['start'] -> s['start_time'])
- wrapper_worker_playground.sh: add Worker launchd script
- com.momentry.playground.plist: add Playground launchd config
2026-05-26 04:35:51 +08:00
Accusys
87dead7f65 fix: POST /api/v1/jobs 500 — wrong column names + NULL file_name 2026-05-25 10:50:37 +08:00
Accusys
20dae387ee docs: sync case-insensitive variant 2026-05-25 10:31:37 +08:00
Accusys
b9e93c6293 docs: update API Ref (V4.2), CHANGELOG, Release Notes for de88fd4e 2026-05-25 10:31:32 +08:00
Accusys
de88fd4e44 fix: restore accidentally deleted type definitions
Add back PipelineType enum, ProcessorType::pipeline() method, and
OLLAMA_URL/EMBED_URL/LLM_HEALTH_URL config constants — all of
which were deleted in commits 78923a89 and 0856b92e while the
referencing code was left intact, causing 5 compilation errors.
2026-05-25 08:50:53 +08:00
Accusys
d7f89a962b fix: frame_number is BIGINT in DB, use i64 not i32
frame_number column in face_detections table is defined as BIGINT (INT8).
Using i32 caused sqlx type mismatch at runtime. Fixed in:
- identity_agent_api.rs: query_as tuples and HashMap key
- qdrant_db.rs: upsert_face_embedding signature and row extraction
2026-05-25 04:07:30 +08:00
M5Max128
25ec1625df Merge branch 'main' of 10.10.10.201:/Users/accusys/momentry_core_0.1/ 2026-05-25 03:59:54 +08:00
M5Max128
0806d44df4 fix: add status/duration/fps to FileDetailResponse; fix progress API with HSET+HGETALL 2026-05-25 03:40:02 +08:00
M5Max128
29eabf6d88 chore: remove swift build artifacts from tracking 2026-05-25 03:37:19 +08:00
Accusys
a2b71fef0d fix: i64→i32 for INT4 cols (identity_binding, identity_agent, qdrant_db) 2026-05-25 03:18:50 +08:00
Accusys
8fdd1d741b fix: stranger_id=NULL on bind/merge; doc: add traces+mergeinto endpoints 2026-05-25 03:03:27 +08:00
M5Max128
78923a8973 fix: system consistency - store_vector, search, worker trigger
- store_vector: stub -> actual PG embedding storage
- search_parent_chunks_semantic: include sentence chunks
- Remove early return in check_and_complete_job
2026-05-24 23:20:02 +08:00
M5Max128
932e43518d fix: trigger_processing — remove fake QUEUED state, create monitor_job if missing
- Remove SET processing_status = 'QUEUED' (no queue exists)
- Fix COALESCE type mismatch (jsonb vs text)
- Fix UPDATE WHERE id =  should be WHERE uuid =
- Check monitor_jobs existence, INSERT if missing via create_monitor_job
- Add UNIQUE constraint on monitor_jobs.uuid
- Fix response message: 'Processing queued' → 'Processing triggered'
2026-05-23 23:06:37 +08:00
M5Max128
5d8449b07c fix: compile processing.rs + mount processing_routes
- Fix 9 compilation errors in processing.rs:
  - memory_mb typo (mem_mb)
  - download_json return type
  - Chunk from_row (use row_to_json)
  - ProgressResponse/SystemHealthInfo/ProcessorProgressInfo Deserialize
  - Remove flush_all/flush (methods don't exist)
- Add pub mod processing to api/mod.rs
- Merge processing::processing_routes() into server router
2026-05-23 22:40:19 +08:00
M5Max128
0856b92ec6 fix: resource path cleanup + mount processing_routes WIP
- config.rs: SCRIPTS_DIR fix, EMBED/OLLAMA_URL 127.0.0.1, PYTHON_PATH restored
- executor.rs: use config::PYTHON_PATH instead of hardcoded path
- probe.rs/watcher.rs: use config::SCRIPTS_DIR instead of hardcoded path
- release.rs: momentry_core_0.1 → momentry_core
- .env.development: fix REDIS_URL host, PYTHON_PATH, SCRIPTS_DIR
- api/mod.rs + server.rs: add processing module declaration (routes not yet mountable due to pre-existing compile errors)
2026-05-23 22:26:03 +08:00
M5Max128
f8bcc0356c feat: frame/time pipeline split + output validation
- Add PipelineType enum + pipeline() to ProcessorType
- Split ProcessorPool into frame_slots (max 2) and time_slots (max 1)
- Add can_start_for() for pipeline-aware scheduling
- Add validate_output_file() — checks JSON validity before marking complete
- Add 3 unit tests for validate_output_file()
- Create DESIGN/FRAME_TIME_PIPELINE_V1.0.md (492 lines)
2026-05-23 21:14:28 +08:00
M5Max128
dddb5d4cbd refactor: centralize port config + fix 8082 conflict
- Add EMBED_URL, OLLAMA_URL, LLM_HEALTH_URL to config.rs
- Fix health.rs hardcoded ports → config references
- Fix sync_db.rs Ollama URL → config::OLLAMA_URL
- Create config/port_registry.tsv (single source of truth for ports)
- Remove Caddy 8082 proxy block (port belongs to LLM)
- Fix .env LLM_URL: localhost → 127.0.0.1 (avoid IPv6 Caddy conflict)
2026-05-23 02:54:34 +08:00
M5Max128
a008bb865b feat: add Gitea to startup script, update AGENTS.md token
- Add Gitea (port 3000) as step 10 in startup script
- Update AGENTS.md Gitea token record
2026-05-23 02:37:19 +08:00
M5Max128
1c30af9557 fix: correct service paths, nohup removal, MongoDB graceful fallback, add MariaDB + Caddy to startup
- Fix Qdrant binary path (services/ -> momentry_resources/bin/)
- Fix LLM binary/model paths (llama/ -> momentry_resources/llama/, models/ -> models/llm/)
- Fix PostgreSQL data path (pgsql/data -> momentry/var/postgresql)
- Remove nohup (fails in LaunchDaemon environment)
- Add MongoDB graceful fallback with 5s timeout in server.rs
- Add MariaDB + Caddy steps to startup script for WordPress
- Revert all unrelated changes
2026-05-23 01:46:23 +08:00
4857 changed files with 1862 additions and 163450 deletions

View File

@@ -23,8 +23,8 @@ MONGODB_URL=mongodb://localhost:27017
MONGODB_DATABASE=momentry_dev
# Redis (already isolated via prefix)
REDIS_URL=redis://:accusys@localhost:6379
REDIS_PASSWORD=accusys
REDIS_URL=redis://127.0.0.1:6379
# REDIS_PASSWORD not set - Redis has no password configured
# Qdrant Vector Database - Collection isolation
QDRANT_URL=http://localhost:6333
@@ -37,8 +37,8 @@ MOMENTRY_BACKUP_DIR=/Users/accusys/momentry/backup/momentry_dev
MOMENTRY_SFTP_ROOT=/Users/accusys/momentry/var/sftpgo/data/demo/
# Python (for processing scripts)
MOMENTRY_PYTHON_PATH=/opt/homebrew/bin/python3.11
MOMENTRY_SCRIPTS_DIR=/Users/accusys/momentry_core_0.1/scripts
MOMENTRY_PYTHON_PATH=/Users/accusys/momentry_core/venv/bin/python
MOMENTRY_SCRIPTS_DIR=/Users/accusys/momentry_core/scripts
# Logging
RUST_LOG=debug
@@ -73,7 +73,7 @@ REDIS_CACHE_TTL_VIDEO_META=3600
TMDB_API_KEY=e9cde52197f6f8df4d9db99da93db1fb
MOMENTRY_TMDB_PROBE_ENABLED=true
# LLM for 5W1H summary (points to M5 Gemma4)
MOMENTRY_LLM_SUMMARY_URL=http://localhost:8082/v1/chat/completions
MOMENTRY_LLM_SUMMARY_URL=http://127.0.0.1:8082/v1/chat/completions
MOMENTRY_LLM_SUMMARY_MODEL=google_gemma-4-26B-A4B-it-Q5_K_M.gguf
MOMENTRY_LLM_SUMMARY_ENABLED=true

2
.gitignore vendored
View File

@@ -16,3 +16,5 @@ node_modules/
*.log
/tmp/
*.log
scripts/swift_processors/.build/

View File

@@ -581,8 +581,7 @@ git push origin main
| 機器 | Token |
|------|-------|
| M5Max128 | `a7cf946148063c2bfa8d59ad629ae541813f0db8` (write:repository) |
| M5Max128 (admin) | `b388aec114a93ae3ce752acf16a9ce678144541b` (write:repository + write:user) |
| M5Max128 | `c33768c4cc26c0f4c575dcce832e92e5cf192773` (write:repository + write:user) |
**注意**: Token 有 write:repository scope勿外洩。如需新增 token 給其他機器,各自產自己的 token。

22
config/port_registry.tsv Normal file
View File

@@ -0,0 +1,22 @@
# Port Registry - Momentry Core
# Each port must have exactly one owner.
# Before adding a service: pick a free port, add a row here, then configure.
#
# Port Service Owner Config Key Default Source
22 ssh sshd - - macOS
80 http Caddy - - Caddyfile
443 https Caddy - - Caddyfile
2019 caddy-admin Caddy - - Caddyfile (internal)
3000 gitea gitea - 3000 start_momentry.sh
3002 production momentry MOMENTRY_SERVER_PORT 3002 run-server-3002.sh
3003 playground momentry_playground MOMENTRY_SERVER_PORT 3003 start_momentry.sh
3200 dashboard Caddy - - Caddyfile
3306 mariadb mariadbd - 3306 start_momentry.sh
5432 postgresql postgres DATABASE_URL postgres://...:5432 start_momentry.sh
6379 redis redis-server REDIS_URL redis://...:6379 start_momentry.sh
6333 qdrant qdrant QDRANT_URL http://...:6333 start_momentry.sh
8081 wordpress Caddy - - Caddyfile
8082 llm llama-server MOMENTRY_LLM_CHAT_URL http://...:8082 start_momentry.sh
9000 php-fpm php-fpm - 9000 brew services
11434 ollama ollama MOMENTRY_OLLAMA_URL http://...:11434 start_momentry.sh
11436 embedding embeddinggemma MOMENTRY_EMBED_URL http://...:11436 start_momentry.sh
1 # Port Registry - Momentry Core
2 # Each port must have exactly one owner.
3 # Before adding a service: pick a free port, add a row here, then configure.
4 #
5 # Port Service Owner Config Key Default Source
6 22 ssh sshd - - macOS
7 80 http Caddy - - Caddyfile
8 443 https Caddy - - Caddyfile
9 2019 caddy-admin Caddy - - Caddyfile (internal)
10 3000 gitea gitea - 3000 start_momentry.sh
11 3002 production momentry MOMENTRY_SERVER_PORT 3002 run-server-3002.sh
12 3003 playground momentry_playground MOMENTRY_SERVER_PORT 3003 start_momentry.sh
13 3200 dashboard Caddy - - Caddyfile
14 3306 mariadb mariadbd - 3306 start_momentry.sh
15 5432 postgresql postgres DATABASE_URL postgres://...:5432 start_momentry.sh
16 6379 redis redis-server REDIS_URL redis://...:6379 start_momentry.sh
17 6333 qdrant qdrant QDRANT_URL http://...:6333 start_momentry.sh
18 8081 wordpress Caddy - - Caddyfile
19 8082 llm llama-server MOMENTRY_LLM_CHAT_URL http://...:8082 start_momentry.sh
20 9000 php-fpm php-fpm - 9000 brew services
21 11434 ollama ollama MOMENTRY_OLLAMA_URL http://...:11434 start_momentry.sh
22 11436 embedding embeddinggemma MOMENTRY_EMBED_URL http://...:11436 start_momentry.sh

View File

@@ -2,15 +2,15 @@
document_type: "reference_doc"
service: "MOMENTRY_CORE"
title: "Momentry Core Release API Reference v1.0.0"
date: "2026-05-14"
version: "V4.1"
date: "2026-05-25"
version: "V4.2"
status: "active"
owner: "Warren"
---
# Momentry Core API Reference v1.0.0
58 endpoints across 10 categories, with real curl examples and responses.
55 endpoints across 10 categories, with real curl examples and responses.
## Base
@@ -30,12 +30,13 @@ owner: "Warren"
|---|--------|------|-------------|
| 1 | GET | `/health` | Server status (ok/degraded) |
| 2 | GET | `/health/detailed` | Per-service health + latency |
| 3 | POST | `/api/v1/auth/login` | Username/password → API key |
| 4 | POST | `/api/v1/auth/logout` | Invalidate session |
| 5 | GET | `/api/v1/stats/ingest` | Ingest statistics |
| 3 | GET | `/health/consistency` | Data consistency check |
| 4 | POST | `/api/v1/auth/login` | Username/password → API key |
| 5 | POST | `/api/v1/auth/logout` | Invalidate session |
| 6 | GET | `/api/v1/stats/sftpgo` | SFTPGo status |
| 7 | GET | `/api/v1/stats/inference` | LLM/Embedding health |
| 8 | POST | `/api/v1/config/cache` | Toggle Redis cache |
| 7 | POST | `/api/v1/config/cache` | Toggle Redis cache |
| 8 | POST | `/api/v1/config/auto-pipeline` | Toggle auto-pipeline on register |
| 9 | POST | `/api/v1/config/watcher-auto-register` | Toggle watcher auto-register |
```bash
curl http://localhost:3002/health
@@ -44,8 +45,8 @@ curl http://localhost:3002/health
{
"status": "ok",
"version": "1.0.0",
"build_git_hash": "26f2434",
"build_timestamp": "2026-05-14T09:09:17Z",
"build_git_hash": "de88fd4e",
"build_timestamp": "2026-05-25",
"uptime_ms": 7052517
}
```
@@ -68,8 +69,8 @@ Supports all file types (video, image, document, audio). SHA256 content_hash com
```json
{
"status": "ok",
"build_git_hash": "26f2434",
"build_timestamp": "2026-05-14T09:09:17Z",
"build_git_hash": "de88fd4e",
"build_timestamp": "2026-05-25",
"services": {
"postgres": {"status": "ok", "latency_ms": 6},
"redis": {"status": "ok", "latency_ms": 0},
@@ -103,17 +104,17 @@ Supports all file types (video, image, document, audio). SHA256 content_hash com
| # | Method | Path | Description |
|---|--------|------|-------------|
| 9 | POST | `/api/v1/files/register` | Register file → file_uuid. Body: `{"file_path":"...", "content_hash":"optional"}` |
| 10 | GET | `/api/v1/files/lookup?file_name=` | Pre-upload name conflict check. Returns matches + `next_name` for auto-rename |
| 11 | POST | `/api/v1/unregister` | Unregister file(s): by `file_uuid` or pattern match (`file_path`+`pattern`) |
| 12 | GET | `/api/v1/files/scan` | Scan directory for new files |
| 13 | GET | `/api/v1/files` | List files (paginated) |
| 14 | GET | `/api/v1/file/:file_uuid` | Single file detail |
| 15 | GET | `/api/v1/file/:file_uuid/probe` | ffprobe metadata |
| 16 | POST | `/api/v1/file/:file_uuid/process` | Start pipeline |
| 17 | GET | `/api/v1/file/:file_uuid/chunk/:chunk_id` | Single chunk detail (V1.0.2+) |
| 18 | GET | `/api/v1/progress/:file_uuid` | Processing progress |
| 19 | GET | `/api/v1/jobs` | Monitor jobs (filterable) |
| 10 | POST | `/api/v1/files/register` | Register file → file_uuid. Body: `{"file_path":"...", "content_hash":"optional"}` |
| 11 | GET | `/api/v1/files/lookup?file_name=` | Pre-upload name conflict check. Returns matches + `next_name` for auto-rename |
| 12 | POST | `/api/v1/unregister` | Unregister file(s): by `file_uuid` or pattern match (`file_path`+`pattern`) |
| 13 | GET | `/api/v1/files/scan` | Scan directory for new files |
| 14 | GET | `/api/v1/files` | List files (paginated) |
| 15 | GET | `/api/v1/file/:file_uuid` | Single file detail |
| 16 | GET | `/api/v1/file/:file_uuid/probe` | ffprobe metadata |
| 17 | POST | `/api/v1/file/:file_uuid/process` | Start pipeline |
| 18 | POST | `/api/v1/file/:file_uuid/chunk/:chunk_id` | Single chunk detail (V1.0.2+) |
| 19 | POST | `/api/v1/progress/:file_uuid` | Processing progress |
| 20 | POST | `/api/v1/jobs` | Monitor jobs (filterable) |
```bash
curl -X POST http://localhost:3002/api/v1/files/register -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" -H "Content-Type: application/json" -d '{"file_path":"/Users/accusys/momentry/var/sftpgo/data/demo/video.mp4"}'
@@ -154,14 +155,14 @@ curl "http://localhost:3002/api/v1/files?page=1&page_size=2" -H "X-API-Key: muse
| # | Method | Path | Description |
|---|--------|------|-------------|
| 20 | POST | `/api/v1/search/visual` | Visual chunk search |
| 21 | POST | `/api/v1/search/visual/class` | By object class |
| 22 | POST | `/api/v1/search/visual/density` | By spatial density |
| 23 | POST | `/api/v1/search/visual/combination` | Combined visual search |
| 24 | POST | `/api/v1/search/visual/stats` | Visual stats |
| 25 | POST | `/api/v1/search/smart` | Semantic (EmbeddingGemma + pgvector) |
| 26 | POST | `/api/v1/search/universal` | BM25 keyword (requires file_uuid) |
| 27 | POST | `/api/v1/search/frames` | Frame-level search |
| 21 | POST | `/api/v1/search/visual` | Visual chunk search |
| 22 | POST | `/api/v1/search/visual/class` | By object class |
| 23 | POST | `/api/v1/search/visual/density` | By spatial density |
| 24 | POST | `/api/v1/search/visual/combination` | Combined visual search |
| 25 | POST | `/api/v1/search/visual/stats` | Visual stats |
| 26 | POST | `/api/v1/search/smart` | Semantic (EmbeddingGemma + pgvector) |
| 27 | POST | `/api/v1/search/universal` | BM25 keyword (requires file_uuid) |
| 28 | POST | `/api/v1/search/frames` | Frame-level search |
```bash
curl -X POST http://localhost:3002/api/v1/search/universal -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" -H "Content-Type: application/json" -d '{"query":"name","limit":2,"mode":"bm25","file_uuid":"3abeee81d94597629ed8cb943f182e94"}'
@@ -183,10 +184,10 @@ curl -X POST http://localhost:3002/api/v1/search/universal -H "X-API-Key: muser
| # | Method | Path | Description |
|---|--------|------|-------------|
| 28 | POST | `/api/v1/file/:file_uuid/face_trace/sortby` | List traces (sorted/filtered) |
| 29 | GET | `/api/v1/file/:file_uuid/trace/:trace_id/faces` | Trace detections (+ interpolation) |
| 29 | POST | `/api/v1/file/:file_uuid/traces` | List traces (sorted/filtered) |
| 30 | GET | `/api/v1/file/:file_uuid/trace/:trace_id/faces` | Trace detections (+ interpolation) |
### sortby — list traces
### traces — list traces
Parameters:
- `sort_by`: `face_count` | `duration` | `first_appearance`
@@ -194,7 +195,7 @@ Parameters:
- `limit`: max results
```bash
curl -X POST "http://localhost:3002/api/v1/file/3abeee81d94597629ed8cb943f182e94/face_trace/sortby" -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" -H "Content-Type: application/json" -d '{"sort_by":"face_count","limit":2}'
curl -X POST "http://localhost:3002/api/v1/file/3abeee81d94597629ed8cb943f182e94/traces" -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" -H "Content-Type: application/json" -d '{"sort_by":"face_count","limit":2}'
```
```json
{"success":true,"total_traces":6892,"total_faces":108204,"traces":[
@@ -224,10 +225,10 @@ curl "http://localhost:3002/api/v1/file/3abeee81d94597629ed8cb943f182e94/trace/2
| # | Method | Path | Description |
|---|--------|------|-------------|
| 30 | GET | `/api/v1/file/:file_uuid/thumbnail` | Frame JPEG (?frame=&x=&y=&w=&h=) |
| 31 | GET | `/api/v1/file/:file_uuid/video` | Raw video stream. Dual input: `?start_time=&end_time=` (seconds) or `?start_frame=&end_frame=` (frames). |
| 32 | GET | `/api/v1/file/:file_uuid/video/bbox` | Bbox overlay. `?start_frame=&end_frame=&face_uuid=&duration=` (all frame numbers). Dual input via `start_time`/`end_time`. |
| 33 | GET | `/api/v1/file/:file_uuid/trace/:trace_id/video` | Trace clip (?mode=&padding=&audio=) |
| 31 | GET | `/api/v1/file/:file_uuid/thumbnail` | Frame JPEG (?frame=&x=&y=&w=&h=) |
| 32 | GET | `/api/v1/file/:file_uuid/video` | Raw video stream. Dual input: `?start_time=&end_time=` (seconds) or `?start_frame=&end_frame=` (frames). |
| 33 | GET | `/api/v1/file/:file_uuid/video/bbox` | Bbox overlay. `?start_frame=&end_frame=&face_uuid=&duration=` (all frame numbers). Dual input via `start_time`/`end_time`. |
| 34 | GET | `/api/v1/file/:file_uuid/trace/:trace_id/video` | Trace clip (?mode=&padding=&audio=) |
All video endpoints support:
- `mode=normal|debug` (default: `normal`)
@@ -260,16 +261,16 @@ Green bbox per face detection: actual frames `thickness=4`, interpolated `thickn
| # | Method | Path | Description |
|---|--------|------|-------------|
| 33 | GET | `/api/v1/identities` | List all identities |
| 34 | GET | `/api/v1/file/:file_uuid/identities` | Identities in a file |
| 35 | POST | `/api/v1/identity` | Register new identity |
| 36 | GET | `/api/v1/identity/:identity_uuid` | Identity detail |
| 37 | DELETE | `/api/v1/identity/:identity_uuid` | Delete identity |
| 38 | GET | `/api/v1/identity/:identity_uuid/files` | Files for identity |
| 39 | GET | `/api/v1/identity/:identity_uuid/chunks` | Chunks for identity |
| 40 | GET | `/api/v1/faces/candidates` | Unbound face gallery |
| 41 | GET | `/api/v1/identities/search?q=` | Search identities by name → chunks |
| 42 | GET | `/api/v1/search/identity_text?q=&file_uuid=` | Full-text search → identity-bound chunks |
| 35 | GET | `/api/v1/identities` | List all identities |
| 36 | GET | `/api/v1/file/:file_uuid/identities` | Identities in a file |
| 37 | POST | `/api/v1/identity` | Register new identity |
| 38 | GET | `/api/v1/identity/:identity_uuid` | Identity detail |
| 39 | DELETE | `/api/v1/identity/:identity_uuid` | Delete identity |
| 40 | GET | `/api/v1/identity/:identity_uuid/files` | Files for identity |
| 41 | GET | `/api/v1/identity/:identity_uuid/chunks` | Chunks for identity |
| 42 | GET | `/api/v1/faces/candidates` | Unbound face gallery |
| 43 | GET | `/api/v1/identities/search?q=` | Search identities by name → chunks |
| 44 | GET | `/api/v1/search/identity_text?q=&file_uuid=` | Full-text search → identity-bound chunks |
```bash
curl "http://localhost:3002/api/v1/identities?page=1&page_size=3" -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69"
@@ -307,9 +308,9 @@ curl "http://localhost:3002/api/v1/faces/candidates?page=1&page_size=2" -H "X-A
| # | Method | Path | Description |
|---|--------|------|-------------|
| 43 | POST | `/api/v1/identity/:identity_uuid/bind` | Bind face → identity |
| 44 | POST | `/api/v1/identity/:identity_uuid/unbind` | Unbind face from identity |
| 45 | POST | `/api/v1/identity/:identity_uuid/mergeinto` | Merge into another identity |
| 45 | POST | `/api/v1/identity/:identity_uuid/bind` | Bind face → identity |
| 46 | POST | `/api/v1/identity/:identity_uuid/unbind` | Unbind face from identity |
| 47 | POST | `/api/v1/identity/:identity_uuid/mergeinto` | Merge into another identity |
```bash
curl -X POST "http://localhost:3002/api/v1/identity/a9a90105-6d6b-46ff-92da-0c3c1a57dff4/bind" -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" -H "Content-Type: application/json" -d '{"file_uuid":"3abeee81d94597629ed8cb943f182e94","face_id":"face_42"}'
@@ -324,9 +325,9 @@ curl -X POST "http://localhost:3002/api/v1/identity/a9a90105-6d6b-46ff-92da-0c3c
| # | Method | Path | Description |
|---|--------|------|-------------|
| 46 | POST | `/api/v1/resource/register` | Register processing resource |
| 47 | POST | `/api/v1/resource/heartbeat` | Resource heartbeat |
| 48 | GET | `/api/v1/resources` | List all resources |
| 48 | POST | `/api/v1/resource/register` | Register processing resource |
| 49 | POST | `/api/v1/resource/heartbeat` | Resource heartbeat |
| 50 | GET | `/api/v1/resources` | List all resources |
```bash
curl "http://localhost:3002/api/v1/resources" -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69"
@@ -341,10 +342,10 @@ curl "http://localhost:3002/api/v1/resources" -H "X-API-Key: muser_686008560363
| # | Method | Path | Description |
|---|--------|------|-------------|
| 49 | POST | `/api/v1/agents/translate` | AI text translation |
| 50 | POST | `/api/v1/agents/5w1h/analyze` | Single chunk analysis |
| 51 | POST | `/api/v1/agents/5w1h/batch` | Batch analysis |
| 52 | GET | `/api/v1/agents/5w1h/status` | Job status |
| 51 | POST | `/api/v1/agents/translate` | AI text translation |
| 52 | POST | `/api/v1/agents/5w1h/analyze` | Single chunk analysis |
| 53 | POST | `/api/v1/agents/5w1h/batch` | Batch analysis |
| 54 | GET | `/api/v1/agents/5w1h/status` | Job status |
```bash
curl -X POST "http://localhost:3002/api/v1/agents/translate" -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" -H "Content-Type: application/json" -d '{"text":"Hello world","target_language":"zh-TW"}'
@@ -359,11 +360,10 @@ curl -X POST "http://localhost:3002/api/v1/agents/translate" -H "X-API-Key: mus
| # | Method | Path | Description |
|---|--------|------|-------------|
| 53 | POST | `/api/v1/agents/identity/analyze` | Identify faces in file |
| 54 | GET | `/api/v1/agents/identity/status` | Analysis status |
| 55 | POST | `/api/v1/agents/identity/suggest` | Name suggestions |
| 56 | POST | `/api/v1/agents/suggest/merge` | Suggest merge |
| 57 | POST | `/api/v1/agents/suggest/clustering` | Suggest re-clustering |
| 55 | POST | `/api/v1/agents/identity/match-from-photo` | Match face from photo |
| 56 | POST | `/api/v1/agents/identity/match-from-trace` | Match face from trace |
| 57 | POST | `/api/v1/agents/suggest/merge` | Suggest merge |
| 58 | POST | `/api/v1/agents/suggest/clustering` | Suggest re-clustering |
---
@@ -371,10 +371,11 @@ curl -X POST "http://localhost:3002/api/v1/agents/translate" -H "X-API-Key: mus
| Version | Date | Changes |
|---------|------|---------|
| V4.2 | 2026-05-25 | Removed phantom routes (stats/ingest, stats/inference, agents/identity/status); fixed HTTP methods (chunk, progress, jobs → POST); renamed endpoints (face_trace/sortby → traces, analyze → match-from-photo, suggest → match-from-trace); added config endpoints (consistency, auto-pipeline, watcher-auto-register); updated git hash to de88fd4e |
| V4.1 | 2026-05-14 | Added `build_timestamp` + `resources` + `pipeline` to health APIs; identity search endpoints; trace debug rework (green bbox, text overlay, all traces listed) |
## Related
- `API_DICTIONARY_V1.0.0.md` — Quick reference (58 endpoints)
- `API_DICTIONARY_V1.0.0.md` — Quick reference (55 endpoints)
- `API_DOCUMENTATION_v1.0.0.md` — Detailed spec with examples
- `TRACE/TRACE_API_REFERENCE_V1.0.0.md` — Trace-specific reference

View File

@@ -2,21 +2,21 @@
document_type: "reference_doc"
service: "MOMENTRY_CORE"
title: "Momentry Core Release API Reference v1.0.0"
date: "2026-05-14"
version: "V4.1"
date: "2026-05-25"
version: "V4.2"
status: "active"
owner: "Warren"
---
# Momentry Core API Reference v1.0.0
58 endpoints across 10 categories, with real curl examples and responses.
55 endpoints across 10 categories, with real curl examples and responses.
## Base
| Environment | URL |
|-------------|-----|
| Production | `http://localhost:3002` or `https://m5api.momentry.ddns.net` |
| Production | `http://localhost:3002` or `https://api.momentry.ddns.net` |
| Development | `http://localhost:3003` |
| Auth | Header `X-API-Key: <key>` (login endpoint unprotected) |
@@ -30,14 +30,13 @@ owner: "Warren"
|---|--------|------|-------------|
| 1 | GET | `/health` | Server status (ok/degraded) |
| 2 | GET | `/health/detailed` | Per-service health + latency |
| 3 | POST | `/api/v1/auth/login` | Username/password → API key |
| 4 | POST | `/api/v1/auth/logout` | Invalidate session |
| 5 | GET | `/api/v1/stats/ingest` | Ingest statistics |
| 3 | GET | `/health/consistency` | Data consistency check |
| 4 | POST | `/api/v1/auth/login` | Username/password → API key |
| 5 | POST | `/api/v1/auth/logout` | Invalidate session |
| 6 | GET | `/api/v1/stats/sftpgo` | SFTPGo status |
| 7 | GET | `/api/v1/stats/inference` | LLM/Embedding health |
| 8 | POST | `/api/v1/config/cache` | Toggle Redis cache |
| 9 | POST | `/api/v1/config/auto-pipeline` | Toggle auto-pipeline on register |
| 10 | POST | `/api/v1/config/watcher-auto-register` | Toggle watcher auto-register |
| 7 | POST | `/api/v1/config/cache` | Toggle Redis cache |
| 8 | POST | `/api/v1/config/auto-pipeline` | Toggle auto-pipeline on register |
| 9 | POST | `/api/v1/config/watcher-auto-register` | Toggle watcher auto-register |
```bash
curl http://localhost:3002/health
@@ -46,8 +45,8 @@ curl http://localhost:3002/health
{
"status": "ok",
"version": "1.0.0",
"build_git_hash": "26f2434",
"build_timestamp": "2026-05-14T09:09:17Z",
"build_git_hash": "de88fd4e",
"build_timestamp": "2026-05-25",
"uptime_ms": 7052517
}
```
@@ -70,8 +69,8 @@ Supports all file types (video, image, document, audio). SHA256 content_hash com
```json
{
"status": "ok",
"build_git_hash": "26f2434",
"build_timestamp": "2026-05-14T09:09:17Z",
"build_git_hash": "de88fd4e",
"build_timestamp": "2026-05-25",
"services": {
"postgres": {"status": "ok", "latency_ms": 6},
"redis": {"status": "ok", "latency_ms": 0},
@@ -105,17 +104,17 @@ Supports all file types (video, image, document, audio). SHA256 content_hash com
| # | Method | Path | Description |
|---|--------|------|-------------|
| 9 | POST | `/api/v1/files/register` | Register file → file_uuid. Body: `{"file_path":"...", "content_hash":"optional"}` |
| 10 | GET | `/api/v1/files/lookup?file_name=` | Pre-upload name conflict check. Returns matches + `next_name` for auto-rename |
| 11 | POST | `/api/v1/unregister` | Unregister file(s): by `file_uuid` or pattern match (`file_path`+`pattern`) |
| 12 | GET | `/api/v1/files/scan` | Scan directory for new files |
| 13 | GET | `/api/v1/files` | List files (paginated) |
| 14 | GET | `/api/v1/file/:file_uuid` | Single file detail |
| 15 | GET | `/api/v1/file/:file_uuid/probe` | ffprobe metadata |
| 16 | POST | `/api/v1/file/:file_uuid/process` | Start pipeline |
| 17 | GET | `/api/v1/file/:file_uuid/chunk/:chunk_id` | Single chunk detail (V1.0.2+) |
| 18 | GET | `/api/v1/progress/:file_uuid` | Processing progress |
| 19 | GET | `/api/v1/jobs` | Monitor jobs (filterable) |
| 10 | POST | `/api/v1/files/register` | Register file → file_uuid. Body: `{"file_path":"...", "content_hash":"optional"}` |
| 11 | GET | `/api/v1/files/lookup?file_name=` | Pre-upload name conflict check. Returns matches + `next_name` for auto-rename |
| 12 | POST | `/api/v1/unregister` | Unregister file(s): by `file_uuid` or pattern match (`file_path`+`pattern`) |
| 13 | GET | `/api/v1/files/scan` | Scan directory for new files |
| 14 | GET | `/api/v1/files` | List files (paginated) |
| 15 | GET | `/api/v1/file/:file_uuid` | Single file detail |
| 16 | GET | `/api/v1/file/:file_uuid/probe` | ffprobe metadata |
| 17 | POST | `/api/v1/file/:file_uuid/process` | Start pipeline |
| 18 | POST | `/api/v1/file/:file_uuid/chunk/:chunk_id` | Single chunk detail (V1.0.2+) |
| 19 | POST | `/api/v1/progress/:file_uuid` | Processing progress |
| 20 | POST | `/api/v1/jobs` | Monitor jobs (filterable) |
```bash
curl -X POST http://localhost:3002/api/v1/files/register -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" -H "Content-Type: application/json" -d '{"file_path":"/Users/accusys/momentry/var/sftpgo/data/demo/video.mp4"}'
@@ -156,14 +155,14 @@ curl "http://localhost:3002/api/v1/files?page=1&page_size=2" -H "X-API-Key: muse
| # | Method | Path | Description |
|---|--------|------|-------------|
| 20 | POST | `/api/v1/search/visual` | Visual chunk search |
| 21 | POST | `/api/v1/search/visual/class` | By object class |
| 22 | POST | `/api/v1/search/visual/density` | By spatial density |
| 23 | POST | `/api/v1/search/visual/combination` | Combined visual search |
| 24 | POST | `/api/v1/search/visual/stats` | Visual stats |
| 25 | POST | `/api/v1/search/smart` | Semantic (EmbeddingGemma + pgvector) |
| 26 | POST | `/api/v1/search/universal` | BM25 keyword (requires file_uuid) |
| 27 | POST | `/api/v1/search/frames` | Frame-level search |
| 21 | POST | `/api/v1/search/visual` | Visual chunk search |
| 22 | POST | `/api/v1/search/visual/class` | By object class |
| 23 | POST | `/api/v1/search/visual/density` | By spatial density |
| 24 | POST | `/api/v1/search/visual/combination` | Combined visual search |
| 25 | POST | `/api/v1/search/visual/stats` | Visual stats |
| 26 | POST | `/api/v1/search/smart` | Semantic (EmbeddingGemma + pgvector) |
| 27 | POST | `/api/v1/search/universal` | BM25 keyword (requires file_uuid) |
| 28 | POST | `/api/v1/search/frames` | Frame-level search |
```bash
curl -X POST http://localhost:3002/api/v1/search/universal -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" -H "Content-Type: application/json" -d '{"query":"name","limit":2,"mode":"bm25","file_uuid":"3abeee81d94597629ed8cb943f182e94"}'
@@ -185,10 +184,10 @@ curl -X POST http://localhost:3002/api/v1/search/universal -H "X-API-Key: muser
| # | Method | Path | Description |
|---|--------|------|-------------|
| 28 | POST | `/api/v1/file/:file_uuid/face_trace/sortby` | List traces (sorted/filtered) |
| 29 | GET | `/api/v1/file/:file_uuid/trace/:trace_id/faces` | Trace detections (+ interpolation) |
| 29 | POST | `/api/v1/file/:file_uuid/traces` | List traces (sorted/filtered) |
| 30 | GET | `/api/v1/file/:file_uuid/trace/:trace_id/faces` | Trace detections (+ interpolation) |
### sortby — list traces
### traces — list traces
Parameters:
- `sort_by`: `face_count` | `duration` | `first_appearance`
@@ -196,7 +195,7 @@ Parameters:
- `limit`: max results
```bash
curl -X POST "http://localhost:3002/api/v1/file/3abeee81d94597629ed8cb943f182e94/face_trace/sortby" -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" -H "Content-Type: application/json" -d '{"sort_by":"face_count","limit":2}'
curl -X POST "http://localhost:3002/api/v1/file/3abeee81d94597629ed8cb943f182e94/traces" -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" -H "Content-Type: application/json" -d '{"sort_by":"face_count","limit":2}'
```
```json
{"success":true,"total_traces":6892,"total_faces":108204,"traces":[
@@ -226,10 +225,10 @@ curl "http://localhost:3002/api/v1/file/3abeee81d94597629ed8cb943f182e94/trace/2
| # | Method | Path | Description |
|---|--------|------|-------------|
| 30 | GET | `/api/v1/file/:file_uuid/thumbnail` | Frame JPEG (?frame=&x=&y=&w=&h=) |
| 31 | GET | `/api/v1/file/:file_uuid/video` | Raw video stream. Dual input: `?start_time=&end_time=` (seconds) or `?start_frame=&end_frame=` (frames). |
| 32 | GET | `/api/v1/file/:file_uuid/video/bbox` | Bbox overlay. `?start_frame=&end_frame=&face_uuid=&duration=` (all frame numbers). Dual input via `start_time`/`end_time`. |
| 33 | GET | `/api/v1/file/:file_uuid/trace/:trace_id/video` | Trace clip (?mode=&padding=&audio=) |
| 31 | GET | `/api/v1/file/:file_uuid/thumbnail` | Frame JPEG (?frame=&x=&y=&w=&h=) |
| 32 | GET | `/api/v1/file/:file_uuid/video` | Raw video stream. Dual input: `?start_time=&end_time=` (seconds) or `?start_frame=&end_frame=` (frames). |
| 33 | GET | `/api/v1/file/:file_uuid/video/bbox` | Bbox overlay. `?start_frame=&end_frame=&face_uuid=&duration=` (all frame numbers). Dual input via `start_time`/`end_time`. |
| 34 | GET | `/api/v1/file/:file_uuid/trace/:trace_id/video` | Trace clip (?mode=&padding=&audio=) |
All video endpoints support:
- `mode=normal|debug` (default: `normal`)
@@ -262,16 +261,16 @@ Green bbox per face detection: actual frames `thickness=4`, interpolated `thickn
| # | Method | Path | Description |
|---|--------|------|-------------|
| 33 | GET | `/api/v1/identities` | List all identities |
| 34 | GET | `/api/v1/file/:file_uuid/identities` | Identities in a file |
| 35 | POST | `/api/v1/identity` | Register new identity |
| 36 | GET | `/api/v1/identity/:identity_uuid` | Identity detail |
| 37 | DELETE | `/api/v1/identity/:identity_uuid` | Delete identity |
| 38 | GET | `/api/v1/identity/:identity_uuid/files` | Files for identity |
| 39 | GET | `/api/v1/identity/:identity_uuid/chunks` | Chunks for identity |
| 40 | GET | `/api/v1/faces/candidates` | Unbound face gallery |
| 41 | GET | `/api/v1/identities/search?q=` | Search identities by name → chunks |
| 42 | GET | `/api/v1/search/identity_text?q=&file_uuid=` | Full-text search → identity-bound chunks |
| 35 | GET | `/api/v1/identities` | List all identities |
| 36 | GET | `/api/v1/file/:file_uuid/identities` | Identities in a file |
| 37 | POST | `/api/v1/identity` | Register new identity |
| 38 | GET | `/api/v1/identity/:identity_uuid` | Identity detail |
| 39 | DELETE | `/api/v1/identity/:identity_uuid` | Delete identity |
| 40 | GET | `/api/v1/identity/:identity_uuid/files` | Files for identity |
| 41 | GET | `/api/v1/identity/:identity_uuid/chunks` | Chunks for identity |
| 42 | GET | `/api/v1/faces/candidates` | Unbound face gallery |
| 43 | GET | `/api/v1/identities/search?q=` | Search identities by name → chunks |
| 44 | GET | `/api/v1/search/identity_text?q=&file_uuid=` | Full-text search → identity-bound chunks |
```bash
curl "http://localhost:3002/api/v1/identities?page=1&page_size=3" -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69"
@@ -309,9 +308,9 @@ curl "http://localhost:3002/api/v1/faces/candidates?page=1&page_size=2" -H "X-A
| # | Method | Path | Description |
|---|--------|------|-------------|
| 43 | POST | `/api/v1/identity/:identity_uuid/bind` | Bind face → identity |
| 44 | POST | `/api/v1/identity/:identity_uuid/unbind` | Unbind face from identity |
| 45 | POST | `/api/v1/identity/:identity_uuid/mergeinto` | Merge into another identity |
| 45 | POST | `/api/v1/identity/:identity_uuid/bind` | Bind face → identity |
| 46 | POST | `/api/v1/identity/:identity_uuid/unbind` | Unbind face from identity |
| 47 | POST | `/api/v1/identity/:identity_uuid/mergeinto` | Merge into another identity |
```bash
curl -X POST "http://localhost:3002/api/v1/identity/a9a90105-6d6b-46ff-92da-0c3c1a57dff4/bind" -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" -H "Content-Type: application/json" -d '{"file_uuid":"3abeee81d94597629ed8cb943f182e94","face_id":"face_42"}'
@@ -326,9 +325,9 @@ curl -X POST "http://localhost:3002/api/v1/identity/a9a90105-6d6b-46ff-92da-0c3c
| # | Method | Path | Description |
|---|--------|------|-------------|
| 46 | POST | `/api/v1/resource/register` | Register processing resource |
| 47 | POST | `/api/v1/resource/heartbeat` | Resource heartbeat |
| 48 | GET | `/api/v1/resources` | List all resources |
| 48 | POST | `/api/v1/resource/register` | Register processing resource |
| 49 | POST | `/api/v1/resource/heartbeat` | Resource heartbeat |
| 50 | GET | `/api/v1/resources` | List all resources |
```bash
curl "http://localhost:3002/api/v1/resources" -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69"
@@ -343,10 +342,10 @@ curl "http://localhost:3002/api/v1/resources" -H "X-API-Key: muser_686008560363
| # | Method | Path | Description |
|---|--------|------|-------------|
| 49 | POST | `/api/v1/agents/translate` | AI text translation |
| 50 | POST | `/api/v1/agents/5w1h/analyze` | Single chunk analysis |
| 51 | POST | `/api/v1/agents/5w1h/batch` | Batch analysis |
| 52 | GET | `/api/v1/agents/5w1h/status` | Job status |
| 51 | POST | `/api/v1/agents/translate` | AI text translation |
| 52 | POST | `/api/v1/agents/5w1h/analyze` | Single chunk analysis |
| 53 | POST | `/api/v1/agents/5w1h/batch` | Batch analysis |
| 54 | GET | `/api/v1/agents/5w1h/status` | Job status |
```bash
curl -X POST "http://localhost:3002/api/v1/agents/translate" -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" -H "Content-Type: application/json" -d '{"text":"Hello world","target_language":"zh-TW"}'
@@ -361,11 +360,10 @@ curl -X POST "http://localhost:3002/api/v1/agents/translate" -H "X-API-Key: mus
| # | Method | Path | Description |
|---|--------|------|-------------|
| 53 | POST | `/api/v1/agents/identity/analyze` | Identify faces in file |
| 54 | GET | `/api/v1/agents/identity/status` | Analysis status |
| 55 | POST | `/api/v1/agents/identity/suggest` | Name suggestions |
| 56 | POST | `/api/v1/agents/suggest/merge` | Suggest merge |
| 57 | POST | `/api/v1/agents/suggest/clustering` | Suggest re-clustering |
| 55 | POST | `/api/v1/agents/identity/match-from-photo` | Match face from photo |
| 56 | POST | `/api/v1/agents/identity/match-from-trace` | Match face from trace |
| 57 | POST | `/api/v1/agents/suggest/merge` | Suggest merge |
| 58 | POST | `/api/v1/agents/suggest/clustering` | Suggest re-clustering |
---
@@ -373,10 +371,11 @@ curl -X POST "http://localhost:3002/api/v1/agents/translate" -H "X-API-Key: mus
| Version | Date | Changes |
|---------|------|---------|
| V4.2 | 2026-05-25 | Removed phantom routes (stats/ingest, stats/inference, agents/identity/status); fixed HTTP methods (chunk, progress, jobs → POST); renamed endpoints (face_trace/sortby → traces, analyze → match-from-photo, suggest → match-from-trace); added config endpoints (consistency, auto-pipeline, watcher-auto-register); updated git hash to de88fd4e |
| V4.1 | 2026-05-14 | Added `build_timestamp` + `resources` + `pipeline` to health APIs; identity search endpoints; trace debug rework (green bbox, text overlay, all traces listed) |
## Related
- `API_DICTIONARY_V1.0.0.md` — Quick reference (58 endpoints)
- `API_DICTIONARY_V1.0.0.md` — Quick reference (55 endpoints)
- `API_DOCUMENTATION_v1.0.0.md` — Detailed spec with examples
- `TRACE/TRACE_API_REFERENCE_V1.0.0.md` — Trace-specific reference

View File

@@ -158,6 +158,8 @@ related_documents:
| 51 | GET | `/api/v1/stats/sftpgo` | SFTPGo 使用者狀態 | ✅ |
| 52 | GET | `/api/v1/stats/inference` | 推理叢集健康狀態 | ✅ |
| 53 | POST | `/api/v1/config/cache` | 切換快取開關 | ✅ |
| 54 | POST | `/api/v1/config/auto-pipeline` | 註冊後自動處理 | ✅ |
| 55 | POST | `/api/v1/config/watcher-auto-register` | Watcher 自動註冊 | ✅ |
---

View File

@@ -234,6 +234,11 @@ Bind a face detection to an identity. Associates the face trace with the identit
| `file_uuid` | string | Yes | File where face is detected |
| `face_id` | string | Yes | Face ID (format: `{frame}_{idx}`) |
#### Side Effects
- 清除該 face detection row 的 `stranger_id`(設為 NULL
- 不影響 `identities` 表中原有的 stranger auto-identity 記錄
#### Example
```bash
@@ -259,6 +264,11 @@ Bind all face detections of a trace to an identity. Updates all rows in `face_de
| `file_uuid` | string | Yes | File where trace exists |
| `trace_id` | integer | Yes | Trace ID (from `face_detections.trace_id`) |
#### Side Effects
- 清除該 trace 所有 face detection rows 的 `stranger_id`(設為 NULL
- 不影響 `identities` 表中原有的 stranger auto-identity 記錄
#### Example
```bash
@@ -287,6 +297,78 @@ curl -s -X POST "$API/api/v1/identity/$IDENTITY_UUID/bind/trace" \
---
### `GET /api/v1/identity/:identity_uuid/traces`
**Auth**: Required
**Scope**: identity-level
Get paginated face traces (continuous tracking segments) associated with this identity across all files.
#### Query Parameters
| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `page` | integer | No | `1` | Page number |
| `page_size` | integer | No | `20` | Items per page |
#### Example
```bash
curl -s "$API/api/v1/identity/$IDENTITY_UUID/traces?page=1&page_size=3" \
-H "X-API-Key: $KEY" | jq '{total, total_faces, traces}'
```
#### Response (200)
```json
{
"success": true,
"identity_uuid": "a9a901056d6b46ff92da0c3c1a57dff4",
"name": "Cary Grant",
"total": 18,
"page": 1,
"page_size": 3,
"total_faces": 542,
"traces": [
{
"file_uuid": "aeed71342a899fe4b4c57b7d41bcb692",
"trace_id": 906,
"frame_count": 52,
"first_frame": 37974,
"last_frame": 38127,
"first_sec": 1519.0,
"last_sec": 1525.1,
"avg_confidence": 0.8254
}
]
}
```
| Field | Type | Description |
|-------|------|-------------|
| `success` | bool | Always `true` |
| `identity_uuid` | string | Identity UUID |
| `name` | string | Identity display name |
| `total` | integer | Total number of traces (across all pages) |
| `total_faces` | integer | Sum of all face detections in returned traces |
| `traces[].file_uuid` | string | File where trace exists |
| `traces[].trace_id` | integer | Trace tracking ID |
| `traces[].frame_count` | integer | Number of frames in this trace |
| `traces[].first_frame` | integer | Start frame number |
| `traces[].last_frame` | integer | End frame number |
| `traces[].first_sec` | float | Start time in seconds |
| `traces[].last_sec` | float | End time in seconds |
| `traces[].avg_confidence` | float | Average detection confidence (0.01.0) |
#### Error Responses
| HTTP | When |
|------|------|
| `404` | Identity not found |
| `500` | Database error |
---
### `POST /api/v1/identity/:identity_uuid/unbind`
**Auth**: Required
@@ -294,6 +376,61 @@ curl -s -X POST "$API/api/v1/identity/$IDENTITY_UUID/bind/trace" \
Unbind a face detection from an identity. Removes the identity association from the face record.
#### Side Effects
- 只清除 `identity_id`(設為 NULL**不會恢復 `stranger_id`**
- 被 unbind 的 face 不會自動成為 stranger
- 要重新標記為 stranger 需重新跑 Agent API`identity/analyze`
---
### `POST /api/v1/identity/:identity_uuid/mergeinto`
**Auth**: Required
**Scope**: identity-level
Transfer all face bindings from this identity to another identity, then optionally delete or mark the source as merged.
#### Request Parameters
| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `into_uuid` | string | Yes | — | Target identity UUID to merge into |
| `keep_history` | bool | No | `true` | Keep source identity record with `status='merged'` (`true`) or delete it (`false`) |
#### Side Effects
- 轉移所有 `face_detections.identity_id` 到目標 identity
- 同時清除所有被轉移 rows 的 `stranger_id`
- `keep_history: true`預設source identity 設為 `status='merged'`,保留記錄
- `keep_history: false`**刪除** source identity 及其 identity JSON 檔案
#### Example
```bash
curl -s -X POST "$API/api/v1/identity/$SOURCE_UUID/mergeinto" \
-H "X-API-Key: $KEY" \
-H "Content-Type: application/json" \
-d '{"into_uuid": "'"$TARGET_UUID"'", "keep_history": false}'
```
#### Response (200)
```json
{
"success": true,
"message": "Merged 'stranger_13894' into 'Louis Viret' (52 faces transferred, source deleted)",
"data": { "faces_transferred": 52 }
}
```
#### Error Responses
| HTTP | When |
|------|------|
| `404` | Source or target identity not found |
| `500` | Database error |
---
### `GET /api/v1/identities/search`
@@ -491,4 +628,4 @@ PATCH /api/v1/identity/:identity_uuid
This **replaces** the entire `aliases` array. To add to existing aliases, include all existing entries in the request.
---
*Updated: 2026-05-22
*Updated: 2026-05-25

View File

@@ -0,0 +1,492 @@
---
title: "Frame / Time 雙產線分流協作設計 v1.0"
version: "1.0"
date: "2026-05-23"
author: "M5"
status: "draft"
scope: "architecture, worker, storage"
---
# Frame / Time 雙產線分流協作設計 v1.0
| Scope | Status | Applies to |
|---------|--------|------------|
| architecture / worker / storage | draft | momentry_core worker pipeline |
---
## 1. 緣起與問題
### 1.1 現狀問題
worker 將所有 processor 混在一起平行執行,導致:
| 問題 | 說明 |
|------|------|
| OOM | `max_concurrent=6` 時 6 個 Python 行程同時載入模型 → 記憶體不足被 kill |
| 資源競爭 | 多個 processor 各自開 ffmpeg decode 同一部影片 → 6 倍 decode |
| 重試粒度粗 | 一個 processor 失敗 → 整部片全部重來 |
| 進度不精確 | 0% → 100%,中間無細粒度進度 |
### 1.2 手動 vs Worker 差異
| 面向 | 手動執行 | Worker 自動化 |
|------|---------|--------------|
| 執行方式 | 循序,一次一個 processor | 平行,最多 max_concurrent 個 |
| 產出檢查 | 人工確認 JSON 內容正確 | `output_path.exists()` 僅檢查檔案存在 |
| 資源 | 單一模型在記憶體 | 多個模型競爭記憶體 |
### 1.3 核心結論
兩個根本問題必須解決:
1. **產線分流** — Frame-base 與 Time-base processor 不應混合排程
2. **Frame-level resource management** — 透過 MarkbaseFMS 統一 frame 存取
---
## 2. 雙產線架構
### 2.1 Pipeline Overview
```mermaid
graph TD
Input[Input Video] --> Probe
Probe -->|frame info| FMS[MarkbaseFMS]
Probe -->|audio track| TimePipe[Time Pipeline]
FMS -->|frame batches| FramePipe[Frame Pipeline]
FMS -->|cache / align / convert| FMS
subgraph FramePipe [Frame Pipeline]
CUT[CUT - scene detection]
YOLO[YOLO - object detection]
Face[Face - face detection]
OCR[OCR - text detection]
Pose[Pose - pose estimation]
end
subgraph TimePipe [Time Pipeline]
ASR[ASR - speech recognition]
ASRX[ASRX - speaker diarization]
end
CUT --> YOLO
CUT --> Face
CUT --> OCR
CUT --> Pose
ASR --> ASRX
YOLO & Face & OCR & Pose --> Merge[Merge Processor Results]
ASR & ASRX --> Merge
Face --> Lip[Lip Sync]
ASR --> Lip
ASRX --> Lip
Merge -->|all essential done| PostProcess
Lip --> PostProcess
subgraph PostProcess [Post-processing]
R1[Rule 1 Chunking]
R3[Rule 3 Chunking]
TK[TKG Build]
W1[5W1H Agent]
ID[Identity Agent]
end
```
### 2.2 各產線定義
#### Frame Pipelineframe-based
| Processor | 輸入 | 輸出 | 瓶頸資源 | 產線 |
|-----------|------|------|---------|------|
| CUT | frame (降解析) | scene.json | CPU | Frame |
| YOLO | frame batch | yolo.json | GPU | Frame |
| Face | frame batch | face.json | ANE/GPU | Frame |
| OCR | frame batch | ocr.json | CPU | Frame |
| Pose | frame batch | pose.json | GPU | Frame |
#### Time Pipelinetime-based
| Processor | 輸入 | 輸出 | 瓶頸資源 | 產線 |
|-----------|------|------|---------|------|
| ASR | audio stream | asr.json | GPU/CPU | Time |
| ASRX | audio stream + ASR result | asrx.json | CPU | Time |
#### 合流點
| 項目 | 需要 | 產出 |
|------|------|------|
| Lip Sync | Face + ASR + ASRX | lip.json (who speaks when) |
| Rule 1 Chunking | ASR + ASRX | sentence chunks |
| Rule 3 Chunking | CUT + ASR | scene chunks |
| TKG Build | 所有 processor | tkg_nodes / tkg_edges |
| 5W1H Agent | CUT + ASR | story summary |
| Identity Agent | Face + ASRX | identity bindings |
---
## 3. MarkbaseFMS 底層設計
### 3.1 三層架構
```
Application
YOLO / Face / OCR / Pose / CUT
讀取 frame buffer → 直接做 inference
│ frame-aligned access
┌───────────────────────────────────┐
│ FMS Filesystem Layer │
│ Layout: frame data 連續存放 │
│ 不跨 frame split │
│ 格式: opaque raw buffer │
│ metadata: 獨立區域 │
│ read-ahead: 預取下一個 Block │
│ alignment: 每 frame page-aligned │
└──────────────────┬────────────────┘
│ page-aligned (4096)
┌──────────────────▼────────────────┐
│ FMS Cache Layer │
│ unit: FrameBlock (64 frames) │
│ alignment: page boundary/frame │
│ eviction: LRU on Block │
│ pin: 使用中的 frame 不 evict │
│ prefetch: 預拉下一個 Block │
│ Direct I/O bypass OS cache │
└──────────────────┬────────────────┘
│ block-aligned (4K / 64K)
┌──────────────────▼────────────────┐
│ Block Device / Storage │
│ sector alignment 4K │
│ FrameBlock = N 個連續 sectors │
│ 無跨 sector split │
│ atomic write per block │
│ O_DIRECT 直接 IO │
└───────────────────────────────────┘
```
### 3.2 對齊原則
#### 各層對齊要求
| 層級 | 對齊單位 | 原因 |
|------|---------|------|
| Block Device | 4K sector | 現代 SSD 原生 sector避免 RMW |
| Cache | 4096 page | `mmap` + `madvise` 大頁面,減少 TLB miss |
| Filesystem | frame block (64 frames) | 連續 layout預測性 read-ahead |
| Frame Buffer | 16 bytes stride | NEON SIMDMPS/ANE texture 要求 |
| Block Index | power of 2 | index 用 bit shift + mask無需除法 |
#### Frame Buffer Layout
```
每個 frame 的 raw buffer
┌──────────────┐
│ Y Plane │ ← 16-byte aligned stride, page-aligned offset
│ (width×h) │
├──────────────┤
│ UV Plane │ ← 16-byte aligned stride
│ (w/2×h/2×2) │ (NV12 interleaved)
└──────────────┘
frame buffer offset: page-aligned (4096)
row stride: align(width * pixel_size, 16)
frame size: align(total_bytes, page_size)
```
### 3.3 Block 排列方式
```
每個 Block 包含連續 64 framesconfigurable
Block index ≤— bit shift (frame_num / 64)
Frame offset = base + (frame_num & 63) * frame_size ← bit mask
Block Dispatching Strategy:
Worker 要求 "batch 0, format=RGB, width=640"
FMS:
1. Check Block Cache (RAM):
Block 0 frames 0-63 是否已 decode?
✅ hit → 直接回傳
❌ miss → decode 64 frames → 存入 cache → 回傳
2. Format Conversion (on-the-fly):
原始 NV12 → per request:
YOLO → RGB (float32 normalized)
Face → RGB (uint8)
OCR → Gray (uint8)
Pose → RGB (uint8)
CUT → RGB (降解析, uint8)
```
### 3.4 儲存 Layout
```
Disk Layout (per file_uuid):
┌──────────────────────────────────────────────┐
│ Metadata Region │
│ - file_uuid (32 bytes) │
│ - total_frames (u32) │
│ - width, height (u32 × 2) │
│ - fps (f64) │
│ - pixel_format (u8 : 0=NV12) │
│ - block_capacity (u32, default 64) │
│ - block_count (u32) │
│ - frame_size (u32, bytes per raw frame)│
│ - block_offsets [u64 × block_count] │
│ Padding to 4096 │
├──────────────────────────────────────────────┤
│ Data Region │
│ Block 0: frames [0, 63] │
│ frame_0: [frame_size bytes] ← page align│
│ frame_1: [frame_size bytes] │
│ ... │
│ frame_63: [frame_size bytes] │
│ Block 1: frames [64, 127] │
│ ... │
│ Block N: frames [N*64, ...] │
└──────────────────────────────────────────────┘
```
### 3.5 記憶體管理
```
┌────────────────────────────────────────────┐
│ FMS Cache │
├──────────┬──────────┬──────────┬───────────┤
│ Block 0 │ Block 1 │ Block 2 │ ... │ ← mmap'd
│ (64 fr) │ (64 fr) │ (64 fr) │ │ or anonymous
├──────────┴──────────┴──────────┴───────────┤
│ LRU eviction policy │
│ max_memory = configurable │
│ (default: 256 frames ~1.5GB @1080p NV12) │
│ pin_count: 正在被 processor 存取的 frame │
│ pinned frame 不參與 LRU eviction │
└─────────────────────────────────────────────┘
```
---
## 4. Worker 調度器修改
### 4.1 Processor 產線標記
```rust
enum PipelineType {
Frame, // frame-based
Time, // time-based
Cross, // needs both frame + time
}
impl ProcessorType {
fn pipeline(&self) -> PipelineType {
match self {
Self::Cut
| Self::Yolo
| Self::Face
| Self::Ocr
| Self::Pose => PipelineType::Frame,
Self::Asr | Self::Asrx => PipelineType::Time,
Self::Story
| Self::Tkg
| Self::Identity
| Self::FiveW1h
| Self::Caption => PipelineType::Cross,
}
}
}
```
### 4.2 資源配額
| 產線 | Max Concurrent | 策略 |
|------|---------------|------|
| Frame | 2 | 最多同時 2 個 frame processor |
| Time | 1 | 一次只跑 1 個 audio processor |
| Cross | 1 | Frame + Time 都完成後才允許 |
Frame pipeline 內部建議順序:
```
CUT (先確定場景邊界)
→ YOLO / Face 可同時 (GPU-bound)
→ OCR / Pose 可同時 (CPU/GPU mixed)
```
### 4.3 產出驗證加強
```rust
// 目前 (job_worker.rs:346):
if output_path.exists() {
mark_completed();
}
// 改為:
if output_path.exists() {
match validate_processor_output(&output_path, processor_type) {
Ok(true) => mark_completed(),
Ok(false) => retry_or_fail(),
Err(e) => mark_failed(e),
}
}
fn validate_processor_output(path: &Path, pt: ProcessorType) -> Result<bool> {
let content = std::fs::read_to_string(path)?;
let json: serde_json::Value = serde_json::from_str(&content)?;
// 至少要有基本欄位
match pt {
ProcessorType::Asr => json.get("segments").is_some(),
ProcessorType::Yolo => json.get("frames").is_some(),
ProcessorType::Face => json.get("frames").is_some(),
// ...
};
Ok(true) // or false
}
```
### 4.4 啟動順序
```
1. Probe → 決定 frame 數、audio 格式
2. CUT (Frame Pipeline 第一階段—決定場景邊界)
3. Frame Pipeline 平行: YOLO / Face / OCR / Pose
Time Pipeline 平行: ASR
4. ASRX (依賴 ASR 結果)
5. Lip Sync (等待 Face + ASR + ASRX)
6. 所有 processor 完成 → 合流:
- Rule 1 / Rule 3 Chunking
- Face Trace / TKG
- 5W1H / Identity Agent
```
---
## 5. Processor 串接 FMS
### 5.1 FMS API
```rust
// Frame access API (async)
impl FmsClient {
/// 取得單一 frame buffer
async fn get_frame(
&self,
file_uuid: &str,
frame_num: u32,
format: PixelFormat,
width: u32,
height: u32,
) -> Result<RawFrame>;
/// 取得一個 batch frames (block-aligned)
async fn get_block(
&self,
file_uuid: &str,
block_idx: u32,
format: PixelFormat,
width: u32,
height: u32,
) -> Result<FrameBlock>;
/// 串流 frames (lazy batch iteration)
fn stream_frames(
&self,
file_uuid: &str,
range: Range<u32>,
format: PixelFormat,
width: u32,
height: u32,
) -> FrameStream;
}
```
### 5.2 YOLO 為例processor 修改
```
目前:
processor::process_yolo(video_path, output_path, uuid)
→ 自己開 ffmpeg decode
→ 逐 frame 處理
→ 寫入 yolo.json
改為 FMS-based:
processor::process_yolo_with_fms(fms, uuid, output_path)
→ let results = Vec::new()
→ for batch in fms.stream_frames(uuid, 0..total, RGB, 640, 640):
→ let detections = yolo_model.infer(batch)
→ results.push(detections)
→ write partial → yolo.partial.{batch_idx}.json
→ merge_partials() → yolo.json
```
### 5.3 Partial Result Merge
```
Frame Pipeline 產出多個 partial JSON
yolo.0000.json (frames 0-63)
yolo.0001.json (frames 64-127)
...
yolo.0063.json (frames 4032-4095)
Worker 合併為單一 yolo.json
```rust
async fn merge_partials(
uuid: &str,
processor: &str,
partial_dir: &Path,
output_path: &Path,
) -> Result<()> {
let partials = read_sorted_partials(uuid, processor, partial_dir);
let merged = merge_detections(partials);
write_json(output_path, merged)
}
```
```
---
## 6. 實作優先序
| 優先 | 項目 | 說明 |
|------|------|------|
| P0 | Worker 產線分流 | ProcessorType::pipeline() + frame_slots / time_slots 分開計算 |
| P0 | 產出驗證加強 | `output_path.exists()` + JSON validity + schema check |
| P1 | FMS FrameBlock 資料結構 | 含 16-byte stride / 4096 page alignment |
| P1 | mmap-based frame cache | page-aligned frame buffers, LRU eviction |
| P2 | FMS API 實作 | get_frame / get_block / stream_frames |
| P2 | YOLO processor 串接 FMS | 改為 stream_frames 方式 |
| P2 | 其他 processor 串接 FMS | Face / OCR / Pose / CUT |
| P3 | FMS Direct I/O + sector alignment | O_DIRECT bypass OS page cache |
| P3 | Prefetch / readahead | 預測下一個 block 並提前載入 |
---
## 7. 注意事項
| # | 項目 |
|---|------|
| 1 | raw buffer 格式依 processor 需求轉換FMS 負責 NV12 → RGB / Gray |
| 2 | Time pipeline 的 ASR/ASRX **不經過 FMS**,直接處理 audio stream |
| 3 | macOS 的 `mmap` 支援 page-aligned但 `O_DIRECT` 需確認 compat |
| 4 | FrameBlock size (64) 可配置,但需維持 power-of-2 |
| 5 | FMS 只管理 frame lifecycle不處理 processor-specific 邏輯 |
| 6 | 多 processor 共享 frame 時FMS 保證只 decode 一次 |
---
## 版本歷史
| Version | Date | Author | Changes |
|---------|------|--------|---------|
| 1.0 | 2026-05-23 | M5 | 初版 |

View File

@@ -0,0 +1,66 @@
# Library Page: Flash & Filter Fix
- **Date**: 2026-05-29
- **Author**: OpenCode
- **Status**: Completed
## Summary
Fixed three interconnected issues on the library page (`/library/`) where video cards would flash 3 times on load, and the enhanced filter panel (size slider, duration, registered/unregistered) stopped working after flash fixes.
## Root Causes & Fixes
### Issue 1: 3x Flash on Load
**Root Cause**: Multiple redundant render cycles triggered by:
1. **`delayedPeopleFilesLoader`** (snippet 55) schedules **6x** `setTimeout(startPeopleFilesLoader, ...)` — 3 from `DOMContentLoaded`, 3 from `window 'load'`. Each creates a `setInterval` that retries `initPeopleFilesMediaLoader` every 200ms.
2. **`loadMediaItems`** (snippet 55) resets `root.dataset.mediaLoaded = ''` after successful load, allowing the next pending `setTimeout(startPeopleFilesLoader, 500/1200)` to trigger a second/third `loadMediaItems` call → each calls `renderItems()` → re-renders all cards.
3. **`bootFilterOnly()`** (snippet 58) has no guard, runs 5+ times from multiple `setTimeout(start, 300/1000/2000)` and event listeners.
4. **`loadMediaMeta()`** (snippet 58) had no guard, ran on every `bootFilterOnly()` call → `debouncedApply()``applyEnhancedFilters()` reordered cards via DOM appendChild after async completion.
**Fix**:
- Snippet 55: Removed `root.dataset.mediaLoaded = ''` reset in `loadMediaItems` success path. `mediaLoaded` stays `'1'` after first successful load, preventing re-triggers.
- Snippet 58: Removed `debouncedApply()` from `loadMediaMeta()`.
- Snippet 58: `setGridView()` already had a class-duplicate guard.
- Snippet 58: `renderFinderRows()` already had a skip guard.
### Issue 2: Filter Not Working
**Root Cause**: `debouncedApply()` (which calls `applyEnhancedFilters()`) was only triggered automatically from `loadMediaMeta()`. After removing it (fix #1), the filter state was never applied to cards.
**Fix** (snippet 58):
- Added `applyEnhancedFilters()` to the `ltPeopleFilesFiltered` event handler (after `renderFinderRows()`).
- Removed the `setTimeout(0)` re-dispatch loop inside `applyEnhancedFilters` that would cause infinite event chaining. Replaced with simple `isApplyingFilter = false`.
### Issue 3: Infinite Event Loop
**Root Cause**: `applyEnhancedFilters()` used `setTimeout(0)` to set `isApplyingFilter = false` and re-dispatch `ltPeopleFilesFiltered`, which would call back into the handler → `applyEnhancedFilters()` → re-dispatch → loop.
**Fix**: Directly set `isApplyingFilter = false` at the end of `applyEnhancedFilters()`.
## Files Modified
| Snippet | ID | Changes |
|---------|-----|---------|
| LT-檔案管理-註冊 | 55 | Removed `mediaLoaded = ''` reset in `loadMediaItems` success |
| LT-檔案管理-篩選功能 | 58 | Added `applyEnhancedFilters()` to `ltPeopleFilesFiltered` handler; removed `debouncedApply()` from `loadMediaMeta`; removed re-dispatch loop in `applyEnhancedFilters` |
## Verification
- ✅ No flashes on page load (single paint)
- ✅ Filter panel works (registered/unregistered, search, sort, sliders)
- ✅ Video streaming works (snippet 61, curl-based proxy)
-`cargo clippy --lib` — N/A (WordPress PHP)
-`cargo test --lib` — N/A
## Context Saved At
- User confirmed "沒有閃了" (no more flashes) and filter working
- AGENTS.md development boundary: WordPress snippets #55, #58, #61 (Code Snippets plugin)
- All edits done via direct MySQL UPDATE on `wp_snippets` table
- Working directory: `/Users/accusys/momentry_core`
- Latest context: user asked to save handoff before changing topic

View File

@@ -0,0 +1,156 @@
---
title: WordPress API URL Update - 2026-05-29
version: "1.0"
date: 2026-05-29
author: OpenCode
status: in_progress
---
# WordPress API URL Update Session
## Scope
Update WordPress Code Snippets to point momentry_core API from `m5api.momentry.ddns.net` / `api.momentry.ddns.net` to `192.168.110.201:3002` (M5Max48 LAN IP).
## Summary
| Item | Status |
|------|--------|
| URL update | ✅ Done |
| `/scan` route | ✅ Working (122 files) |
| `/search-proxy?mode=people` | ✅ Working (3788 results) |
| `/search-proxy?mode=semantic` | ❌ Returns 0 results (direct API works with 20 results) |
| `/search-proxy?mode=keyword` | ❌ Returns 0 results (direct API works with 21 results) |
| Snippet #66 PHP syntax fix | ✅ Fixed (removed `.` before array keys) |
| Added `limit/page/page_size` | ✅ Added to search bodies |
## Changes Made
### 1. URL Updates
Changed in multiple snippets:
| Old URL | New URL |
|---------|---------|
| `https://m5api.momentry.ddns.net` | `http://192.168.110.201:3002` |
| `https://api.momentry.ddns.net` | `http://192.168.110.201:3002` |
| `localhost:3002` | `192.168.110.201:3002` |
Affected snippets: #37, #43, #44, #48, #55, #59, #60, #61, #62, #63, #64, #66, #67
### 2. Snippet #66 Fixes
**Before (syntax error)**:
```php
$body = [
. 'query' => $query, // ❌ Invalid PHP syntax
. 'limit' => 20,
];
```
**After (fixed)**:
```php
// Semantic search body
$body = [
'query' => $query,
'limit' => 20,
'page' => 1,
'page_size' => 20,
];
// Universal search body
$body = [
'query' => $query,
'limit' => 20,
'page' => 1,
'page_size' => 20,
];
```
Note: `file_uuid` was NOT added per user request.
## Backup Location
```
/Users/accusys/momentry_core/backups/wp_snippets_20260529_181847/
```
Contains:
- `wp_snippets_full.sql` - Full backup before any changes
- `snippets_with_old_url.sql` - Snippets containing old URLs
- `snippets_43_44_48_54_before_api_fix.sql`
- `snippet_66_before_syntax_fix.sql`
## Restore Command
```bash
mysql -u wp_user -p'wp_password_123' wordpress < /Users/accusys/momentry_core/backups/wp_snippets_20260529_181847/wp_snippets_full.sql
```
## Pending Issue: Semantic/Keyword Search Returns Empty
### Symptoms
- Direct API call to momentry_core: Returns results
- WP proxy call: Returns `{"results": [], "total": 0}`
### Direct API Test (Works)
```bash
curl -s http://192.168.110.201:3002/api/v1/search/smart \
-H 'Content-Type: application/json' \
-H 'X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69' \
-d '{"query":"love","limit":20,"page":1,"page_size":20}'
# Returns 20 results
```
### WP Proxy Test (Empty)
```bash
curl -sk 'https://m5wp.momentry.ddns.net/wp-json/momentry/v1/search-proxy?mode=semantic&query=love'
# Returns {"query":"love","results":[],"page":1,"page_size":20,"strategy":"semantic_vector_search"}
```
### Hypothesis
1. WordPress `wp_remote_request` may encode JSON differently
2. Header mismatch between WordPress and curl
3. PHP `$body` array construction issue
### Debug Steps Needed
1. Add debug output to snippet to return the exact `$body` JSON being sent
2. Check WordPress HTTP request logs
3. Compare raw request payload from WordPress vs curl
### Temporary Workaround
Use people search (works) or call momentry_core directly from frontend bypassing WP proxy.
## Environment Context
| Server | IP | Port | Role |
|--------|-----|------|------|
| M5Max48 | 192.168.110.201 | 3002 | momentry_core production |
| M5Max48 | 192.168.110.201 | 3003 | momentry_core playground (dev) |
| M4mini | 192.168.110.210 | 443 | Caddy reverse proxy for WordPress |
| WordPress | - | - | MariaDB, PHP-FPM 8.5, Code Snippets plugin |
## API Key
```
muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69
```
## Database State
- PostgreSQL: `momentry` database
- `public.chunk`: 294,531 rows (has embeddings)
- `public.videos`: 4 registered files including Charade_YouTube_24fps.mp4
- Qdrant: `momentry_rule1` collection with embeddings
## Version History
| Version | Date | Author | Change |
|---------|------|--------|--------|
| 1.0 | 2026-05-29 | OpenCode | Initial session record |

View File

@@ -1,7 +1,7 @@
# Release Notes — v1.0.0 (Production 3002)
# Release Notes — v1.0.1 (Production 3002)
**Date**: 2026-05-13
**Build**: `301da08`
**Date**: 2026-05-25
**Build**: `de88fd4e`
**Deployed by**: M4
---
@@ -101,6 +101,36 @@
---
## Changes Since v1.0.0 (301da08 → de88fd4e)
### Added
- POST `/api/v1/agents/search` — Gemma4 function calling agent
- POST `/api/v1/identity/:uuid/bind/trace` — trace-level identity binding
- GET `/api/v1/file/:uuid/identities/:a/co-occur-with/:b` — co-occurrence
- GET `/api/v1/file/:uuid/trace/:tid/thumbnail` — trace thumbnail
- GET `/api/v1/file/:uuid/trace/:tid/representative-face` — representative face
- PATCH `/api/v1/identity/:identity_uuid` — identity update + alias system
- TKG extension: pose data + mutual gaze detection
- `/health/consistency` — data consistency check
- Config endpoints: cache toggle, auto-pipeline, watcher-auto-register
- Representative frame auto-detection
### Fixed
- System consistency: store_vector, search, worker trigger reliability
- trigger_processing: remove fake QUEUED state, create monitor_job if missing
- stranger_id set to NULL on bind/merge operations
- frame_number type: i32→i64 to match BIGINT schema
- Compilation errors: restored PipelineType enum, pipeline() method, constants
### Changed
- Unified LLM config: CHAT_URL/VISION_URL/SUMMARY_URL with env var overrides
- Port config centralized (8082 conflict resolved)
- Resources API returns data (config+metadata)
- server.rs split into modular route files
- API Reference: 55 endpoints, removed phantom routes, fixed methods, renamed endpoints
---
## Known Notes
| Item | Note |

View File

@@ -6,13 +6,135 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [Unreleased]
### Fixed
- Compilation errors: restore PipelineType enum, pipeline() method, OLLAMA_URL/EMBED_URL/LLM_HEALTH_URL constants
- frame_number type: changed i32→i64 to match BIGINT schema
## [1.0.1] - 2026-05-25
### Fixed
- System consistency: store_vector, search, worker trigger reliability
- trigger_processing: remove fake QUEUED state, create monitor_job if missing
- stranger_id set to NULL on bind/merge operations
- resource path cleanup
### Added
- Gitea API token integration
- n8n API key integration
- API key caching with Moka
- Rate limiting for API key validation
- Constant-time hash comparison
- OpenAPI documentation with utoipa
- Frame/time pipeline split with output validation
- `/api/v1/agents/search` — Gemma4 function calling agent
- `/api/v1/identity/:uuid/bind/trace` — trace-level identity binding
- `/api/v1/file/:uuid/identities/:a/co-occur-with/:b` — co-occurrence endpoint
- `/api/v1/file/:uuid/trace/:tid/thumbnail` — trace thumbnail
- `/api/v1/file/:uuid/trace/:tid/representative-face` — representative face
- identity PATCH update, alias system, name UNIQUE removal
- TKG extension: pose data + mutual gaze detection
### Changed
- Unified LLM config: CHAT_URL/VISION_URL/SUMMARY_URL with env var overrides
- Refactored server.rs into modular route files
- Port config centralized (8082 conflict resolved)
- Resources API returns data (config+metadata); register source code resource
## [1.0.0] - 2026-05-14
### Added
- Release version v1.0.0 tag
- Production binary at port 3002
- Playground binary at port 3003
- Dedicated Gitea sync pipeline
### Changed
- Full V4.0 API reference published (55 endpoints)
- Health APIs include build_timestamp + resources + pipeline
- Qdrant refactored to use i64 for frame_number
- Rust edition 2021, max_width=100, tab_spaces=4
## [0.6.0] - 2026-05-01
### Added
- `/api/v1/agents/translate` — AI text translation
- `/api/v1/agents/5w1h/batch` — batch analysis
- `/api/v1/agents/identity/match-from-photo` — face matching from photo
- `/api/v1/agents/identity/match-from-trace` — face matching from trace
- `/api/v1/agents/suggest/merge` — merge suggestions
- `/api/v1/agents/suggest/clustering` — re-clustering suggestions
- `/api/v1/search/visual` endpoints (class, density, combination, stats)
- `/api/v1/search/frames` — frame-level search
- `/api/v1/faces/candidates` — unbound face gallery
- `/api/v1/identities/search` — identity name search
- `/api/v1/search/identity_text` — full-text identity-bound chunk search
- `/health/consistency` — data consistency check
- Config endpoints: auto-pipeline toggle, watcher-auto-register toggle
- Cache toggle via `/api/v1/config/cache`
### Fixed
- Various type mismatches in DB layer (i64/i32 alignment)
- Trace debug mode bbox rendering
- Interpolation for sparse face detections
## [0.5.0] - 2026-04-15
### Added
- `/api/v1/file/:file_uuid/traces` — trace listing (replaces face_trace/sortby)
- `/api/v1/file/:file_uuid/trace/:trace_id/faces` — trace detections with interpolation
- `/api/v1/file/:file_uuid/video/bbox` — bbox overlay video
- `/api/v1/file/:file_uuid/trace/:trace_id/video` — trace clip (normal/debug mode)
- File probe endpoint (`/api/v1/file/:file_uuid/probe`)
- Jobs monitoring (`/api/v1/jobs`)
- Processing progress (`/api/v1/progress/:file_uuid`)
- Chunk detail (`/api/v1/file/:file_uuid/chunk/:chunk_id`)
- `/api/v1/unregister` — file unregistration by uuid or pattern
### Changed
- face_trace/sortby renamed to `/api/v1/file/:file_uuid/traces`
- Chunk/progress/jobs endpoints changed from GET to POST
- Qdrant MongoDB dependency replaced with Rust-native driver
- Redis upgraded to 1.0.x for performance
## [0.4.0] - 2026-04-01
### Added
- Identity management: register, detail, delete, files, chunks
- Identity binding: bind/unbind face → identity
- Identity merging: mergeinto endpoint
- Resource management: register, heartbeat, list
- Docker health check integration
- SFTPGo status endpoint
- Pagination support for list endpoints
### Changed
- Database schema: `person_identities` table removed
- N:N relationship via `file_identities` table
- Architecture shift: Face → Identity (two-layer direct binding)
- Terminology: `video_uuid``file_uuid` across all APIs
## [0.3.0] - 2026-03-25
### Added
- `/api/v1/search/universal` — BM25 keyword search
- `/api/v1/search/smart` — semantic search (pgvector)
- `/api/v1/files/register` — file registration
- `/api/v1/files/lookup` — name conflict check
- `/api/v1/files/scan` — directory scan
- File listing and detail endpoints
- POST `/api/v1/file/:file_uuid/process` — processing pipeline trigger
- `/api/v1/auth/login` and `/api/v1/auth/logout`
- `/health/detailed` — per-service health check
### Changed
- Video processing pipeline refactored (ASR, OCR, YOLO, Face, Pose)
- Centralized Python executor with timeout support
- Logging unified under `logs/` directory
- Startup scripts auto-build and kill old processes
## [0.2.0] - 2026-03-22
### Added
- `/health` endpoint with status/build info
- Video stream endpoint (`/api/v1/file/:file_uuid/video`)
- Thumbnail extraction endpoint
- FaceNet CoreML model integration
- Basic API key authentication
- PostgreSQL, MongoDB, Redis, Qdrant service initialization
## [0.1.0] - 2026-03-21
@@ -140,4 +262,11 @@ N8N_URL=https://n8n.momentry.ddns.net
| Version | Date | Description |
|---------|------|-------------|
| 1.0.1 | 2026-05-25 | Bug fixes, agent endpoints, identity alias system, TKG extension |
| 1.0.0 | 2026-05-14 | Release v1.0.0, V4.0 API reference, production binary |
| 0.6.0 | 2026-05-01 | Agent endpoints (translate, 5w1h, identity match), search visual endpoints |
| 0.5.0 | 2026-04-15 | Traces API, media endpoints, jobs/progress/chunk, rename face_trace→traces |
| 0.4.0 | 2026-04-01 | Identity management, resource API, video_uuid→file_uuid rename |
| 0.3.0 | 2026-03-25 | Search (BM25/semantic), file registration, health check, auth |
| 0.2.0 | 2026-03-22 | Health endpoint, video stream, thumbnail, FaceNet model, DB init |
| 0.1.0 | 2026-03-21 | Initial release with API Key Management |

View File

@@ -234,6 +234,11 @@ Bind a face detection to an identity. Associates the face trace with the identit
| `file_uuid` | string | Yes | File where face is detected |
| `face_id` | string | Yes | Face ID (format: `{frame}_{idx}`) |
#### Side Effects
- 清除該 face detection row 的 `stranger_id`(設為 NULL
- 不影響 `identities` 表中原有的 stranger auto-identity 記錄
#### Example
```bash
@@ -259,6 +264,11 @@ Bind all face detections of a trace to an identity. Updates all rows in `face_de
| `file_uuid` | string | Yes | File where trace exists |
| `trace_id` | integer | Yes | Trace ID (from `face_detections.trace_id`) |
#### Side Effects
- 清除該 trace 所有 face detection rows 的 `stranger_id`(設為 NULL
- 不影響 `identities` 表中原有的 stranger auto-identity 記錄
#### Example
```bash
@@ -287,6 +297,78 @@ curl -s -X POST "$API/api/v1/identity/$IDENTITY_UUID/bind/trace" \
---
### `GET /api/v1/identity/:identity_uuid/traces`
**Auth**: Required
**Scope**: identity-level
Get paginated face traces (continuous tracking segments) associated with this identity across all files.
#### Query Parameters
| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `page` | integer | No | `1` | Page number |
| `page_size` | integer | No | `20` | Items per page |
#### Example
```bash
curl -s "$API/api/v1/identity/$IDENTITY_UUID/traces?page=1&page_size=3" \
-H "X-API-Key: $KEY" | jq '{total, total_faces, traces}'
```
#### Response (200)
```json
{
"success": true,
"identity_uuid": "a9a901056d6b46ff92da0c3c1a57dff4",
"name": "Cary Grant",
"total": 18,
"page": 1,
"page_size": 3,
"total_faces": 542,
"traces": [
{
"file_uuid": "aeed71342a899fe4b4c57b7d41bcb692",
"trace_id": 906,
"frame_count": 52,
"first_frame": 37974,
"last_frame": 38127,
"first_sec": 1519.0,
"last_sec": 1525.1,
"avg_confidence": 0.8254
}
]
}
```
| Field | Type | Description |
|-------|------|-------------|
| `success` | bool | Always `true` |
| `identity_uuid` | string | Identity UUID |
| `name` | string | Identity display name |
| `total` | integer | Total number of traces (across all pages) |
| `total_faces` | integer | Sum of all face detections in returned traces |
| `traces[].file_uuid` | string | File where trace exists |
| `traces[].trace_id` | integer | Trace tracking ID |
| `traces[].frame_count` | integer | Number of frames in this trace |
| `traces[].first_frame` | integer | Start frame number |
| `traces[].last_frame` | integer | End frame number |
| `traces[].first_sec` | float | Start time in seconds |
| `traces[].last_sec` | float | End time in seconds |
| `traces[].avg_confidence` | float | Average detection confidence (0.01.0) |
#### Error Responses
| HTTP | When |
|------|------|
| `404` | Identity not found |
| `500` | Database error |
---
### `POST /api/v1/identity/:identity_uuid/unbind`
**Auth**: Required
@@ -294,6 +376,61 @@ curl -s -X POST "$API/api/v1/identity/$IDENTITY_UUID/bind/trace" \
Unbind a face detection from an identity. Removes the identity association from the face record.
#### Side Effects
- 只清除 `identity_id`(設為 NULL**不會恢復 `stranger_id`**
- 被 unbind 的 face 不會自動成為 stranger
- 要重新標記為 stranger 需重新跑 Agent API`identity/analyze`
---
### `POST /api/v1/identity/:identity_uuid/mergeinto`
**Auth**: Required
**Scope**: identity-level
Transfer all face bindings from this identity to another identity, then optionally delete or mark the source as merged.
#### Request Parameters
| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `into_uuid` | string | Yes | — | Target identity UUID to merge into |
| `keep_history` | bool | No | `true` | Keep source identity record with `status='merged'` (`true`) or delete it (`false`) |
#### Side Effects
- 轉移所有 `face_detections.identity_id` 到目標 identity
- 同時清除所有被轉移 rows 的 `stranger_id`
- `keep_history: true`預設source identity 設為 `status='merged'`,保留記錄
- `keep_history: false`**刪除** source identity 及其 identity JSON 檔案
#### Example
```bash
curl -s -X POST "$API/api/v1/identity/$SOURCE_UUID/mergeinto" \
-H "X-API-Key: $KEY" \
-H "Content-Type: application/json" \
-d '{"into_uuid": "'"$TARGET_UUID"'", "keep_history": false}'
```
#### Response (200)
```json
{
"success": true,
"message": "Merged 'stranger_13894' into 'Louis Viret' (52 faces transferred, source deleted)",
"data": { "faces_transferred": 52 }
}
```
#### Error Responses
| HTTP | When |
|------|------|
| `404` | Source or target identity not found |
| `500` | Database error |
---
### `GET /api/v1/identities/search`
@@ -491,4 +628,4 @@ PATCH /api/v1/identity/:identity_uuid
This **replaces** the entire `aliases` array. To add to existing aliases, include all existing entries in the request.
---
*Updated: 2026-05-22
*Updated: 2026-05-25

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.momentry.playground</string>
<key>UserName</key>
<string>accusys</string>
<key>GroupName</key>
<string>staff</string>
<key>WorkingDirectory</key>
<string>/Users/accusys/momentry_core</string>
<key>ProgramArguments</key>
<array>
<string>/Users/accusys/momentry_core/scripts/wrapper_playground.sh</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>/Users/accusys/momentry/log/playground.log</string>
<key>StandardErrorPath</key>
<string>/Users/accusys/momentry/log/playground.error.log</string>
</dict>
</plist>

View File

@@ -174,8 +174,8 @@ class SelfASRXFixed:
wav = np.mean(wav, axis=1) # 轉 mono
print(f" Audio loaded: {len(wav)/sample_rate:.2f}s, {sample_rate}Hz")
# 使用 ASR segments 取代 VAD
speech_segments = [(s["start"], s["end"]) for s in asr_segments]
# 使用 ASR segments 取代 VAD (audio处理用time)
speech_segments = [(s["start_time"], s["end_time"]) for s in asr_segments]
print(f" Speech segments from ASR: {len(speech_segments)}")
if len(speech_segments) == 0:

View File

@@ -86,7 +86,9 @@ class RedisPublisher:
try:
client: redis.Redis = self._client
client.publish(self.channel, json.dumps(asdict(msg)))
json_data = json.dumps(asdict(msg))
client.publish(self.channel, json_data)
client.hset(self.channel, msg.processor, json_data)
return True
except Exception as e:
import sys

View File

@@ -25,7 +25,7 @@ echo ""
LOG_DIR="/Users/accusys/momentry/logs"
# ── 1. PostgreSQL ──
echo -e "${YELLOW}[1/7] PostgreSQL${NC}"
echo -e "${YELLOW}[1/8] PostgreSQL${NC}"
PG_DATA="/Users/accusys/pgsql/data"
PG_BIN="/Users/accusys/pgsql/18.3/bin"
if $PG_BIN/pg_isready -q 2>/dev/null; then
@@ -37,7 +37,7 @@ else
fi
# ── 2. Redis ──
echo -e "${YELLOW}[2/7] Redis${NC}"
echo -e "${YELLOW}[2/8] Redis${NC}"
if redis-cli ping 2>/dev/null | grep -q PONG; then
echo -e " ${GREEN}${NC} already running"
else
@@ -47,14 +47,14 @@ else
fi
# ── 3. Qdrant ──
echo -e "${YELLOW}[3/7] Qdrant${NC}"
QDRANT_BIN="${PROJECT_DIR}/services/qdrant/target/release/qdrant"
echo -e "${YELLOW}[3/8] Qdrant${NC}"
QDRANT_BIN="/Users/accusys/momentry_resources/bin/qdrant"
QDRANT_STORAGE="/Users/accusys/momentry/qdrant_storage"
if curl -s -o /dev/null -w "%{http_code}" --connect-timeout 3 http://localhost:6333/healthz 2>/dev/null | grep -q 200; then
echo -e " ${GREEN}${NC} already running"
else
mkdir -p "$QDRANT_STORAGE"
nohup "$QDRANT_BIN" > "$LOG_DIR/qdrant.log" 2>&1 &
"$QDRANT_BIN" > "$LOG_DIR/qdrant.log" 2>&1 &
for i in $(seq 1 15); do
sleep 2
if curl -s -o /dev/null -w "%{http_code}" --connect-timeout 2 http://localhost:6333/healthz 2>/dev/null | grep -q 200; then
@@ -65,7 +65,7 @@ else
fi
# ── 4. Qdrant Collection ──
echo -e "${YELLOW}[4/7] Qdrant Collection${NC}"
echo -e "${YELLOW}[4/8] Qdrant Collection${NC}"
source "$ENV_FILE" 2>/dev/null || true
COLLECTION="${QDRANT_COLLECTION:-momentry_dev_rule1_v2}"
EXISTS=$(curl -s "http://localhost:6333/collections/$COLLECTION" 2>/dev/null | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('result',{}).get('status','not_found'))" 2>/dev/null)
@@ -79,13 +79,13 @@ curl -s "http://localhost:6333/collections/$COLLECTION" 2>/dev/null | python3 -c
check "collection '$COLLECTION' ready"
# ── 5. LLM (Gemma4 / llama.cpp) ──
echo -e "${YELLOW}[5/7] LLM Server (Gemma4)${NC}"
echo -e "${YELLOW}[5/8] LLM Server (Gemma4)${NC}"
if curl -s -o /dev/null -w "%{http_code}" --connect-timeout 5 http://localhost:8082/health 2>/dev/null | grep -q 200; then
echo -e " ${GREEN}${NC} already running"
else
LLM_BIN="/Users/accusys/llama/bin/llama-server"
LLM_MODEL="/Users/accusys/models/google_gemma-4-26B-A4B-it-Q5_K_M.gguf"
nohup "$LLM_BIN" -m "$LLM_MODEL" --host 0.0.0.0 --port 8082 -ngl 99 -c 16384 --temp 0.1 --mlock --reasoning off > "$LOG_DIR/llama_server.log" 2>&1 &
LLM_BIN="/Users/accusys/momentry_resources/llama/bin/llama-server"
LLM_MODEL="/Users/accusys/momentry/models/llm/google_gemma-4-26B-A4B-it-Q5_K_M.gguf"
"$LLM_BIN" -m "$LLM_MODEL" --host 0.0.0.0 --port 8082 -ngl 99 -c 16384 --temp 0.1 --mlock --reasoning off > "$LOG_DIR/llama_server.log" 2>&1 &
echo -e " ${YELLOW}⏳ loading model (~30s)...${NC}"
for i in $(seq 1 30); do
sleep 2
@@ -97,7 +97,7 @@ else
fi
# ── 6. Embedding Server ──
echo -e "${YELLOW}[6/7] EmbeddingGemma${NC}"
echo -e "${YELLOW}[6/8] EmbeddingGemma${NC}"
if curl -s -o /dev/null -w "%{http_code}" --connect-timeout 5 http://localhost:11436/health 2>/dev/null | grep -q 200; then
echo -e " ${GREEN}${NC} already running"
else
@@ -107,22 +107,37 @@ else
VENV_PYTHON="/opt/homebrew/bin/python3.11"
pip install flask 2>/dev/null || true
fi
nohup "$VENV_PYTHON" "$EMBED_SCRIPT" --port 11436 > "$LOG_DIR/embed.log" 2>&1 &
"$VENV_PYTHON" "$EMBED_SCRIPT" --port 11436 > "$LOG_DIR/embed.log" 2>&1 &
sleep 5
curl -s -o /dev/null -w "%{http_code}" --connect-timeout 5 http://localhost:11436/health 2>/dev/null | grep -q 200; check "started"
fi
# ── 7. Playground Server ──
echo -e "${YELLOW}[7/7] Playground API Server${NC}"
echo -e "${YELLOW}[7/8] Playground API Server${NC}"
if curl -s -o /dev/null -w "%{http_code}" -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" --connect-timeout 5 http://127.0.0.1:3003/api/v1/agents/5w1h/status 2>/dev/null | grep -q 200; then
echo -e " ${GREEN}${NC} already running"
else
cd "$PROJECT_DIR"
nohup target/debug/momentry_playground server > "$LOG_DIR/playground.log" 2>&1 &
target/debug/momentry_playground server > "$LOG_DIR/playground.log" 2>&1 &
sleep 4
curl -s -o /dev/null -w "%{http_code}" -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" --connect-timeout 5 http://127.0.0.1:3003/api/v1/agents/5w1h/status 2>/dev/null | grep -q 200; check "started"
fi
# ── 8. Ollama (Gemma4 E4B) ──
echo -e "${YELLOW}[8/8] Ollama (Gemma4 E4B)${NC}"
if curl -s -o /dev/null -w "%{http_code}" --connect-timeout 5 http://localhost:11434/api/tags 2>/dev/null | grep -q 200; then
echo -e " ${GREEN}${NC} already running"
else
OLLAMA_BIN="/Users/accusys/momentry_resources/bin/ollama"
if [ ! -f "$OLLAMA_BIN" ]; then
echo -e " ${YELLOW}⚠ ollama binary not found, skipping${NC}"
else
"$OLLAMA_BIN" serve > "$LOG_DIR/ollama.log" 2>&1 &
sleep 3
curl -s -o /dev/null -w "%{http_code}" --connect-timeout 5 http://localhost:11434/api/tags 2>/dev/null | grep -q 200; check "started"
fi
fi
echo ""
if [ ${#FAILURES[@]} -eq 0 ]; then
echo -e "${GREEN}====================================${NC}"
@@ -138,6 +153,7 @@ echo ""
echo " Playground: http://127.0.0.1:3003"
echo " LLM: http://127.0.0.1:8082"
echo " Embedding: http://127.0.0.1:11436"
echo " Ollama: http://localhost:11434"
echo " Qdrant: http://localhost:6333"
echo " PostgreSQL: localhost:5432"
echo " Redis: localhost:6379"

View File

@@ -1 +0,0 @@
17505

Some files were not shown because too many files have changed in this diff Show More