feat: Phase 2-3 TKG-only architecture
Phase 2.1: build_face_trace_nodes_from_qdrant() - Read trace_id, frame, bbox directly from Qdrant payload - No dependency on face_detections table Phase 2.3: Rule2 queries TKG nodes - identity resolution from tkg_nodes.properties.identity_id - TKG-only architecture (Phase 2.3) Phase 3: Identity Agent updates TKG nodes - match_faces_iterative() updates tkg_nodes.properties - bind_identity_trace() syncs identity_id to TKG - unbind_identity() removes identity_id from TKG Test results: - 23 face_trace nodes from Qdrant (Phase 2.1) - 75 relationship chunks (Rule2) - TKG rebuild: Phase0 → Phase1 → Phase2
This commit is contained in:
@@ -225,6 +225,32 @@ pub async fn unbind_identity(
|
||||
)
|
||||
})?;
|
||||
|
||||
// Phase 2.3: Also update TKG node (find trace_id first)
|
||||
let trace_id_opt: Option<i32> = sqlx::query_scalar(&format!(
|
||||
"SELECT trace_id FROM {} WHERE file_uuid = $1 AND face_id = $2",
|
||||
table
|
||||
))
|
||||
.bind(&req.file_uuid)
|
||||
.bind(&req.face_id)
|
||||
.fetch_optional(state.db.pool())
|
||||
.await
|
||||
.ok()
|
||||
.flatten();
|
||||
|
||||
if let Some(trace_id) = trace_id_opt {
|
||||
let nodes_table = crate::core::db::schema::table_name("tkg_nodes");
|
||||
let external_id = format!("trace_{}", trace_id);
|
||||
let _ = sqlx::query(&format!(
|
||||
"UPDATE {} SET properties = properties - 'identity_id' - 'identity_name' \
|
||||
WHERE file_uuid = $1 AND node_type = 'face_trace' AND external_id = $2",
|
||||
nodes_table
|
||||
))
|
||||
.bind(&req.file_uuid)
|
||||
.bind(&external_id)
|
||||
.execute(state.db.pool())
|
||||
.await;
|
||||
}
|
||||
|
||||
// Record history if there was a binding
|
||||
if let Some(identity_id) = old_identity_id {
|
||||
// Clear bind redo stack
|
||||
@@ -794,6 +820,33 @@ pub async fn bind_identity_trace(
|
||||
)
|
||||
})?;
|
||||
|
||||
// Phase 2.3: Also update TKG node properties
|
||||
let nodes_table = crate::core::db::schema::table_name("tkg_nodes");
|
||||
let external_id = format!("trace_{}", req.trace_id);
|
||||
let identity_name: Option<String> = sqlx::query_scalar(&format!(
|
||||
"SELECT name FROM {} WHERE id = $1",
|
||||
id_table
|
||||
))
|
||||
.bind(identity_id)
|
||||
.fetch_optional(state.db.pool())
|
||||
.await
|
||||
.ok()
|
||||
.flatten();
|
||||
|
||||
let _ = sqlx::query(&format!(
|
||||
"UPDATE {} SET properties = jsonb_set(\
|
||||
jsonb_set(properties, '{{identity_id}}', $1::jsonb, false),\
|
||||
'{{identity_name}}', $2::jsonb, false)\
|
||||
WHERE file_uuid = $3 AND node_type = 'face_trace' AND external_id = $4",
|
||||
nodes_table
|
||||
))
|
||||
.bind(identity_id)
|
||||
.bind(identity_name.as_deref())
|
||||
.bind(&req.file_uuid)
|
||||
.bind(&external_id)
|
||||
.execute(state.db.pool())
|
||||
.await;
|
||||
|
||||
// Clear bind redo stack
|
||||
let _ = sqlx::query(&format!(
|
||||
"DELETE FROM {} WHERE identity_id = $1 AND is_undone = true AND operation IN ('bind','unbind','bind_trace')",
|
||||
|
||||
Reference in New Issue
Block a user