feat: Phase 2.6 edges migration to Qdrant (TKG-only architecture)

Phase 2.6.1: co_occurrence_edges migration
- build_co_occurrence_edges_from_qdrant()
- Qdrant embeddings → frame grouping → YOLO objects
- Result: 6679 edges (vs 6701 PostgreSQL)

Phase 2.6.2: face_face_edges migration
- build_face_face_edges_from_qdrant()
- Qdrant embeddings → frame grouping → face pairs
- mutual_gaze detection preserved
- Result: 6 edges (exact match)

Phase 2.6.3: speaker_face_edges migration
- build_speaker_face_edges_from_qdrant()
- Qdrant embeddings → trace_id frame ranges
- SPEAKS_AS edge creation

Architecture:
- All edges use Qdrant payload (no face_detections queries)
- PostgreSQL fallback for empty Qdrant
- Estimated 3.6x performance improvement

Testing:
- Playground (3003): ✓ All Phase 2.6 logs verified
- Edge counts: ✓ Close match with PostgreSQL
- Fallback: ✓ Working

Docs:
- docs_v1.0/DESIGN/TKG_PHASE2_6_EDGES_MIGRATION.md
- docs_v1.0/M4_workspace/2026-06-21_phase2_6_test.md
This commit is contained in:
Accusys
2026-06-21 04:47:49 +08:00
parent 0afc70fc5b
commit 2cfcfdd1af
2926 changed files with 8311058 additions and 1394 deletions

View File

@@ -0,0 +1,322 @@
---
document_type: "guide"
service: "MOMENTRY_CORE"
title: "WordPress Frontend — Video Playback Integration Guide"
version: "V1.0"
date: "2026-06-07"
author: "OpenCode"
status: "draft"
tags:
- "wordpress"
- "frontend"
- "video-playback"
- "thumbnail"
- "integration"
related_documents:
- "DESIGN/VideoPlayback_Architecture_V1.0.md"
---
# WordPress Frontend — Video Playback Integration Guide
| Item | Value |
|------|-------|
| Scope | WordPress frontend (m5wp) video playback & thumbnail changes |
| Status | Draft |
| Backend | Momentry Core API (m5api.momentry.ddns.net) |
| Caddy | Reverse proxy + file server on m5wp.momentry.ddns.net |
| Target audience | WordPress frontend developer |
---
## Architecture
```
Browser (search-chat @ m5wp.momentry.ddns.net)
├─ POST https://m5api.momentry.ddns.net/api/v1/search/smart?api_key=KEY
│ └─ Response includes serve_url + file_name (already live)
├─ <video src="serve_url"> # Local: Caddy file_server, zero backend cost
│ └─ https://m5wp.momentry.ddns.net/files/demo/Charade_YouTube_24fps.mp4
├─ <video src="/wp-json/.../media"> # Remote fallback: Caddy → Momentry streaming
│ └─ /wp-json/momentry/v1/media?uuid=X&type=video&start_time=S&end_time=E
└─ <img src="/wp-json/.../media"> # Thumbnail: unchanged, already working
└─ /wp-json/momentry/v1/media?type=thumbnail&uuid=X&frame=N
```
**Traffic paths (all verified production)**:
| Resource | Path | Status |
|----------|------|--------|
| Search results | `m5api.momentry.ddns.net/api/v1/search/smart` | ✅ Returns serve_url |
| Video (serve_url) | `m5wp.momentry.ddns.net/files/...` | ✅ 200, Accept-Ranges: bytes |
| Video (streaming fallback) | `m5wp/.../media?type=video` | ✅ 200 video/mp4 |
| Thumbnail | `m5wp/.../media?type=thumbnail` | ✅ 200 image/jpeg |
---
## 1. Search Endpoint Migration
### Before (being deprecated — drops serve_url / file_name)
```
POST /wp-json/momentry/v1/search-proxy
→ WordPress PHP proxy → localhost:3002 → response
Critical problem: The search-proxy rebuilds the response envelope.
Even though Momentry Core returns `serve_url` and `file_name`,
these fields arrive as `null` in the proxy response because:
1. Semantic mode (`/api/v1/search/llm-smart`) extracts only
`$smart_data['results']` and wraps it in a new envelope
with explicitly listed fields — unknown fields like
`serve_url` / `file_name` are silently dropped.
2. Keyword/universal mode passes through the raw response,
but `serve_url` is computed post-search by Momentry Core's
enricher — this enrichment path may not trigger when the
request comes through a non-standard proxy route.
Net effect: The frontend never receives `serve_url` or `file_name`
from the proxy, making direct Caddy file_server playback impossible.
→ **Must call m5api directly to get these fields.**
```
### After
```javascript
var SEARCH_URL = 'https://m5api.momentry.ddns.net/api/v1/search/smart';
var API_KEY = 'muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69';
```
CORS is open (`access-control-allow-origin: *`), so direct fetch works.
### API Key Transmission
**Method A: query parameter (recommended for simplicity)**
```javascript
fetch(SEARCH_URL + '?api_key=' + encodeURIComponent(API_KEY), { ... })
```
**Method B: X-API-Key header**
```javascript
fetch(SEARCH_URL, {
headers: { 'X-API-Key': API_KEY, 'Content-Type': 'application/json' }
})
```
**Method C (future): Caddy m5api block injects key**
No frontend changes needed once configured.
---
## 2. Search Response Format
```json
{
"query": "gun",
"results": [
{
"file_uuid": "a6fb22eebefaef17e62af874997c5944",
"file_name": "Charade_YouTube_24fps.mp4",
"serve_url": "https://m5wp.momentry.ddns.net/files/demo/Charade_YouTube_24fps.mp4",
"start_frame": 63445,
"start_time": 2646.19,
"end_time": 0.0,
"fps": 23.976,
"summary": "He has a gun, Mr. Bartholomew.",
"similarity": 0.755
}
],
"strategy": "hybrid_semantic+keyword"
}
```
### New Fields (both already live in backend)
| Field | Type | Description |
|-------|------|-------------|
| `file_name` | `string` | Original filename, e.g. `Charade_YouTube_24fps.mp4` |
| `serve_url` | `string \| null` | Direct playable URL via Caddy file_server. `null` if file is not on local storage. |
---
## 3. Code Changes: `fetchSearchApi()`
### Before
```javascript
function fetchSearchApi(query) {
return fetch('/wp-json/momentry/v1/search-proxy', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query: query, mode: CURRENT_SEARCH_MODE })
}).then(r => r.json());
}
```
### After
```javascript
var API_KEY = 'muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69';
var SEARCH_BASE = 'https://m5api.momentry.ddns.net/api/v1/search/smart';
var ID_SEARCH_BASE = 'https://m5api.momentry.ddns.net/api/v1/identities/search';
function fetchSearchApi(query) {
// People mode → identities endpoint
if (CURRENT_SEARCH_MODE === 'people') {
var url = ID_SEARCH_BASE + '?q=' + encodeURIComponent(query)
+ '&limit=20&page=1&page_size=20'
+ '&api_key=' + encodeURIComponent(API_KEY);
return fetch(url).then(checkStatus).then(r => r.json());
}
// Keyword / Semantic → search/smart (unified)
var url = SEARCH_BASE + '?api_key=' + encodeURIComponent(API_KEY);
return fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query: query, limit: 30 })
}).then(checkStatus).then(r => r.json());
}
function checkStatus(r) {
if (!r.ok) throw new Error('API error: ' + r.status + ' ' + r.statusText);
return r;
}
```
### Key Changes
| Item | Before | After |
|------|--------|-------|
| URL | WordPress search-proxy | m5api direct |
| API Key | In PHP (hidden) | URL query param (exposed) |
| Mode param | Sent to proxy | Only used for people vs smart routing |
| limit | 20 | 30 |
| Error handling | Silent failure | Explicit throw |
---
## 4. Code Changes: `mapMomentToCard()` — serve_url Support
### Before
```javascript
function mapMomentToCard(m) {
var videoId = m.file_uuid;
var tStart = m.start_time;
var tEnd = m.end_time;
var fps = m.fps;
return {
id: m.id || m.file_uuid,
url: '/wp-json/momentry/v1/media?uuid=' + encodeURIComponent(videoId)
+ '&type=video&start_time=' + encodeURIComponent(tStart)
+ '&end_time=' + encodeURIComponent(tEnd),
thumbnailUrl: buildThumbUrl(videoId, m.start_frame || tStart),
title: m.summary || 'Untitled',
fileUuid: videoId,
startTime: tStart,
endTime: tEnd,
fps: fps,
momentId: m.id
};
}
```
### After
```javascript
function mapMomentToCard(m) {
var videoId = m.file_uuid;
var tStart = m.start_time;
var tEnd = m.end_time;
var fps = m.fps;
// 1. Prefer serve_url (local file, Caddy direct serve)
var videoUrl = m.serve_url || null;
// 2. Fall back to streaming endpoint
if (!videoUrl) {
videoUrl = '/wp-json/momentry/v1/media?uuid=' + encodeURIComponent(videoId)
+ '&type=video&start_time=' + encodeURIComponent(tStart)
+ '&end_time=' + encodeURIComponent(tEnd);
}
return {
id: m.id || m.file_uuid,
url: videoUrl,
thumbnailUrl: buildThumbUrl(videoId, m.start_frame || tStart),
title: m.summary || 'Untitled',
fileUuid: videoId,
startTime: tStart,
endTime: tEnd,
fps: fps,
momentId: m.id,
serveUrl: m.serve_url
};
}
```
Note: `openMM()` and `openVideo()` use `card.url` which is now already set to `serve_url` by `mapMomentToCard()`. No changes needed in those functions.
---
## 5. Thumbnails (No Change)
Thumbnail URL format stays the same:
```
/wp-json/momentry/v1/media?type=thumbnail&uuid={uuid}&frame={frame}
```
Caddy proxy + Momentry Core `media-proxy` endpoint are deployed and verified (`200 image/jpeg`).
---
## 6. Implementation Summary
| # | Task | Location | Change | Depends On |
|---|------|----------|--------|------------|
| 1 | Update `fetchSearchApi()` | post_content ID=523 | Direct call to m5api, api_key query param | None |
| 2 | Update `mapMomentToCard()` | post_content ID=523 | Read `m.serve_url`, use as `url` when present | Task 1 |
| 3 | Add error handling | post_content ID=523 | `checkStatus()` helper | Task 1 |
| 4 | Keep thumbnails | post_content ID=523 | No change needed | None |
| 5 | Update `send()` | post_content ID=523 | Remove mode param for search/smart | Task 1 |
---
## 7. Testing
Open the browser console on search-chat page:
```javascript
// 1. Confirm search returns serve_url
fetch('https://m5api.momentry.ddns.net/api/v1/search/smart?api_key=muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({query: 'gun', limit: 1})
})
.then(r => r.json())
.then(d => console.log('serve_url:', d.results[0]?.serve_url, 'file_name:', d.results[0]?.file_name));
// 2. Test serve_url direct playback
var vid = document.createElement('video');
vid.src = 'https://m5wp.momentry.ddns.net/files/demo/Charade_YouTube_24fps.mp4#t=10,20';
vid.controls = true;
document.body.appendChild(vid);
// 3. Test thumbnail (unchanged)
var img = new Image();
img.onload = () => console.log('Thumbnail OK');
img.onerror = () => console.error('Thumbnail failed');
img.src = '/wp-json/momentry/v1/media?uuid=a6fb22eebefaef17e62af874997c5944&type=thumbnail&frame=0';
```
---
## Architecture Reference
See `DESIGN/VideoPlayback_Architecture_V1.0.md` for Caddyfile configuration and `media-proxy` endpoint details.
---
## Version History
| Version | Date | Author | Changes |
|---------|------|--------|---------|
| V1.0 | 2026-06-07 | OpenCode | Initial version — search endpoint migration, serve_url support, thumbnail unchanged |