Ruperth commited on
Commit
7271127
·
1 Parent(s): be575e1

feat: persist user comments per video with author and source filters

Browse files

Extends /predict to accept video_id, author and a persist flag, so a manual post lands in Supabase under source=user_comment with the team-member name attached. /predictions now wraps the rows under a predictions key and supports filtering by source. The list_predictions helper and SQL schema gain the author column and a matching source filter.

src/api/routes/predict.py CHANGED
@@ -21,13 +21,16 @@ router = APIRouter(tags=["Prediction"])
21
  @router.post("/predict", response_model=PredictResponse)
22
  async def predict(request: PredictRequest):
23
  response = predict_single(request.text, request.threshold)
24
- save_prediction(
25
- text=request.text,
26
- result=response,
27
- source="api_direct",
28
- threshold=request.threshold,
29
- latency_ms=response.latency_ms,
30
- )
 
 
 
31
  return response
32
 
33
 
@@ -110,7 +113,8 @@ async def predict_video(request: VideoRequest):
110
  @router.get("/predictions")
111
  async def get_predictions(
112
  video_id: str | None = Query(default=None),
 
113
  limit: int = Query(default=50, ge=1, le=200),
114
  ):
115
- rows = list_predictions(video_id=video_id, limit=limit)
116
- return rows
 
21
  @router.post("/predict", response_model=PredictResponse)
22
  async def predict(request: PredictRequest):
23
  response = predict_single(request.text, request.threshold)
24
+ if request.persist:
25
+ save_prediction(
26
+ text=request.text,
27
+ result=response,
28
+ source="user_comment" if request.author else "api_direct",
29
+ video_id=request.video_id,
30
+ author=request.author,
31
+ threshold=request.threshold,
32
+ latency_ms=response.latency_ms,
33
+ )
34
  return response
35
 
36
 
 
113
  @router.get("/predictions")
114
  async def get_predictions(
115
  video_id: str | None = Query(default=None),
116
+ source: str | None = Query(default=None),
117
  limit: int = Query(default=50, ge=1, le=200),
118
  ):
119
+ rows = list_predictions(video_id=video_id, source=source, limit=limit)
120
+ return {"predictions": rows, "total": len(rows)}
src/api/schemas.py CHANGED
@@ -8,6 +8,9 @@ from pydantic import BaseModel, Field, field_validator
8
  class PredictRequest(BaseModel):
9
  text: str = Field(..., min_length=1, max_length=5000)
10
  threshold: float = Field(0.5, ge=0.0, le=1.0)
 
 
 
11
 
12
  @field_validator("text")
13
  @classmethod
 
8
  class PredictRequest(BaseModel):
9
  text: str = Field(..., min_length=1, max_length=5000)
10
  threshold: float = Field(0.5, ge=0.0, le=1.0)
11
+ video_id: Optional[str] = None
12
+ author: Optional[str] = None
13
+ persist: bool = True
14
 
15
  @field_validator("text")
16
  @classmethod
src/db/supabase_client.py CHANGED
@@ -51,6 +51,7 @@ def save_prediction(
51
  video_url: str | None = None,
52
  threshold: float | None = None,
53
  latency_ms: float | None = None,
 
54
  ) -> None:
55
  """Persist a single prediction, silently no-op when DB is not configured.
56
 
@@ -87,6 +88,7 @@ def save_prediction(
87
  "threshold": threshold,
88
  "latency_ms": latency_ms if latency_ms is not None else data.get("latency_ms"),
89
  "source": source,
 
90
  }
91
  client.table(_TABLE).insert(row).execute()
92
  except Exception as exc:
@@ -96,6 +98,7 @@ def save_prediction(
96
  def list_predictions(
97
  video_id: str | None = None,
98
  limit: int = 50,
 
99
  ) -> list[dict]:
100
  """Return latest predictions ordered by ``created_at`` desc.
101
 
@@ -109,6 +112,8 @@ def list_predictions(
109
  query = client.table(_TABLE).select("*").order("created_at", desc=True)
110
  if video_id:
111
  query = query.eq("video_id", video_id)
 
 
112
  query = query.limit(max(1, min(limit, 200)))
113
  response = query.execute()
114
  return list(getattr(response, "data", []) or [])
 
51
  video_url: str | None = None,
52
  threshold: float | None = None,
53
  latency_ms: float | None = None,
54
+ author: str | None = None,
55
  ) -> None:
56
  """Persist a single prediction, silently no-op when DB is not configured.
57
 
 
88
  "threshold": threshold,
89
  "latency_ms": latency_ms if latency_ms is not None else data.get("latency_ms"),
90
  "source": source,
91
+ "author": author,
92
  }
93
  client.table(_TABLE).insert(row).execute()
94
  except Exception as exc:
 
98
  def list_predictions(
99
  video_id: str | None = None,
100
  limit: int = 50,
101
+ source: str | None = None,
102
  ) -> list[dict]:
103
  """Return latest predictions ordered by ``created_at`` desc.
104
 
 
112
  query = client.table(_TABLE).select("*").order("created_at", desc=True)
113
  if video_id:
114
  query = query.eq("video_id", video_id)
115
+ if source:
116
+ query = query.eq("source", source)
117
  query = query.limit(max(1, min(limit, 200)))
118
  response = query.execute()
119
  return list(getattr(response, "data", []) or [])
supabase/predictions_setup.sql CHANGED
@@ -17,9 +17,13 @@ create table if not exists public.predictions (
17
  model_used text,
18
  threshold double precision,
19
  latency_ms double precision,
20
- source text -- "api_direct" | "video_fetch" | "user_comment"
 
21
  );
22
 
 
 
 
23
  -- 2. Indexes for the queries the API will run
24
  create index if not exists predictions_created_at_idx
25
  on public.predictions (created_at desc);
 
17
  model_used text,
18
  threshold double precision,
19
  latency_ms double precision,
20
+ source text, -- "api_direct" | "video_fetch" | "user_comment"
21
+ author text
22
  );
23
 
24
+ -- Migration: add author column on existing installs
25
+ alter table public.predictions add column if not exists author text;
26
+
27
  -- 2. Indexes for the queries the API will run
28
  create index if not exists predictions_created_at_idx
29
  on public.predictions (created_at desc);