Spaces:
Running
Running
Update src/summarization/README.md
Browse files- src/summarization/README.md +95 -109
src/summarization/README.md
CHANGED
|
@@ -1,116 +1,102 @@
|
|
| 1 |
-
# Summarization Module
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
- **Key Classes:**
|
| 33 |
-
- `SummarySchema` — Full structured output (title, detected_language, summary, segments, suggested_category, conclusion, topics).
|
| 34 |
-
- `SegmentSchema` — A timeline section (title, summary, key_insight, why_it_matters).
|
| 35 |
-
|
| 36 |
-
### 2. `note_generator.py`
|
| 37 |
-
- **Purpose:** Generate notes using a local mT5 model with chunk-based map-reduce and schema validation.
|
| 38 |
-
- **Main Class:** `NoteGenerator`
|
| 39 |
-
- **Key Methods:**
|
| 40 |
-
- `generateSummary(transcript, title)` — Runs the map-reduce pipeline and returns a `SummarySchema`-validated dict.
|
| 41 |
-
- `format_notes_to_markdown(json_notes)` — Converts JSON to clean Markdown.
|
| 42 |
-
- `format_final_notes(notes, title, url, duration)` — Wraps Markdown with Source/Duration header.
|
| 43 |
-
|
| 44 |
-
### 3. `segmenter.py`
|
| 45 |
-
- **Purpose:** Split long texts into smaller segments for preprocessing.
|
| 46 |
-
- **Main Class:** `TranscriptSegmenter`
|
| 47 |
-
- **Key Methods:**
|
| 48 |
-
- `segment_text_by_words()` — Split text into fixed-size word chunks (used by the mT5 map step).
|
| 49 |
-
- `segment_by_time()` — Split by time intervals.
|
| 50 |
-
- `segment_by_topic()` — Split by paragraph/topic boundaries.
|
| 51 |
-
- `clean_text()` — Remove filler words.
|
| 52 |
-
|
| 53 |
-
## JSON Output Structure
|
| 54 |
-
```json
|
| 55 |
-
{
|
| 56 |
-
"title": "...",
|
| 57 |
-
"detected_language": "Arabic",
|
| 58 |
-
"summary": "Overall summary (3-5 sentences)",
|
| 59 |
-
"segments": [
|
| 60 |
-
{
|
| 61 |
-
"title": "Segment title",
|
| 62 |
-
"summary": "What this section covers",
|
| 63 |
-
"key_insight": "Most important point",
|
| 64 |
-
"why_it_matters": "Why this is valuable"
|
| 65 |
-
}
|
| 66 |
-
],
|
| 67 |
-
"suggested_category": "General",
|
| 68 |
-
"conclusion": "Final takeaway",
|
| 69 |
-
"topics": ["Topic1", "Topic2"]
|
| 70 |
-
}
|
| 71 |
```
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
| summary | Overall Summary | الملخص العام |
|
| 87 |
-
| timeline | Timeline | التسلسل الزمني |
|
| 88 |
-
| insight | Key Insight | أهم نقطة |
|
| 89 |
-
| why | Why It Matters | لماذا يهم؟ |
|
| 90 |
-
| conclusion | Conclusion | الخلاصة |
|
| 91 |
-
|
| 92 |
-
## Testing
|
| 93 |
```python
|
| 94 |
from src.summarization.note_generator import NoteGenerator
|
| 95 |
|
| 96 |
-
generator = NoteGenerator()
|
| 97 |
-
|
| 98 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 99 |
|
| 100 |
-
#
|
| 101 |
-
|
| 102 |
-
notes_md = generator.format_notes_to_markdown(summary_json)
|
| 103 |
-
print(notes_md)
|
| 104 |
```
|
| 105 |
|
| 106 |
-
##
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
|
| 115 |
-
|
|
| 116 |
-
| `
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Summarization Module
|
| 2 |
+
|
| 3 |
+
Part of the `Aldea-Server` project (`src/summarization`). This module turns a raw YouTube video transcript into a structured, validated set of study notes: a general summary, a chronological timeline of segments, key insights, a conclusion, and extracted topics — in either Arabic or English, auto-detected from the transcript.
|
| 4 |
+
|
| 5 |
+
## Evolution of this module
|
| 6 |
+
|
| 7 |
+
The summarization approach went through three phases before reaching its current design:
|
| 8 |
+
|
| 9 |
+
| Phase | Approach | Outcome |
|
| 10 |
+
|---|---|---|
|
| 11 |
+
| 1 | Third-party LLM API only | Fluent output, but fully dependent on an external service (cost, latency, no offline mode) |
|
| 12 |
+
| 2 | Local **mT5** model | **Discontinued** — output quality (coherence, accuracy) was too weak compared to the API, especially on longer/Arabic transcripts |
|
| 13 |
+
| 3 (current) | Hybrid: **Groq API** (Llama-3.3-70B) **or** local **Qwen2.5-0.5B-Instruct** | Best of both — high quality via the cloud path, or fully local/offline inference when data privacy or no-internet constraints apply |
|
| 14 |
+
|
| 15 |
+
Switching between the Groq and local-Qwen backends is controlled by a single `INFERENCE_MODE` flag (see `model_loader.py` in `src/utils/`, shared across the project). Both backends share the exact same prompts, chunking logic, and output schema — the rest of the app never needs to know which one produced a given summary.
|
| 16 |
+
|
| 17 |
+
> ⚠️ Evaluation note: comparisons between the three phases have been **qualitative/manual** so far (no ROUGE/BLEU scores were collected). See *Limitations* below.
|
| 18 |
+
|
| 19 |
+
## Architecture (Map-Reduce pipeline)
|
| 20 |
+
|
| 21 |
+
```
|
| 22 |
+
Transcript ─▶ TranscriptSegmenter ─▶ MAP (summarize each chunk)
|
| 23 |
+
│
|
| 24 |
+
▼
|
| 25 |
+
REDUCE (combine into one summary)
|
| 26 |
+
│
|
| 27 |
+
▼
|
| 28 |
+
Validate against SummarySchema (Pydantic)
|
| 29 |
+
│
|
| 30 |
+
▼
|
| 31 |
+
Markdown formatting (timeline + conclusion)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
```
|
| 33 |
+
|
| 34 |
+
## Files in this module
|
| 35 |
+
|
| 36 |
+
| File | Responsibility |
|
| 37 |
+
|---|---|
|
| 38 |
+
| `note_generator.py` | `NoteGenerator` — orchestrates the Map-Reduce pipeline, language detection, schema validation, fallback handling, Markdown formatting, and the "chat with this note" Q&A feature |
|
| 39 |
+
| `segmenter.py` | `TranscriptSegmenter` — splits transcripts into chunks (by word count, by time, or by topic heuristic) and cleans filler words |
|
| 40 |
+
| `schemas.py` | Pydantic models (`SummarySchema`, `SegmentSchema`) that define and enforce the output contract |
|
| 41 |
+
| `__init__.py` | Module entry point |
|
| 42 |
+
|
| 43 |
+
> `model_loader.py` (which exposes `generate_text`, `INFERENCE_MODE`, `get_groq_client`, `_generate_text_local`) lives in `src/utils/` since it's shared by other parts of the server, not just summarization.
|
| 44 |
+
|
| 45 |
+
## Usage
|
| 46 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 47 |
```python
|
| 48 |
from src.summarization.note_generator import NoteGenerator
|
| 49 |
|
| 50 |
+
generator = NoteGenerator(chunk_size_words=200)
|
| 51 |
+
|
| 52 |
+
result = generator.generateSummary(transcript_text, video_title)
|
| 53 |
+
# result is a dict validated against SummarySchema
|
| 54 |
+
|
| 55 |
+
markdown_body = generator.format_notes_to_markdown(result)
|
| 56 |
+
final_note = generator.format_final_notes(
|
| 57 |
+
markdown_body,
|
| 58 |
+
video_title=video_title,
|
| 59 |
+
video_url=video_url,
|
| 60 |
+
duration=duration_seconds,
|
| 61 |
+
detected_language=result["detected_language"],
|
| 62 |
+
)
|
| 63 |
|
| 64 |
+
# Optional: ask a follow-up question grounded in the generated note
|
| 65 |
+
answer = generator.chat_with_note(final_note, "What's the main takeaway?")
|
|
|
|
|
|
|
| 66 |
```
|
| 67 |
|
| 68 |
+
## Output schema
|
| 69 |
+
|
| 70 |
+
**`SummarySchema`**
|
| 71 |
+
|
| 72 |
+
| Field | Type | Notes |
|
| 73 |
+
|---|---|---|
|
| 74 |
+
| `title` | `str \| None` | Carried through for formatting |
|
| 75 |
+
| `detected_language` | `str \| None` | `"Arabic"` or `"English"` |
|
| 76 |
+
| `summary` | `str` | 3–5 sentence overview |
|
| 77 |
+
| `segments` | `list[SegmentSchema]` | 3–7 chronological sections |
|
| 78 |
+
| `suggested_category` | `str` | 1–2 word category, always English |
|
| 79 |
+
| `conclusion` | `str \| None` | Closing takeaway |
|
| 80 |
+
| `topics` | `list[str]` | Extracted topics (min 1) |
|
| 81 |
+
|
| 82 |
+
**`SegmentSchema`**: `title`, `summary`, `key_insight`, `why_it_matters`
|
| 83 |
+
|
| 84 |
+
## Configuration
|
| 85 |
+
|
| 86 |
+
- **`INFERENCE_MODE`** — `"groq"` to use the Groq Cloud API (Llama-3.3-70B-Versatile), anything else to use the local Qwen2.5-0.5B-Instruct path.
|
| 87 |
+
- **`chunk_size_words`** (`NoteGenerator.__init__`) — default `200`. Controls how many words go into each Map-step chunk.
|
| 88 |
+
- Requires a valid Groq API key/credentials when `INFERENCE_MODE = "groq"` (handled by `get_groq_client()` in `model_loader.py`).
|
| 89 |
+
- `langdetect` is optional — if not installed, language detection falls back to an Arabic-character-ratio heuristic.
|
| 90 |
+
|
| 91 |
+
## Limitations
|
| 92 |
+
|
| 93 |
+
- No quantitative benchmark (ROUGE/BLEU) exists yet across the API / mT5 / Qwen phases — decisions so far were based on manual review.
|
| 94 |
+
- The local Qwen2.5-0.5B-Instruct model is small by design (CPU-friendly), so it's less capable than the cloud Llama-3.3-70B path on long or technical transcripts.
|
| 95 |
+
- Fixed word-count chunking doesn't guarantee chunks align with real topic boundaries.
|
| 96 |
+
- `langdetect` can misclassify very short transcripts.
|
| 97 |
+
|
| 98 |
+
## Possible next steps
|
| 99 |
+
|
| 100 |
+
- Add a quantitative evaluation pipeline (ROUGE-1/2/L or a small human-rated set) to formally compare phases and catch regressions.
|
| 101 |
+
- Try light fine-tuning of the local Qwen model on Arabic/English video-summary pairs.
|
| 102 |
+
- Move from fixed-size chunking to a topic/semantic-aware segmentation strategy.
|