refactor: remove face embedding architecture - single Qdrant _faces collection
- Delete FaceEmbeddingDb module (face_embedding_db.rs) - Stub match_faces_iterative, generate_seed_embeddings, tmdb_match_handler - Remove sync_trace_embeddings, populate_face_embeddings_to_qdrant - Remove embedding from face.json output (face_processor.py) - Remove embedding from PG UPDATE (store_traced_faces.py) - Remove workspace traces staging (checkin.rs, qdrant_workspace.rs) - Fix tests: add pose_angle to Face, hand_nodes to TkgResult Disabled functions (need reimplement with _faces): - match_faces_iterative (identity agent) - generate_seed_embeddings (TMDb seeds) - tmdb_match_handler (TMDb matching) - cluster_face_embeddings, search_similar_faces - merge_traces_within_cuts
This commit is contained in:
@@ -1,197 +0,0 @@
|
||||
#!/opt/homebrew/bin/python3.11
|
||||
"""
|
||||
Regenerate parent chunk summaries using 5W1H multi-dimensional structure via gemma4.
|
||||
|
||||
5W1H Structure:
|
||||
- Who: Main characters/people involved
|
||||
- What: Key actions/events
|
||||
- When: Temporal context (sequence in story)
|
||||
- Where: Location/setting
|
||||
- Why: Motivation/conflict driving the scene
|
||||
- How: Emotional tone/manner of events
|
||||
"""
|
||||
|
||||
import json
|
||||
import requests
|
||||
import psycopg2
|
||||
import psycopg2.extras
|
||||
|
||||
DB_CONFIG = {"host": "localhost", "user": "accusys", "dbname": "momentry"}
|
||||
UUID = "384b0ff44aaaa1f1"
|
||||
LLAMA_URL = "http://127.0.0.1:8081/v1/chat/completions"
|
||||
|
||||
|
||||
def get_parent_with_children():
|
||||
"""Get all parent chunks with their child chunk texts"""
|
||||
conn = psycopg2.connect(**DB_CONFIG)
|
||||
cur = conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor)
|
||||
|
||||
cur.execute(
|
||||
"""
|
||||
SELECT pc.id, pc.scene_order, pc.start_time, pc.end_time,
|
||||
pc.start_frame, pc.end_frame, pc.fps, pc.summary_text as old_summary,
|
||||
pc.metadata,
|
||||
ARRAY_AGG(c.text_content ORDER BY c.start_time) as child_texts
|
||||
FROM parent_chunks pc
|
||||
LEFT JOIN chunks c ON c.parent_chunk_id = pc.id::varchar
|
||||
WHERE pc.uuid = %s
|
||||
GROUP BY pc.id, pc.scene_order, pc.start_time, pc.end_time,
|
||||
pc.start_frame, pc.end_frame, pc.fps, pc.summary_text, pc.metadata
|
||||
ORDER BY pc.scene_order
|
||||
""",
|
||||
(UUID,),
|
||||
)
|
||||
|
||||
parents = cur.fetchall()
|
||||
cur.close()
|
||||
conn.close()
|
||||
return parents
|
||||
|
||||
|
||||
def call_gemma4(prompt, max_tokens=1500):
|
||||
"""Call Gemma4 via llama-server OpenAI-compatible API"""
|
||||
payload = {
|
||||
"messages": [{"role": "user", "content": prompt}],
|
||||
"max_tokens": max_tokens,
|
||||
"temperature": 0.3,
|
||||
"min_p": 0.1,
|
||||
}
|
||||
try:
|
||||
resp = requests.post(LLAMA_URL, json=payload, timeout=180)
|
||||
if resp.status_code == 200:
|
||||
result = resp.json()
|
||||
content = (
|
||||
result.get("choices", [{}])[0]
|
||||
.get("message", {})
|
||||
.get("content", "")
|
||||
.strip()
|
||||
)
|
||||
return content
|
||||
except Exception as e:
|
||||
print(f" ⚠️ llama-server error: {e}")
|
||||
return ""
|
||||
|
||||
|
||||
def generate_5w1h_summary(parent, scene_num):
|
||||
"""Generate 5W1H structured summary using gemma4"""
|
||||
texts = [t for t in (parent["child_texts"] or []) if t]
|
||||
if not texts:
|
||||
return None
|
||||
|
||||
# Use only first 3 and last 3 dialogue lines for context (much faster)
|
||||
sample_texts = texts[:3] + ["..."] + texts[-3:] if len(texts) > 6 else texts
|
||||
combined = "\n".join(sample_texts)[:1500]
|
||||
duration = parent["end_time"] - parent["start_time"]
|
||||
|
||||
prompt = f"""You are a film scene analyst. Analyze this scene and provide 5W1H analysis.
|
||||
|
||||
Scene {scene_num}/17 | {duration:.0f}s | {len(texts)} dialogue lines
|
||||
|
||||
Key dialogue:
|
||||
{combined}
|
||||
|
||||
Respond with ONLY this JSON:
|
||||
{{"summary_5lines":"...","who":"...","what":"...","when":"...","where":"...","why":"...","how":"...","characters":[],"tone":[],"key_events":[]}}
|
||||
IMPORTANT: "summary_5lines" must be EXACTLY 5 lines describing the scene. Each line should be a complete sentence separated by \\n."""
|
||||
|
||||
response = call_gemma4(prompt, max_tokens=2000)
|
||||
|
||||
if not response:
|
||||
return None
|
||||
|
||||
# Simple JSON extraction: find first { and last }
|
||||
try:
|
||||
start = response.find("{")
|
||||
end = response.rfind("}") + 1
|
||||
if start >= 0 and end > start:
|
||||
return json.loads(response[start:end])
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def update_parent_chunk(parent, analysis):
|
||||
"""Update parent chunk with 5W1H structured data"""
|
||||
if not analysis:
|
||||
return False
|
||||
|
||||
conn = psycopg2.connect(**DB_CONFIG)
|
||||
cur = conn.cursor()
|
||||
|
||||
# Create structured summary text (5 lines)
|
||||
structured_text = f"{analysis.get('summary_5lines', '')}"
|
||||
|
||||
# Update metadata with full 5W1H structure
|
||||
metadata = parent["metadata"] if parent["metadata"] else {}
|
||||
metadata["auto_generated_by"] = "gemma4"
|
||||
metadata["chunk_count"] = len(parent["child_texts"] or [])
|
||||
metadata["structured_summary"] = {
|
||||
"summary_5lines": analysis.get("summary_5lines", ""),
|
||||
"who": analysis.get("who", ""),
|
||||
"what": analysis.get("what", ""),
|
||||
"when": analysis.get("when", ""),
|
||||
"where": analysis.get("where", ""),
|
||||
"why": analysis.get("why", ""),
|
||||
"how": analysis.get("how", ""),
|
||||
"characters": analysis.get("characters", []),
|
||||
"tone": analysis.get("tone", []),
|
||||
"key_events": analysis.get("key_events", []),
|
||||
}
|
||||
|
||||
cur.execute(
|
||||
"""
|
||||
UPDATE parent_chunks
|
||||
SET summary_text = %s,
|
||||
metadata = %s::jsonb
|
||||
WHERE id = %s
|
||||
""",
|
||||
(structured_text, json.dumps(metadata, ensure_ascii=False), parent["id"]),
|
||||
)
|
||||
|
||||
conn.commit()
|
||||
cur.close()
|
||||
conn.close()
|
||||
return True
|
||||
|
||||
|
||||
def main():
|
||||
print(f"🎬 Regenerating 5W1H summaries for {UUID}")
|
||||
print(f" Using llama.cpp server at {LLAMA_URL}")
|
||||
print("=" * 70)
|
||||
|
||||
parents = get_parent_with_children()
|
||||
print(f"📥 Found {len(parents)} parent chunks")
|
||||
|
||||
success_count = 0
|
||||
for i, parent in enumerate(parents):
|
||||
duration = parent["end_time"] - parent["start_time"]
|
||||
text_count = len(parent["child_texts"] or [])
|
||||
print(
|
||||
f"\n🎬 Scene {parent['scene_order']}: {parent['start_time']:.0f}s-{parent['end_time']:.0f}s ({duration:.0f}s, {text_count} chunks)"
|
||||
)
|
||||
if parent["old_summary"]:
|
||||
print(f" Old: {parent['old_summary'][:80]}...")
|
||||
|
||||
analysis = generate_5w1h_summary(parent, parent["scene_order"])
|
||||
|
||||
if analysis:
|
||||
summary = analysis.get("summary_5lines", "N/A")
|
||||
print(f" ✅ Summary: {summary[:100]}...")
|
||||
print(f" 👤 Who: {analysis.get('who', 'N/A')[:60]}")
|
||||
print(f" 📍 Where: {analysis.get('where', 'N/A')[:60]}")
|
||||
print(f" 💡 Why: {analysis.get('why', 'N/A')[:60]}")
|
||||
|
||||
if update_parent_chunk(parent, analysis):
|
||||
success_count += 1
|
||||
else:
|
||||
print(" ❌ Failed to generate analysis")
|
||||
|
||||
print(f"\n{'=' * 70}")
|
||||
print(
|
||||
f"✅ Updated {success_count}/{len(parents)} parent chunks with 5W1H summaries"
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user