from pydantic import BaseModel, Field
from typing import Optional
# ── Common ─────────────────────────────────────────────────────────────────
class PaginationMeta(BaseModel):
total: int
page: int
per_page: int
pages: int
model_config = {
"json_schema_extra": {
"examples": [{"total": 6986, "page": 1, "per_page": 20, "pages": 350}]
}
}
class PaginatedResponse(BaseModel):
meta: PaginationMeta
data: list
# ── Hadith ─────────────────────────────────────────────────────────────────
class HadithSummary(BaseModel):
id: str = Field(description="Unique hadith UUID")
collection: str = Field(description="Collection name in English")
hadith_number: int = Field(description="Hadith number within collection")
grade: Optional[str] = Field(None, description="Grading: Sahih, Hasan, Da'if, etc.")
arabic_text: Optional[str] = Field(None, description="Full Arabic text (may be truncated in list views)")
matn_text: Optional[str] = Field(None, description="Body text only (without isnad)")
sanad_text: Optional[str] = Field(None, description="Chain of narration text only")
model_config = {
"json_schema_extra": {
"examples": [{
"id": "dcf8df41-3185-4e20-a9af-db3696a48c79",
"collection": "Sahih Bukhari",
"hadith_number": 1,
"grade": "Sahih",
"arabic_text": "حَدَّثَنَا الْحُمَيْدِيُّ عَبْدُ اللَّهِ بْنُ الزُّبَيْرِ قَالَ حَدَّثَنَا سُفْيَانُ...",
"matn_text": "إِنَّمَا الأَعْمَالُ بِالنِّيَّاتِ وَإِنَّمَا لِكُلِّ امْرِئٍ مَا نَوَى...",
"sanad_text": "حَدَّثَنَا الْحُمَيْدِيُّ عَبْدُ اللَّهِ بْنُ الزُّبَيْرِ قَالَ حَدَّثَنَا سُفْيَانُ قَالَ حَدَّثَنَا يَحْيَى بْنُ سَعِيدٍ الأَنْصَارِيُّ"
}]
}
}
class TopicTag(BaseModel):
topic_arabic: str = Field(description="Topic name in Arabic, e.g. الصلاة")
topic_english: str = Field(description="Topic name in English, e.g. Prayer")
category: str = Field(description="Broad Islamic category: عقيدة، فقه، سيرة، أخلاق، تفسير")
model_config = {
"json_schema_extra": {
"examples": [{
"topic_arabic": "النية",
"topic_english": "Intention",
"category": "فقه"
}]
}
}
class NarratorInChain(BaseModel):
order: int = Field(description="Position in chain: 1=closest to compiler, last=closest to Prophet ﷺ")
name_arabic: str = Field(description="Narrator's Arabic name as it appears in the hadith text")
name_transliterated: Optional[str] = Field(None, description="Latin transliteration of the name")
entity_type: Optional[str] = Field(None, description="PERSON, KUNYA (أبو/أم), NISBA (attributional), or TITLE (رسول الله)")
transmission_verb: Optional[str] = Field(None, description="Exact Arabic transmission verb: حدثنا، أخبرنا، عن، سمعت")
model_config = {
"json_schema_extra": {
"examples": [{
"order": 1,
"name_arabic": "الْحُمَيْدِيُّ",
"name_transliterated": "al-Humaydi",
"entity_type": "NISBA",
"transmission_verb": "حَدَّثَنَا"
}]
}
}
class HadithDetail(BaseModel):
id: str = Field(description="Unique hadith UUID")
collection: str = Field(description="Collection English name")
hadith_number: int = Field(description="Number within collection")
grade: Optional[str] = Field(None, description="Hadith grade")
arabic_text: Optional[str] = Field(None, description="Complete Arabic text")
sanad_text: Optional[str] = Field(None, description="Isnad (chain) text only")
matn_text: Optional[str] = Field(None, description="Matn (body) text only")
narrator_chain: list[NarratorInChain] = Field(default_factory=list, description="Ordered narrator chain from Neo4j graph")
topics: list[TopicTag] = Field(default_factory=list, description="Topic tags for searchability")
model_config = {
"json_schema_extra": {
"examples": [{
"id": "dcf8df41-3185-4e20-a9af-db3696a48c79",
"collection": "Sahih Bukhari",
"hadith_number": 1,
"grade": "Sahih",
"arabic_text": "حَدَّثَنَا الْحُمَيْدِيُّ عَبْدُ اللَّهِ بْنُ الزُّبَيْرِ...",
"sanad_text": "حَدَّثَنَا الْحُمَيْدِيُّ...",
"matn_text": "إِنَّمَا الأَعْمَالُ بِالنِّيَّاتِ...",
"narrator_chain": [
{"order": 1, "name_arabic": "الْحُمَيْدِيُّ", "name_transliterated": "al-Humaydi", "entity_type": "NISBA", "transmission_verb": "حَدَّثَنَا"},
{"order": 2, "name_arabic": "سُفْيَانُ", "name_transliterated": "Sufyan", "entity_type": "PERSON", "transmission_verb": "حَدَّثَنَا"},
],
"topics": [
{"topic_arabic": "النية", "topic_english": "Intention", "category": "فقه"},
]
}]
}
}
# ── Narrator ───────────────────────────────────────────────────────────────
class NarratorSummary(BaseModel):
name_arabic: str = Field(description="Primary Arabic name")
name_transliterated: Optional[str] = Field(None, description="Latin transliteration")
entity_type: Optional[str] = Field(None, description="PERSON, KUNYA, NISBA, TITLE")
generation: Optional[str] = Field(None, description="طبقة: صحابي، تابعي، تابع التابعين")
reliability_grade: Optional[str] = Field(None, description="جرح وتعديل: ثقة، صدوق، ضعيف، متروك")
hadith_count: int = Field(0, description="Number of hadiths this narrator appears in")
model_config = {
"json_schema_extra": {
"examples": [{
"name_arabic": "أَبُو هُرَيْرَةَ",
"name_transliterated": "Abu Hurayrah",
"entity_type": "KUNYA",
"generation": "صحابي",
"reliability_grade": "ثقة",
"hadith_count": 5374
}]
}
}
class NameForm(BaseModel):
name: str = Field(description="Alternative name form")
type: str = Field(description="Name type: PERSON, KUNYA, NISBA, TITLE")
class FamilyInfo(BaseModel):
father: Optional[str] = None
mother: Optional[str] = None
spouse: Optional[str] = None
children: list[str] = Field(default_factory=list)
class PlaceRelation(BaseModel):
place: str = Field(description="Place name in Arabic")
relation: str = Field(description="BORN_IN, LIVED_IN, DIED_IN, or TRAVELED_TO")
model_config = {
"json_schema_extra": {
"examples": [{"place": "المدينة", "relation": "LIVED_IN"}]
}
}
class NarratorProfile(BaseModel):
name_arabic: str = Field(description="Primary Arabic name")
name_transliterated: Optional[str] = Field(None, description="Latin transliteration")
entity_type: Optional[str] = Field(None, description="PERSON, KUNYA, NISBA, TITLE")
full_nasab: Optional[str] = Field(None, description="Full lineage: فلان بن فلان بن فلان")
kunya: Optional[str] = Field(None, description="أبو/أم name (e.g. أبو هريرة)")
nisba: Optional[str] = Field(None, description="Attributional name (e.g. البخاري، المدني، الزهري)")
laqab: Optional[str] = Field(None, description="Title or epithet (e.g. أمير المؤمنين في الحديث)")
generation: Optional[str] = Field(None, description="طبقة: صحابي، تابعي، تابع التابعين، أتباع تابع التابعين")
reliability_grade: Optional[str] = Field(None, description="جرح وتعديل: ثقة، ثقة حافظ، صدوق، ضعيف، متروك")
reliability_detail: Optional[str] = Field(None, description="Extended grading explanation from scholars")
birth_year_hijri: Optional[int] = Field(None, description="Birth year (Hijri calendar)")
death_year_hijri: Optional[int] = Field(None, description="Death year (Hijri calendar)")
birth_year_ce: Optional[int] = Field(None, description="Birth year (CE)")
death_year_ce: Optional[int] = Field(None, description="Death year (CE)")
biography_summary_arabic: Optional[str] = Field(None, description="2-3 sentence biography in Arabic")
biography_summary_english: Optional[str] = Field(None, description="2-3 sentence biography in English")
total_hadiths_narrated_approx: Optional[int] = Field(None, description="Approximate total hadiths narrated across all collections")
hadith_count: int = Field(0, description="Hadiths in current database")
hadiths: list[HadithSummary] = Field(default_factory=list, description="Sample hadiths narrated (max 50)")
teachers: list[NarratorSummary] = Field(default_factory=list, description="Known teachers / شيوخ")
students: list[NarratorSummary] = Field(default_factory=list, description="Known students / تلاميذ")
places: list[PlaceRelation] = Field(default_factory=list, description="Associated places (born, lived, died, traveled)")
tribes: list[str] = Field(default_factory=list, description="Tribal affiliations (e.g. قريش، دوس، الأنصار)")
bio_verified: bool = Field(False, description="Whether biography has been manually verified against classical sources")
model_config = {
"json_schema_extra": {
"examples": [{
"name_arabic": "أَبُو هُرَيْرَةَ",
"name_transliterated": "Abu Hurayrah",
"entity_type": "KUNYA",
"full_nasab": "عبد الرحمن بن صخر الدوسي",
"kunya": "أبو هريرة",
"nisba": "الدوسي",
"laqab": None,
"generation": "صحابي",
"reliability_grade": "ثقة",
"reliability_detail": "صحابي جليل، أكثر الصحابة رواية للحديث",
"birth_year_hijri": None,
"death_year_hijri": 57,
"birth_year_ce": None,
"death_year_ce": 676,
"biography_summary_arabic": "أبو هريرة الدوسي، صحابي جليل، أكثر الصحابة رواية للحديث النبوي. أسلم عام خيبر ولازم النبي ﷺ.",
"biography_summary_english": "Abu Hurayrah al-Dawsi, a prominent Companion and the most prolific narrator of hadith. He accepted Islam during Khaybar and remained close to the Prophet ﷺ.",
"total_hadiths_narrated_approx": 5374,
"hadith_count": 142,
"hadiths": [],
"teachers": [{"name_arabic": "رسول الله ﷺ", "name_transliterated": "Prophet Muhammad", "entity_type": "TITLE", "generation": None, "reliability_grade": None, "hadith_count": 0}],
"students": [{"name_arabic": "الزهري", "name_transliterated": "al-Zuhri", "entity_type": "NISBA", "generation": "تابعي", "reliability_grade": "ثقة", "hadith_count": 89}],
"places": [{"place": "المدينة", "relation": "LIVED_IN"}],
"tribes": ["دوس"],
"bio_verified": False,
}]
}
}
# ── Isnad Chain ────────────────────────────────────────────────────────────
class IsnadNode(BaseModel):
name_arabic: str = Field(description="Narrator Arabic name")
name_transliterated: Optional[str] = Field(None, description="Latin transliteration")
entity_type: Optional[str] = Field(None, description="PERSON, KUNYA, NISBA, TITLE")
generation: Optional[str] = Field(None, description="طبقة")
reliability_grade: Optional[str] = Field(None, description="جرح وتعديل grade")
class IsnadLink(BaseModel):
source: str = Field(description="name_arabic of narrator who received the hadith")
target: str = Field(description="name_arabic of narrator they received it from")
transmission_verb: Optional[str] = Field(None, description="Exact verb: حدثنا، أخبرنا، عن، سمعت، أنبأنا")
class IsnadChain(BaseModel):
hadith_id: str = Field(description="UUID of the hadith")
collection: str = Field(description="Collection name")
hadith_number: int = Field(description="Hadith number")
nodes: list[IsnadNode] = Field(default_factory=list, description="Narrator nodes for graph visualization")
links: list[IsnadLink] = Field(default_factory=list, description="Directed edges: source heard from target")
model_config = {
"json_schema_extra": {
"examples": [{
"hadith_id": "dcf8df41-3185-4e20-a9af-db3696a48c79",
"collection": "Sahih Bukhari",
"hadith_number": 1,
"nodes": [
{"name_arabic": "الْحُمَيْدِيُّ", "name_transliterated": "al-Humaydi", "entity_type": "NISBA", "generation": "تابع التابعين", "reliability_grade": "ثقة"},
{"name_arabic": "سُفْيَانُ بْنُ عُيَيْنَةَ", "name_transliterated": "Sufyan ibn Uyaynah", "entity_type": "PERSON", "generation": "تابع التابعين", "reliability_grade": "ثقة"},
{"name_arabic": "يَحْيَى بْنُ سَعِيدٍ", "name_transliterated": "Yahya ibn Sa'id al-Ansari", "entity_type": "PERSON", "generation": "تابعي", "reliability_grade": "ثقة"},
{"name_arabic": "عُمَرُ بْنُ الْخَطَّابِ", "name_transliterated": "Umar ibn al-Khattab", "entity_type": "PERSON", "generation": "صحابي", "reliability_grade": "ثقة"},
],
"links": [
{"source": "الْحُمَيْدِيُّ", "target": "سُفْيَانُ بْنُ عُيَيْنَةَ", "transmission_verb": "حَدَّثَنَا"},
{"source": "سُفْيَانُ بْنُ عُيَيْنَةَ", "target": "يَحْيَى بْنُ سَعِيدٍ", "transmission_verb": "حَدَّثَنَا"},
{"source": "يَحْيَى بْنُ سَعِيدٍ", "target": "عُمَرُ بْنُ الْخَطَّابِ", "transmission_verb": "عن"},
]
}]
}
}
# ── Relationships / Who Met Who ────────────────────────────────────────────
class NarratorInteraction(BaseModel):
narrator_a: str = Field(description="First narrator Arabic name")
narrator_a_transliterated: Optional[str] = Field(None, description="First narrator transliteration")
narrator_b: str = Field(description="Second narrator Arabic name")
narrator_b_transliterated: Optional[str] = Field(None, description="Second narrator transliteration")
relationship_type: str = Field(description="NARRATED_FROM, TEACHER_OF, HEARD_BY, STUDENT_OF")
shared_hadith_count: int = Field(0, description="Number of hadiths connecting them")
hadith_ids: list[str] = Field(default_factory=list, description="IDs of connecting hadiths (max 20)")
model_config = {
"json_schema_extra": {
"examples": [{
"narrator_a": "الزهري",
"narrator_a_transliterated": "al-Zuhri",
"narrator_b": "أنس بن مالك",
"narrator_b_transliterated": "Anas ibn Malik",
"relationship_type": "NARRATED_FROM",
"shared_hadith_count": 23,
"hadith_ids": ["abc-123", "def-456"]
}]
}
}
class NarratorConnection(BaseModel):
narrator: str = Field(description="Connected narrator Arabic name")
narrator_transliterated: Optional[str] = Field(None, description="Transliteration")
connection_type: str = Field(description="Relationship type")
direction: str = Field(description="'incoming' (they → this) or 'outgoing' (this → them)")
class NarratorNetwork(BaseModel):
center: NarratorSummary
connections: list[NarratorConnection] = Field(default_factory=list)
total_connections: int = 0
# ── Search ─────────────────────────────────────────────────────────────────
class SemanticSearchResult(BaseModel):
hadith: HadithSummary = Field(description="Matching hadith")
score: float = Field(description="Cosine similarity score (0-1, higher = more relevant)")
collection: Optional[str] = Field(None, description="Collection name")
model_config = {
"json_schema_extra": {
"examples": [{
"hadith": {
"id": "abc-123",
"collection": "Sahih Bukhari",
"hadith_number": 1,
"grade": "Sahih",
"arabic_text": "إِنَّمَا الأَعْمَالُ بِالنِّيَّاتِ..."
},
"score": 0.9234,
"collection": "Sahih Bukhari"
}]
}
}
class FullTextSearchResult(BaseModel):
hadith: HadithSummary = Field(description="Matching hadith")
score: float = Field(description="Elasticsearch relevance score (higher = more relevant)")
highlights: list[str] = Field(default_factory=list, description="Text fragments with highlighted matches")
model_config = {
"json_schema_extra": {
"examples": [{
"hadith": {
"id": "abc-123",
"collection": "Sahih Muslim",
"hadith_number": 1599,
"grade": "Sahih",
"arabic_text": "..."
},
"score": 12.45,
"highlights": ["...عن الصلاة في المسجد الحرام..."]
}]
}
}