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:
@@ -0,0 +1,119 @@
|
||||
use axum::{
|
||||
extract::{Path, State},
|
||||
http::StatusCode,
|
||||
routing::{get, post},
|
||||
Json, Router,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::api::types::AppState;
|
||||
use crate::core::checkin;
|
||||
use crate::core::db::VideoStatus;
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct CheckinResponse {
|
||||
file_uuid: String,
|
||||
pre_chunks_moved: usize,
|
||||
speaker_detections_moved: usize,
|
||||
vectors_moved: usize,
|
||||
status: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct CheckoutResponse {
|
||||
file_uuid: String,
|
||||
rows_deleted: usize,
|
||||
status: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct WorkspaceStatusResponse {
|
||||
file_uuid: String,
|
||||
exists: bool,
|
||||
}
|
||||
|
||||
async fn checkin_handler(
|
||||
State(state): State<AppState>,
|
||||
Path(file_uuid): Path<String>,
|
||||
) -> Result<Json<CheckinResponse>, (StatusCode, Json<serde_json::Value>)> {
|
||||
match checkin::checkin(&state.db, &file_uuid).await {
|
||||
Ok(result) => {
|
||||
if let Err(e) = state
|
||||
.db
|
||||
.update_video_status(&file_uuid, VideoStatus::Indexed)
|
||||
.await
|
||||
{
|
||||
tracing::warn!(
|
||||
"Failed to update video status to Indexed for {}: {}",
|
||||
file_uuid,
|
||||
e
|
||||
);
|
||||
}
|
||||
Ok(Json(CheckinResponse {
|
||||
file_uuid: result.file_uuid.clone(),
|
||||
pre_chunks_moved: result.pre_chunks_moved,
|
||||
speaker_detections_moved: result.speaker_detections_moved,
|
||||
vectors_moved: result.vectors_moved,
|
||||
status: "indexed".to_string(),
|
||||
}))
|
||||
}
|
||||
Err(e) => Err((
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
Json(serde_json::json!({
|
||||
"error": format!("Checkin failed: {}", e),
|
||||
"file_uuid": file_uuid,
|
||||
})),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
async fn checkout_handler(
|
||||
State(state): State<AppState>,
|
||||
Path(file_uuid): Path<String>,
|
||||
) -> Result<Json<CheckoutResponse>, (StatusCode, Json<serde_json::Value>)> {
|
||||
match checkin::checkout(&state.db, &file_uuid).await {
|
||||
Ok(result) => {
|
||||
if let Err(e) = state
|
||||
.db
|
||||
.update_video_status(&file_uuid, VideoStatus::CheckedOut)
|
||||
.await
|
||||
{
|
||||
tracing::warn!(
|
||||
"Failed to update video status to CheckedOut for {}: {}",
|
||||
file_uuid,
|
||||
e
|
||||
);
|
||||
}
|
||||
Ok(Json(CheckoutResponse {
|
||||
file_uuid: result.file_uuid.clone(),
|
||||
rows_deleted: result.rows_deleted,
|
||||
status: "checked_out".to_string(),
|
||||
}))
|
||||
}
|
||||
Err(e) => Err((
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
Json(serde_json::json!({
|
||||
"error": format!("Checkout failed: {}", e),
|
||||
"file_uuid": file_uuid,
|
||||
})),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
async fn workspace_status_handler(Path(file_uuid): Path<String>) -> Json<WorkspaceStatusResponse> {
|
||||
use crate::core::db::workspace_sqlite::WorkspaceDb;
|
||||
Json(WorkspaceStatusResponse {
|
||||
file_uuid: file_uuid.clone(),
|
||||
exists: WorkspaceDb::exists(&file_uuid),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn checkin_routes() -> Router<AppState> {
|
||||
Router::new()
|
||||
.route("/api/v1/file/:file_uuid/checkin", post(checkin_handler))
|
||||
.route("/api/v1/file/:file_uuid/checkout", post(checkout_handler))
|
||||
.route(
|
||||
"/api/v1/file/:file_uuid/workspace",
|
||||
get(workspace_status_handler),
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user