Problem
POST /api/v1/events/extract splits a long transcript into overlapping 24 000-character
chunks and then extracts events from them strictly one at a time:
for chunk in transcript_chunks:
if len(events) >= _MAX_EVENTS:
break
for ev in await _extract_chunk(chunk):
...
Each _extract_chunk call is an independent, billed Gemini round-trip via
HybridProcessorService.process. Because the loop awaits each one before starting the
next, wall-clock latency is the sum of every chunk's provider latency.
Impact
src/youtube_extension/backend/api/v1/router.py is mounted on the live HTTP surface
(backend/main.py:35 imports v1_router; main.py:164 calls
app.include_router(v1_router)), so this is a user-facing request path, not dead code.
Chunk count grows linearly with transcript length (stride is
_CHUNK_SIZE - _CHUNK_OVERLAP = 23 500 chars):
| Transcript |
Chunks |
Serial latency @ ~3 s/chunk |
| 30 min talk (~30 k chars) |
2 |
~6 s |
| 90 min podcast (~120 k chars) |
~6 |
~18 s |
| 4 h stream (~500 k chars) |
~22 |
~66 s |
The chunks are mutually independent — nothing in _extract_chunk reads state produced by
a previous chunk — so the serialisation buys nothing.
Expected
Extract chunks in bounded windows so independent provider calls overlap, while:
- keeping the merge in chunk order (dedup and the
_MAX_EVENTS budget must select exactly
the events the serial walk selected);
- re-checking the
_MAX_EVENTS budget between windows so extraction still stops early;
- keeping failure isolation — one chunk failing must not discard its siblings' events;
- bounding in-flight billed calls so a very long transcript cannot fan out without limit.
Acceptance
Problem
POST /api/v1/events/extractsplits a long transcript into overlapping 24 000-characterchunks and then extracts events from them strictly one at a time:
Each
_extract_chunkcall is an independent, billed Gemini round-trip viaHybridProcessorService.process. Because the loop awaits each one before starting thenext, wall-clock latency is the sum of every chunk's provider latency.
Impact
src/youtube_extension/backend/api/v1/router.pyis mounted on the live HTTP surface(
backend/main.py:35importsv1_router;main.py:164callsapp.include_router(v1_router)), so this is a user-facing request path, not dead code.Chunk count grows linearly with transcript length (stride is
_CHUNK_SIZE - _CHUNK_OVERLAP= 23 500 chars):The chunks are mutually independent — nothing in
_extract_chunkreads state produced bya previous chunk — so the serialisation buys nothing.
Expected
Extract chunks in bounded windows so independent provider calls overlap, while:
_MAX_EVENTSbudget must select exactlythe events the serial walk selected);
_MAX_EVENTSbudget between windows so extraction still stops early;Acceptance