keep ingested chunks findable and structured
Two bugs made self-ingested content useless, both silently: - _payload wrote `chapter_number` (int) while math retrieval filters on `chapter` (the full "فصل ۲: کسر" string), so anything we ingest could never match a search — no error, just permanently invisible content. - ingest_pdf flattened the vision extraction into one text blob and threw away type/keywords/generalSolution/problem/solution. LessonChunk is the source of truth, so rebuilding meant paying for vision again — and the problem/solution pairs are the input for building lesson questions. LessonChunk now stores those fields and _payload emits them under the same keys the other team's collections use, so ours and theirs stay one shape. Also fixes a regression from the whitenoise commit: the Manifest storage variant raises when staticfiles.json is absent, which broke all 23 admin rendering tests (Django forces DEBUG=False in tests, so USE_FINDERS is off) and would have taken the admin panel down entirely the day DEBUG=0 ships without a collectstatic run. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
78
rag/tests.py
78
rag/tests.py
@@ -160,6 +160,84 @@ class IngestTextTests(TestCase):
|
||||
self.assertIn(resp.status_code, (401, 403))
|
||||
|
||||
|
||||
class IngestPayloadContractTests(TestCase):
|
||||
"""
|
||||
قراردادِ بینِ چیزی که ingest مینویسه و چیزی که retrieval دنبالش میگرده.
|
||||
|
||||
این تستها عمداً ریزبینانهان چون شکستنِ این قرارداد هیچ خطایی تولید نمیکنه:
|
||||
نقطهها سالم در Qdrant میشینن، ولی فیلترِ جستجو هیچوقت بهشون نمیخوره و
|
||||
محتوا بیصدا نامرئی میمونه.
|
||||
"""
|
||||
|
||||
def test_math_payload_key_matches_the_key_retrieval_filters_on(self):
|
||||
from rag.services.ingest import _payload
|
||||
from rag.services.known_collections import filter_key_for_subject
|
||||
|
||||
chunk = LessonChunk(
|
||||
lesson_id=1, grade_id=1, subject='ریاضی', lesson_number=2, chapter=2,
|
||||
chapter_title='فصل ۲: کسر', order=1, title='کسر',
|
||||
original_text='x', summary='x', collection='ریاضی ششم',
|
||||
)
|
||||
payload = _payload(chunk)
|
||||
|
||||
# کلیدی که ریاضی روش فیلتر میشه باید دقیقاً در payload باشه، با همون مقدار.
|
||||
self.assertEqual(filter_key_for_subject('ریاضی'), 'chapter')
|
||||
self.assertIn('chapter', payload)
|
||||
self.assertEqual(payload['chapter'], 'فصل ۲: کسر')
|
||||
|
||||
def test_text_subject_payload_keeps_lesson_number_filter(self):
|
||||
from rag.services.ingest import _payload
|
||||
from rag.services.known_collections import filter_key_for_subject
|
||||
|
||||
chunk = LessonChunk(
|
||||
lesson_id=1, grade_id=1, subject='علوم تجربی', lesson_number=3, chapter=None,
|
||||
order=1, title='آب', original_text='x', summary='x', collection='علوم تجربی سوم',
|
||||
)
|
||||
payload = _payload(chunk)
|
||||
|
||||
self.assertEqual(filter_key_for_subject('علوم تجربی'), 'lesson_number')
|
||||
self.assertEqual(payload['lesson_number'], 3)
|
||||
# فصل نداره، پس نباید کلیدِ خالیِ chapter بنویسه.
|
||||
self.assertNotIn('chapter', payload)
|
||||
|
||||
def test_structured_fields_survive_into_payload(self):
|
||||
"""
|
||||
استخراجِ vision گرونه؛ اگه ساختارش فقط به متنِ صاف تبدیل بشه، برای
|
||||
ساختِ سؤالهای مسیرِ درس باید دوباره همون هزینه داده بشه.
|
||||
"""
|
||||
from rag.services.ingest import _payload
|
||||
|
||||
chunk = LessonChunk(
|
||||
lesson_id=1, grade_id=1, subject='ریاضی', lesson_number=2, chapter=2,
|
||||
chapter_title='فصل ۲: کسر', grade_title='ششم دبستان',
|
||||
problem_type='مقایسه کسرها', keywords=['کسر', 'مقایسه'],
|
||||
general_solution='مخرج مشترک بگیرید.',
|
||||
problem='کدام کسر بزرگتر است؟', solution='۳/۴ بزرگتر است.',
|
||||
order=1, title='کسر', original_text='x', summary='x', collection='ریاضی ششم',
|
||||
)
|
||||
payload = _payload(chunk)
|
||||
|
||||
self.assertEqual(payload['type'], 'مقایسه کسرها')
|
||||
self.assertEqual(payload['keywords'], ['کسر', 'مقایسه'])
|
||||
self.assertEqual(payload['generalSolution'], 'مخرج مشترک بگیرید.')
|
||||
self.assertEqual(payload['problem'], 'کدام کسر بزرگتر است؟')
|
||||
self.assertEqual(payload['solution'], '۳/۴ بزرگتر است.')
|
||||
self.assertEqual(payload['grade'], 'ششم دبستان')
|
||||
|
||||
def test_payload_text_reads_what_ingest_writes(self):
|
||||
"""پلِ نهایی: متنی که retrieval از payload درمیآره نباید خالی باشه."""
|
||||
from rag.services.ingest import _payload
|
||||
from rag.services.retrieval import _payload_text
|
||||
|
||||
chunk = LessonChunk(
|
||||
lesson_id=1, grade_id=1, subject='ریاضی', lesson_number=2, chapter=2,
|
||||
chapter_title='فصل ۲: کسر', general_solution='مخرج مشترک بگیرید.',
|
||||
order=1, title='کسر', original_text='متنِ اصلی', summary='خلاصهی تکه',
|
||||
collection='ریاضی ششم',
|
||||
)
|
||||
self.assertTrue(_payload_text(_payload(chunk)))
|
||||
|
||||
|
||||
class RagConfigTests(TestCase):
|
||||
def test_rag_disabled_without_keys(self):
|
||||
from rag.services.config import rag_is_configured
|
||||
|
||||
Reference in New Issue
Block a user