WhoIsLuwah commited on
Commit
d6c336d
·
1 Parent(s): fe69644

fix(api): return queued or failed rows from /extract/url instead of 500

Browse files

Submitting a URL whose row already existed but was not finished (pending,
processing or failed) returned 500: ExtractionResponse required a full
ReelExtraction and a title, and it dropped status/error. The response
model now matches what the table actually holds, so the dashboard shows
the queue or failure banner for that reel.

Claude-Session: https://claude.ai/code/session_0166W7S7XvHg5P2uyJtP4GiC

Files changed (2) hide show
  1. main.py +7 -2
  2. verify_pipeline.py +21 -0
main.py CHANGED
@@ -160,13 +160,18 @@ class ReelExtraction(BaseModel):
160
  return str(item).strip()
161
 
162
  class ExtractionResponse(BaseModel):
 
 
 
163
  id: Optional[str] = None
164
  url: Optional[str] = None
165
- title: str
166
  raw_transcript: Optional[str] = None
167
  post_caption: Optional[str] = None
168
- extracted_json: ReelExtraction
169
  created_at: Optional[str] = None
 
 
170
 
171
 
172
  def _chat_json(task: str, system_prompt: str, user_prompt: str, parse):
 
160
  return str(item).strip()
161
 
162
  class ExtractionResponse(BaseModel):
163
+ """A saved reel row. /extract/url can return one that is still queued or
164
+ failed (the URL was already submitted), so only finished rows carry a
165
+ title and a full ReelExtraction in extracted_json."""
166
  id: Optional[str] = None
167
  url: Optional[str] = None
168
+ title: Optional[str] = None
169
  raw_transcript: Optional[str] = None
170
  post_caption: Optional[str] = None
171
+ extracted_json: dict
172
  created_at: Optional[str] = None
173
+ status: str = "done"
174
+ error: Optional[str] = None
175
 
176
 
177
  def _chat_json(task: str, system_prompt: str, user_prompt: str, parse):
verify_pipeline.py CHANGED
@@ -167,6 +167,26 @@ def test_extract_url_regression():
167
  main.db.get_reel_by_url = orig_cache
168
 
169
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170
  def test_worker_tick_success():
171
  print("Testing worker_tick processes a pending reel (mocked)...")
172
  import main
@@ -701,6 +721,7 @@ if __name__ == "__main__":
701
  test_worker_tick_empty()
702
  test_run_worker_drain()
703
  test_extract_url_regression()
 
704
  test_delete_reel()
705
  test_get_reel_details()
706
  test_reels_include_cluster()
 
167
  main.db.get_reel_by_url = orig_cache
168
 
169
 
170
+ def test_extract_url_returns_unfinished_row():
171
+ print("Testing POST /extract/url returns an already-queued or failed row instead of 500...")
172
+ import main
173
+ orig = main.db.get_reel_by_url
174
+ try:
175
+ # title is None for rows the iPhone Shortcut inserts over REST
176
+ for status, title, error in (("pending", None, None), ("failed", "Reel (Q)", "Meta blocking request")):
177
+ main.db.get_reel_by_url = lambda url: {
178
+ "id": "row-q", "url": url, "title": title, "raw_transcript": "", "post_caption": None,
179
+ "extracted_json": {}, "created_at": "2026-10-10T00:00:00Z", "cluster": "Unclustered",
180
+ "status": status, "error": error}
181
+ r = client.post("/extract/url", json={"url": "https://www.instagram.com/reel/QUEUED1/"})
182
+ assert r.status_code == 200, r.text
183
+ assert r.json()["status"] == status, r.json()
184
+ assert r.json()["error"] == error, r.json()
185
+ print("[OK] extract_url unfinished-row passthrough passed!")
186
+ finally:
187
+ main.db.get_reel_by_url = orig
188
+
189
+
190
  def test_worker_tick_success():
191
  print("Testing worker_tick processes a pending reel (mocked)...")
192
  import main
 
721
  test_worker_tick_empty()
722
  test_run_worker_drain()
723
  test_extract_url_regression()
724
+ test_extract_url_returns_unfinished_row()
725
  test_delete_reel()
726
  test_get_reel_details()
727
  test_reels_include_cluster()