Hydra-Bolt
commited on
Commit
·
eef2a73
1
Parent(s):
0e65d5f
restructured
Browse files- Dockerfile +2 -1
- {tools → app}/__init__.py +0 -0
- services.py → app/agent/services.py +3 -10
- routes.py → app/api/routes.py +2 -2
- app/config/__init__.py +0 -0
- constants.py → app/config/constants.py +0 -0
- config.py → app/config/settings.py +0 -0
- app/db/__init__.py +0 -0
- models.py → app/db/models.py +0 -0
- app.py → app/main.py +2 -2
- app/tools/__init__.py +0 -0
- {tools → app/tools}/fetch.py +1 -42
- {tools → app/tools}/scrape_shamela.py +0 -16
- asd.ipynb +35 -269
- docs/README_Enhanced_Narrator_Agent.md +0 -190
- docs/README_ShamelaNarratorExtractor.md +0 -246
- test_api.py → tests/test_api.py +0 -0
- test_enhanced_narrator_agent.py → tests/test_enhanced_narrator_agent.py +1 -1
Dockerfile
CHANGED
|
@@ -25,4 +25,5 @@ RUN playwright install chromium
|
|
| 25 |
# Copy app code
|
| 26 |
COPY --chown=user . /app
|
| 27 |
|
| 28 |
-
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
|
|
|
|
|
|
| 25 |
# Copy app code
|
| 26 |
COPY --chown=user . /app
|
| 27 |
|
| 28 |
+
CMD ["uvicorn", "app/main:app", "--host", "0.0.0.0", "--port", "7860"]
|
| 29 |
+
|
{tools → app}/__init__.py
RENAMED
|
File without changes
|
services.py → app/agent/services.py
RENAMED
|
@@ -7,10 +7,10 @@ from langchain.output_parsers import PydanticOutputParser
|
|
| 7 |
from langchain_core.prompts import PromptTemplate
|
| 8 |
from dotenv import load_dotenv
|
| 9 |
|
| 10 |
-
from models import NarratorExtractionResponse, NarratorAnalysisResponse
|
| 11 |
-
from tools.scrape_shamela import ShamelaNarratorExtractor
|
| 12 |
|
| 13 |
-
from constants import EXTRACT_PROMPT, ANALYZE_PROMPT, SYNTHESIS_PROMPT
|
| 14 |
import asyncio
|
| 15 |
|
| 16 |
|
|
@@ -223,13 +223,6 @@ class LLMService:
|
|
| 223 |
"success": True,
|
| 224 |
}
|
| 225 |
|
| 226 |
-
return {
|
| 227 |
-
"overall_assessment": synthesis_result.content,
|
| 228 |
-
"individual_results": chain_results,
|
| 229 |
-
"chain_length": len(chain_results),
|
| 230 |
-
"success": True,
|
| 231 |
-
}
|
| 232 |
-
|
| 233 |
except Exception as e:
|
| 234 |
return {
|
| 235 |
"overall_assessment": f"Synthesis failed: {str(e)}",
|
|
|
|
| 7 |
from langchain_core.prompts import PromptTemplate
|
| 8 |
from dotenv import load_dotenv
|
| 9 |
|
| 10 |
+
from app.db.models import NarratorExtractionResponse, NarratorAnalysisResponse
|
| 11 |
+
from app.tools.scrape_shamela import ShamelaNarratorExtractor
|
| 12 |
|
| 13 |
+
from app.config.constants import EXTRACT_PROMPT, ANALYZE_PROMPT, SYNTHESIS_PROMPT
|
| 14 |
import asyncio
|
| 15 |
|
| 16 |
|
|
|
|
| 223 |
"success": True,
|
| 224 |
}
|
| 225 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 226 |
except Exception as e:
|
| 227 |
return {
|
| 228 |
"overall_assessment": f"Synthesis failed: {str(e)}",
|
routes.py → app/api/routes.py
RENAMED
|
@@ -2,7 +2,7 @@ from fastapi import APIRouter, HTTPException, status
|
|
| 2 |
from fastapi.responses import JSONResponse
|
| 3 |
from typing import List, Dict, Any
|
| 4 |
|
| 5 |
-
from models import (
|
| 6 |
HadithTextRequest,
|
| 7 |
NarratorExtractionResponse,
|
| 8 |
NarratorAnalysisRequest,
|
|
@@ -15,7 +15,7 @@ from models import (
|
|
| 15 |
ChainAnalysisResult,
|
| 16 |
ExtractAndAnalyzeMetadata,
|
| 17 |
)
|
| 18 |
-
from services import get_llm_service
|
| 19 |
|
| 20 |
router = APIRouter(prefix="/api/v1", tags=["hadith-analysis"])
|
| 21 |
|
|
|
|
| 2 |
from fastapi.responses import JSONResponse
|
| 3 |
from typing import List, Dict, Any
|
| 4 |
|
| 5 |
+
from app.db.models import (
|
| 6 |
HadithTextRequest,
|
| 7 |
NarratorExtractionResponse,
|
| 8 |
NarratorAnalysisRequest,
|
|
|
|
| 15 |
ChainAnalysisResult,
|
| 16 |
ExtractAndAnalyzeMetadata,
|
| 17 |
)
|
| 18 |
+
from app.agent.services import get_llm_service
|
| 19 |
|
| 20 |
router = APIRouter(prefix="/api/v1", tags=["hadith-analysis"])
|
| 21 |
|
app/config/__init__.py
ADDED
|
File without changes
|
constants.py → app/config/constants.py
RENAMED
|
File without changes
|
config.py → app/config/settings.py
RENAMED
|
File without changes
|
app/db/__init__.py
ADDED
|
File without changes
|
models.py → app/db/models.py
RENAMED
|
File without changes
|
app.py → app/main.py
RENAMED
|
@@ -4,8 +4,8 @@ from fastapi.responses import JSONResponse
|
|
| 4 |
import uvicorn
|
| 5 |
import os
|
| 6 |
|
| 7 |
-
from config import settings
|
| 8 |
-
from routes import router
|
| 9 |
|
| 10 |
|
| 11 |
# Create FastAPI application
|
|
|
|
| 4 |
import uvicorn
|
| 5 |
import os
|
| 6 |
|
| 7 |
+
from app.config.settings import settings
|
| 8 |
+
from app.api.routes import router
|
| 9 |
|
| 10 |
|
| 11 |
# Create FastAPI application
|
app/tools/__init__.py
ADDED
|
File without changes
|
{tools → app/tools}/fetch.py
RENAMED
|
@@ -289,45 +289,4 @@ async def fetch_html_with_browser(url, browser_type='chromium', max_retries=5, m
|
|
| 289 |
await asyncio.sleep(wait_time)
|
| 290 |
|
| 291 |
print(f"[FAIL] Could not fetch {url} with {browser_type} after {max_retries} attempts.")
|
| 292 |
-
return None
|
| 293 |
-
|
| 294 |
-
# Example usage
|
| 295 |
-
async def main():
|
| 296 |
-
"""Example usage with async functions."""
|
| 297 |
-
url = "https://shamela.ws/narrator/5710"
|
| 298 |
-
|
| 299 |
-
print("=" * 60)
|
| 300 |
-
print("Testing basic fetch_html function...")
|
| 301 |
-
print("=" * 60)
|
| 302 |
-
|
| 303 |
-
# Test basic function
|
| 304 |
-
html = await fetch_html(url, headless=True) # Set headless=False to see the browser
|
| 305 |
-
if html:
|
| 306 |
-
with open("debug_shamela_basic.html", "w", encoding="utf-8") as f:
|
| 307 |
-
f.write(html)
|
| 308 |
-
print(f"[SUCCESS] Basic HTML saved to debug_shamela_basic.html ({len(html)} chars)")
|
| 309 |
-
else:
|
| 310 |
-
print("[FAILED] Could not fetch HTML with basic function")
|
| 311 |
-
|
| 312 |
-
print("\n" + "=" * 60)
|
| 313 |
-
print("Testing enhanced fetch_html_with_js_execution function...")
|
| 314 |
-
print("=" * 60)
|
| 315 |
-
|
| 316 |
-
# Test enhanced function
|
| 317 |
-
html_js = await fetch_html_with_js_execution(url, headless=True, wait_for_js=True)
|
| 318 |
-
if html_js:
|
| 319 |
-
with open("debug_shamela_js.html", "w", encoding="utf-8") as f:
|
| 320 |
-
f.write(html_js)
|
| 321 |
-
print(f"[SUCCESS] Enhanced HTML saved to debug_shamela_js.html ({len(html_js)} chars)")
|
| 322 |
-
else:
|
| 323 |
-
print("[FAILED] Could not fetch HTML with enhanced function")
|
| 324 |
-
|
| 325 |
-
# Compare results
|
| 326 |
-
if html and html_js:
|
| 327 |
-
print(f"\n[INFO] Basic HTML length: {len(html)} chars")
|
| 328 |
-
print(f"[INFO] Enhanced HTML length: {len(html_js)} chars")
|
| 329 |
-
print(f"[INFO] Difference: {abs(len(html_js) - len(html))} chars")
|
| 330 |
-
|
| 331 |
-
|
| 332 |
-
if __name__ == "__main__":
|
| 333 |
-
asyncio.run(main())
|
|
|
|
| 289 |
await asyncio.sleep(wait_time)
|
| 290 |
|
| 291 |
print(f"[FAIL] Could not fetch {url} with {browser_type} after {max_retries} attempts.")
|
| 292 |
+
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{tools → app/tools}/scrape_shamela.py
RENAMED
|
@@ -314,19 +314,3 @@ class ShamelaNarratorExtractor:
|
|
| 314 |
print(f"Total scholars: {info['extraction_metadata']['total_scholars']}")
|
| 315 |
print(f"Biographical fields: {info['extraction_metadata']['biographical_fields']}")
|
| 316 |
print(f"Total comments: {info['extraction_metadata']['total_comments']}")
|
| 317 |
-
|
| 318 |
-
|
| 319 |
-
# Example usage
|
| 320 |
-
# Example usage
|
| 321 |
-
async def main():
|
| 322 |
-
"""Example usage with async functions."""
|
| 323 |
-
narrator_name = "أَبُو بَكْرِ بْنُ أَبِي شَيْبَةَ"
|
| 324 |
-
|
| 325 |
-
# Extract narrator information using the class
|
| 326 |
-
info = await ShamelaNarratorExtractor.extract_narrator_by_name(narrator_name)
|
| 327 |
-
|
| 328 |
-
# Print the results
|
| 329 |
-
ShamelaNarratorExtractor.print_narrator_info(info)
|
| 330 |
-
|
| 331 |
-
if __name__ == "__main__":
|
| 332 |
-
asyncio.run(main())
|
|
|
|
| 314 |
print(f"Total scholars: {info['extraction_metadata']['total_scholars']}")
|
| 315 |
print(f"Biographical fields: {info['extraction_metadata']['biographical_fields']}")
|
| 316 |
print(f"Total comments: {info['extraction_metadata']['total_comments']}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
asd.ipynb
CHANGED
|
@@ -2,298 +2,64 @@
|
|
| 2 |
"cells": [
|
| 3 |
{
|
| 4 |
"cell_type": "code",
|
| 5 |
-
"execution_count":
|
| 6 |
-
"id": "
|
| 7 |
-
"metadata": {},
|
| 8 |
-
"outputs": [
|
| 9 |
-
{
|
| 10 |
-
"name": "stdout",
|
| 11 |
-
"output_type": "stream",
|
| 12 |
-
"text": [
|
| 13 |
-
"Searching for: \" ابن كثير\" site:shamela.ws/narrator\n",
|
| 14 |
-
"Extracted works for narrator: ابن كثير\n",
|
| 15 |
-
"NarratorInfo(narrator_name=None, biographical_info={}, scholarly_critique=[], extraction_metadata={'total_scholars': 0, 'biographical_fields': 0, 'has_critique_section': False, 'total_comments': 0})\n"
|
| 16 |
-
]
|
| 17 |
-
}
|
| 18 |
-
],
|
| 19 |
-
"source": [
|
| 20 |
-
"from tools.scrape_shamela import ShamelaNarratorExtractor\n",
|
| 21 |
-
"\n",
|
| 22 |
-
"narrator_name = \" ابن كثير\"\n",
|
| 23 |
-
"if __name__ == \"__main__\":\n",
|
| 24 |
-
" extractor = ShamelaNarratorExtractor()\n",
|
| 25 |
-
" details = extractor.search_and_extract(narrator_name)\n",
|
| 26 |
-
" print(f\"Extracted works for narrator: {narrator_name}\")\n",
|
| 27 |
-
" print(details)\n"
|
| 28 |
-
]
|
| 29 |
-
},
|
| 30 |
-
{
|
| 31 |
-
"cell_type": "code",
|
| 32 |
-
"execution_count": null,
|
| 33 |
-
"id": "e2e25f79",
|
| 34 |
"metadata": {},
|
| 35 |
"outputs": [],
|
| 36 |
"source": [
|
| 37 |
-
"import requests\n",
|
| 38 |
"from bs4 import BeautifulSoup\n",
|
| 39 |
-
"from
|
| 40 |
-
"
|
| 41 |
-
"
|
| 42 |
-
"
|
| 43 |
-
"
|
| 44 |
-
"
|
| 45 |
-
" \n",
|
| 46 |
-
"
|
| 47 |
-
"
|
| 48 |
-
"
|
| 49 |
-
" Returns:\n",
|
| 50 |
-
" Dictionary containing extracted narrator information\n",
|
| 51 |
-
" \"\"\"\n",
|
| 52 |
-
" soup = BeautifulSoup(html_content, \"html.parser\")\n",
|
| 53 |
-
" result = {}\n",
|
| 54 |
-
"\n",
|
| 55 |
-
" # 1. Extract Narrator Name (from <h1>)\n",
|
| 56 |
-
" name_tag = soup.find(\"h1\")\n",
|
| 57 |
-
" result[\"narrator_name\"] = name_tag.text.strip() if name_tag else None\n",
|
| 58 |
-
"\n",
|
| 59 |
-
" # 2. Extract Biographical Information (before الجرح والتعديل section)\n",
|
| 60 |
-
" bio_info = {}\n",
|
| 61 |
-
" container = soup.find(\"div\", class_=\"col-md-12\")\n",
|
| 62 |
-
" \n",
|
| 63 |
-
" # Keywords that indicate scholarly critique content (not biographical)\n",
|
| 64 |
-
" critique_keywords = [\n",
|
| 65 |
-
" \"ذكره\", \"قال\", \"وقال\", \"ثقة\", \"ضعيف\", \"صدوق\", \"كذاب\", \"متروك\",\n",
|
| 66 |
-
" \"الثقات\", \"الضعفاء\", \"تهذيب\", \"الكاشف\", \"الميزان\", \"العجلي\",\n",
|
| 67 |
-
" \"النسائي\", \"ابن سعد\", \"أحمد\", \"يعقوب\", \"ابن حبان\"\n",
|
| 68 |
-
" ]\n",
|
| 69 |
-
" \n",
|
| 70 |
-
" if container:\n",
|
| 71 |
-
" # Find the الجرح والتعديل header first\n",
|
| 72 |
-
" critique_header = None\n",
|
| 73 |
-
" for h4 in container.find_all(\"h4\"):\n",
|
| 74 |
-
" if \"الجرح والتعديل\" in h4.text:\n",
|
| 75 |
-
" critique_header = h4\n",
|
| 76 |
-
" break\n",
|
| 77 |
-
" \n",
|
| 78 |
-
" # Process divs before the critique section\n",
|
| 79 |
-
" for div in container.find_all(\"div\", recursive=False):\n",
|
| 80 |
-
" # Stop when we reach the critique header\n",
|
| 81 |
-
" if critique_header and div == critique_header:\n",
|
| 82 |
-
" break\n",
|
| 83 |
-
" \n",
|
| 84 |
-
" b_tag = div.find(\"b\")\n",
|
| 85 |
-
" if b_tag:\n",
|
| 86 |
-
" key = b_tag.text.strip().rstrip(\":\").rstrip(\":\")\n",
|
| 87 |
-
" \n",
|
| 88 |
-
" # Skip if this looks like scholarly critique content\n",
|
| 89 |
-
" is_critique_content = any(keyword in key for keyword in critique_keywords)\n",
|
| 90 |
-
" if is_critique_content:\n",
|
| 91 |
-
" continue\n",
|
| 92 |
-
" \n",
|
| 93 |
-
" # Get the text after the <b> tag\n",
|
| 94 |
-
" remaining_text = \"\"\n",
|
| 95 |
-
" for sibling in b_tag.next_siblings:\n",
|
| 96 |
-
" if hasattr(sibling, 'strip'):\n",
|
| 97 |
-
" remaining_text += sibling.strip()\n",
|
| 98 |
-
" else:\n",
|
| 99 |
-
" remaining_text += str(sibling).strip()\n",
|
| 100 |
-
" \n",
|
| 101 |
-
" # Additional check: if the content contains critique keywords, skip it\n",
|
| 102 |
-
" if remaining_text:\n",
|
| 103 |
-
" content_has_critique = any(keyword in remaining_text for keyword in critique_keywords)\n",
|
| 104 |
-
" if not content_has_critique:\n",
|
| 105 |
-
" bio_info[key] = remaining_text.strip()\n",
|
| 106 |
-
"\n",
|
| 107 |
-
" result[\"biographical_info\"] = bio_info\n",
|
| 108 |
-
"\n",
|
| 109 |
-
" # 3. Extract Scholarly Critique (after الجرح والتعديل header)\n",
|
| 110 |
-
" scholarly_critique = []\n",
|
| 111 |
-
" \n",
|
| 112 |
-
" if container:\n",
|
| 113 |
-
" critique_header = None\n",
|
| 114 |
-
" for h4 in container.find_all(\"h4\"):\n",
|
| 115 |
-
" if \"الجرح والتعديل\" in h4.text:\n",
|
| 116 |
-
" critique_header = h4\n",
|
| 117 |
-
" break\n",
|
| 118 |
-
" \n",
|
| 119 |
-
" if critique_header:\n",
|
| 120 |
-
" current_scholar = None\n",
|
| 121 |
-
" current_comments = []\n",
|
| 122 |
-
" \n",
|
| 123 |
-
" # Process all siblings after the critique header\n",
|
| 124 |
-
" for element in critique_header.find_next_siblings():\n",
|
| 125 |
-
" # Scholar name (in alert-info div)\n",
|
| 126 |
-
" if (element.name == \"div\" and \n",
|
| 127 |
-
" \"alert-info\" in element.get(\"class\", [])):\n",
|
| 128 |
-
" \n",
|
| 129 |
-
" # Save previous scholar's data if exists\n",
|
| 130 |
-
" if current_scholar and current_comments:\n",
|
| 131 |
-
" scholarly_critique.append({\n",
|
| 132 |
-
" \"scholar\": current_scholar,\n",
|
| 133 |
-
" \"comments\": current_comments.copy()\n",
|
| 134 |
-
" })\n",
|
| 135 |
-
" \n",
|
| 136 |
-
" # Start new scholar\n",
|
| 137 |
-
" current_scholar = element.text.strip()\n",
|
| 138 |
-
" current_comments = []\n",
|
| 139 |
-
" \n",
|
| 140 |
-
" # Scholar comment (regular div)\n",
|
| 141 |
-
" elif (element.name == \"div\" and \n",
|
| 142 |
-
" not \"alert-info\" in element.get(\"class\", []) and\n",
|
| 143 |
-
" element.text.strip()):\n",
|
| 144 |
-
" \n",
|
| 145 |
-
" comment_text = element.text.strip()\n",
|
| 146 |
-
" \n",
|
| 147 |
-
" # If no current scholar, extract scholar name from the comment\n",
|
| 148 |
-
" if not current_scholar:\n",
|
| 149 |
-
" # Look for patterns like \"قال ابن حبان\" or \"وذكره العجلي\"\n",
|
| 150 |
-
" for word in comment_text.split():\n",
|
| 151 |
-
" if any(scholar_name in word for scholar_name in \n",
|
| 152 |
-
" [\"ابن حبان\", \"العجلي\", \"النسائي\", \"ابن سعد\", \"الذهبي\", \"ابن حجر\"]):\n",
|
| 153 |
-
" current_scholar = \"مختلف العلماء\" # Mixed scholars\n",
|
| 154 |
-
" break\n",
|
| 155 |
-
" \n",
|
| 156 |
-
" if not current_scholar:\n",
|
| 157 |
-
" current_scholar = \"غير محدد\" # Unspecified\n",
|
| 158 |
-
" \n",
|
| 159 |
-
" # Extract bold text if present\n",
|
| 160 |
-
" bold_tags = element.find_all(\"b\")\n",
|
| 161 |
-
" comment = {\n",
|
| 162 |
-
" \"text\": comment_text,\n",
|
| 163 |
-
" \"highlighted\": [b.text.strip() for b in bold_tags] if bold_tags else []\n",
|
| 164 |
-
" }\n",
|
| 165 |
-
" current_comments.append(comment)\n",
|
| 166 |
-
" \n",
|
| 167 |
-
" # Don't forget the last scholar\n",
|
| 168 |
-
" if current_scholar and current_comments:\n",
|
| 169 |
-
" scholarly_critique.append({\n",
|
| 170 |
-
" \"scholar\": current_scholar,\n",
|
| 171 |
-
" \"comments\": current_comments\n",
|
| 172 |
-
" })\n",
|
| 173 |
-
"\n",
|
| 174 |
-
" result[\"scholarly_critique\"] = scholarly_critique\n",
|
| 175 |
-
"\n",
|
| 176 |
-
" # 4. Extract metadata\n",
|
| 177 |
-
" result[\"extraction_metadata\"] = {\n",
|
| 178 |
-
" \"total_scholars\": len(scholarly_critique),\n",
|
| 179 |
-
" \"biographical_fields\": len(bio_info),\n",
|
| 180 |
-
" \"has_critique_section\": len(scholarly_critique) > 0,\n",
|
| 181 |
-
" \"total_comments\": sum(len(scholar[\"comments\"]) for scholar in scholarly_critique)\n",
|
| 182 |
-
" }\n",
|
| 183 |
-
"\n",
|
| 184 |
-
" return result\n",
|
| 185 |
-
"\n",
|
| 186 |
-
"def clean_biographical_info(bio_info: Dict[str, str]) -> Dict[str, str]:\n",
|
| 187 |
-
" \"\"\"\n",
|
| 188 |
-
" Additional cleaning function to remove any remaining critique content from bio info.\n",
|
| 189 |
-
" \"\"\"\n",
|
| 190 |
-
" cleaned_bio = {}\n",
|
| 191 |
-
" \n",
|
| 192 |
-
" # Define purely biographical field patterns\n",
|
| 193 |
-
" bio_field_patterns = [\n",
|
| 194 |
-
" \"الاسم\", \"الكنية\", \"النسب\", \"بلد\", \"تاريخ\", \"طبقة\", \"علاقات\"\n",
|
| 195 |
-
" ]\n",
|
| 196 |
-
" \n",
|
| 197 |
-
" for key, value in bio_info.items():\n",
|
| 198 |
-
" # Keep if it's clearly a biographical field\n",
|
| 199 |
-
" if any(pattern in key for pattern in bio_field_patterns):\n",
|
| 200 |
-
" cleaned_bio[key] = value\n",
|
| 201 |
-
" # Or if it doesn't contain critique-style language\n",
|
| 202 |
-
" elif not any(word in value for word in [\"قال\", \"ذكره\", \"ثقة\", \"ضعيف\"]):\n",
|
| 203 |
-
" cleaned_bio[key] = value\n",
|
| 204 |
-
" \n",
|
| 205 |
-
" return cleaned_bio\n",
|
| 206 |
-
"\n",
|
| 207 |
-
"# Updated main extraction function\n",
|
| 208 |
-
"def extract_narrator_info_enhanced(html_content: str) -> Dict[str, Any]:\n",
|
| 209 |
-
" \"\"\"Enhanced extraction with better cleaning.\"\"\"\n",
|
| 210 |
-
" result = extract_narrator_info(html_content)\n",
|
| 211 |
-
" \n",
|
| 212 |
-
" # Apply additional cleaning to biographical info\n",
|
| 213 |
-
" result[\"biographical_info\"] = clean_biographical_info(result[\"biographical_info\"])\n",
|
| 214 |
-
" \n",
|
| 215 |
-
" return result"
|
| 216 |
]
|
| 217 |
},
|
| 218 |
{
|
| 219 |
"cell_type": "code",
|
| 220 |
-
"execution_count":
|
| 221 |
-
"id": "
|
| 222 |
"metadata": {},
|
| 223 |
"outputs": [
|
| 224 |
{
|
| 225 |
"name": "stdout",
|
| 226 |
"output_type": "stream",
|
| 227 |
"text": [
|
| 228 |
-
"
|
| 229 |
-
|
| 230 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 231 |
]
|
| 232 |
}
|
| 233 |
],
|
| 234 |
"source": [
|
| 235 |
-
"
|
| 236 |
-
"
|
| 237 |
-
"if __name__ == \"__main__\":\n",
|
| 238 |
-
" # Use googlesearch to find the narrator page on shamela.ws\n",
|
| 239 |
-
" query = f'{narrator_name.strip()} site:shamela.ws/narrator'\n",
|
| 240 |
-
" search_results = list(search(query, num_results=5))\n",
|
| 241 |
-
"\n",
|
| 242 |
-
" print(f\"Searching for narrator: {narrator_name}\")\n",
|
| 243 |
-
" print(f\"Google search query: {query}\")\n",
|
| 244 |
-
" print(f\"Search results: {search_results}\")\n",
|
| 245 |
-
"\n",
|
| 246 |
-
" # if search_results:\n",
|
| 247 |
-
" # # Extract URL from SearchResult object\n",
|
| 248 |
-
" # url = search_results[0] if hasattr(search_results[0], 'url') else str(search_results[0])\n",
|
| 249 |
-
" # print(f\"Found narrator page: {url}\")\n",
|
| 250 |
-
" \n",
|
| 251 |
-
" # # Fetch the HTML content\n",
|
| 252 |
-
" # response = requests.get(url)\n",
|
| 253 |
-
" # if response.status_code == 200:\n",
|
| 254 |
-
" # html_content = response.text\n",
|
| 255 |
-
" # info = extract_narrator_info_enhanced(html_content)\n",
|
| 256 |
-
" # else:\n",
|
| 257 |
-
" # print(f\"Failed to fetch page: {response.status_code}\")\n",
|
| 258 |
-
" # info = {}\n",
|
| 259 |
-
"\n",
|
| 260 |
-
" # else:\n",
|
| 261 |
-
" # info = {\n",
|
| 262 |
-
" # \"narrator_name\": narrator_name,\n",
|
| 263 |
-
" # \"biographical_info\": {},\n",
|
| 264 |
-
" # \"scholarly_critique\": [],\n",
|
| 265 |
-
" # \"extraction_metadata\": {\n",
|
| 266 |
-
" # \"total_scholars\": 0,\n",
|
| 267 |
-
" # \"biographical_fields\": 0,\n",
|
| 268 |
-
" # \"has_critique_section\": True,\n",
|
| 269 |
-
" # \"total_comments\": 0\n",
|
| 270 |
-
" # }\n",
|
| 271 |
-
" # }\n",
|
| 272 |
-
"\n",
|
| 273 |
-
" # print(\"=== NARRATOR INFORMATION ===\")\n",
|
| 274 |
-
" # print(f\"Name: {info['narrator_name']}\")\n",
|
| 275 |
-
" # print(f\"\\n=== BIOGRAPHICAL INFO ===\")\n",
|
| 276 |
-
" # for key, value in info['biographical_info'].items():\n",
|
| 277 |
-
" # print(f\"{key}: {value}\")\n",
|
| 278 |
-
"\n",
|
| 279 |
-
" # print(f\"\\n=== SCHOLARLY CRITIQUE ===\")\n",
|
| 280 |
-
" # for scholar_data in info['scholarly_critique']:\n",
|
| 281 |
-
" # print(f\"\\nScholar: {scholar_data['scholar']}\")\n",
|
| 282 |
-
" # for comment in scholar_data['comments']:\n",
|
| 283 |
-
" # print(f\" - {comment['text']}\")\n",
|
| 284 |
-
" # if comment['highlighted']:\n",
|
| 285 |
-
" # print(f\" Highlighted: {comment['highlighted']}\")\n",
|
| 286 |
-
"\n",
|
| 287 |
-
" # print(f\"\\n=== METADATA ===\")\n",
|
| 288 |
-
" # print(f\"Total scholars: {info['extraction_metadata']['total_scholars']}\")\n",
|
| 289 |
-
" # print(f\"Biographical fields: {info['extraction_metadata']['biographical_fields']}\")\n",
|
| 290 |
-
" # print(f\"Total comments: {info['extraction_metadata']['total_comments']}\")\n"
|
| 291 |
]
|
| 292 |
},
|
| 293 |
{
|
| 294 |
"cell_type": "code",
|
| 295 |
"execution_count": null,
|
| 296 |
-
"id": "
|
| 297 |
"metadata": {},
|
| 298 |
"outputs": [],
|
| 299 |
"source": []
|
|
|
|
| 2 |
"cells": [
|
| 3 |
{
|
| 4 |
"cell_type": "code",
|
| 5 |
+
"execution_count": 6,
|
| 6 |
+
"id": "f41e65f6",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
"metadata": {},
|
| 8 |
"outputs": [],
|
| 9 |
"source": [
|
|
|
|
| 10 |
"from bs4 import BeautifulSoup\n",
|
| 11 |
+
"from tools.fetch import fetch_html\n",
|
| 12 |
+
"\n",
|
| 13 |
+
"async def scrape_siyar_alaam_nubala():\n",
|
| 14 |
+
" html = await fetch_html(\"https://www.islamweb.net/ar/library/content/60/531/%D8%B3%D8%B9%D9%8A%D8%AF-%D8%A8%D9%86-%D8%A7%D9%84%D9%85%D8%B3%D9%8A%D8%A8\")\n",
|
| 15 |
+
" if html is None:\n",
|
| 16 |
+
" return []\n",
|
| 17 |
+
" soup = BeautifulSoup(html, \"html.parser\")\n",
|
| 18 |
+
" divs = soup.find_all(\"div\", class_=\"bookcontent-dic\")\n",
|
| 19 |
+
" texts = [div.get_text(strip=True) for div in divs]\n",
|
| 20 |
+
" return texts\n"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
]
|
| 22 |
},
|
| 23 |
{
|
| 24 |
"cell_type": "code",
|
| 25 |
+
"execution_count": 7,
|
| 26 |
+
"id": "c50a7413",
|
| 27 |
"metadata": {},
|
| 28 |
"outputs": [
|
| 29 |
{
|
| 30 |
"name": "stdout",
|
| 31 |
"output_type": "stream",
|
| 32 |
"text": [
|
| 33 |
+
"[INFO] Fetching https://www.islamweb.net/ar/library/content/60/531/%D8%B3%D8%B9%D9%8A%D8%AF-%D8%A8%D9%86-%D8%A7%D9%84%D9%85%D8%B3%D9%8A%D8%A8 (Attempt 1/5)\n"
|
| 34 |
+
]
|
| 35 |
+
},
|
| 36 |
+
{
|
| 37 |
+
"name": "stdout",
|
| 38 |
+
"output_type": "stream",
|
| 39 |
+
"text": [
|
| 40 |
+
"[OK] https://www.islamweb.net/ar/library/content/60/531/%D8%B3%D8%B9%D9%8A%D8%AF-%D8%A8%D9%86-%D8%A7%D9%84%D9%85%D8%B3%D9%8A%D8%A8 (Attempt 1) | Preview: <!DOCTYPE html><html><head> \t\t<meta charset=\"utf-8\"> \t\t<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\"> \t\t<meta name=\"language\" content=\"ar\"> \t\t ...\n",
|
| 41 |
+
"['سعيد بن المسيب ( ع )ابن حزن بن أبي وهب بن عمرو بن عائذ بن عمران بن مخزوم بن يقظة ، الإمام العلم ، أبو محمد القرشي المخزومي ، عالمأهلالمدينة، وسيد التابعين في زمانه . ولد[ص:218 ]لسنتين مضتا من خلافةعمررضي الله عنه وقيل : لأربع مضين منهابالمدينة.رأىعمر، وسمععثمان،وعليا،nindex.php?page=showalam&ids=47وزيد بن ثابت،وأبا موسى،وسعدا،nindex.php?page=showalam&ids=25وعائشةnindex.php?page=showalam&ids=3وأبا هريرة،nindex.php?page=showalam&ids=11وابن عباس،ومحمد بن مسلمة،nindex.php?page=showalam&ids=54وأم سلمة، وخلقا سواهم . وقيل : إنه سمع منعمر.وروى عنأبي بن كعبمرسلا ،وبلالكذلك ،nindex.php?page=showalam&ids=228وسعد بن عبادةكذلك ،وأبي ذرnindex.php?page=showalam&ids=4وأبي الدرداءكذلك ، وروايته عنعلي،وسعد،وعثمان،وأبي موسى،nindex.php?page=showalam&ids=25وعائشة،nindex.php?page=showalam&ids=11598وأم شريك،nindex.php?page=showalam&ids=12وابن عمر،nindex.php?page=showalam&ids=3وأبي هريرة،nindex.php?page=showalam&ids=11وابن عباس،nindex.php?page=showalam&ids=137وحكيم بن حزام،nindex.php?page=showalam&ids=12وعبد الله بن عمرو، وأبيهالمسيب،وأبي سعيدفي \" الصحيحين \" وعنحسان بن ثابت،nindex.php?page=showalam&ids=90وصفوان بن أمية،ومعمر بن عبد الله،ومعاوية،nindex.php?page=showalam&ids=54وأم سلمة، في صحيحمسلم. وروايته عنجبير بن مطعموجابر، وغيرهما فيnindex.php?page=showalam&ids=12070البخاري. وروايته عنعمرفي السنن الأربعة .وروى -أيضا- عنnindex.php?page=showalam&ids=47زيد بن ثابت،وسراقة بن مالك،وصهيب،والضحاك بن سفيان،وعبد الرحمن بن عثمان التيمي، وروايته عنعتاب بن أسيدفي السنن الأربعة ، وهو مرسل . وأرسل عن النبي -صلى الله عليه وسلم- وعنnindex.php?page=showalam&ids=1أبي بكر الصديقوكان زوج بنتnindex.php?page=showalam&ids=3أبي هريرة، وأعلم الناس بحديثه .روى عنه خلق : منهمإدريس بن صبيح،وأسامة بن زيد الليثي،وإسماعيل بن أمية، وبشيروعبد الرحمن بن حرملةوعبد الرحمن بن حميد بن عبد الرحمن،nindex.php?page=showalam&ids=16395وعبد الكريم الجزري،nindex.php?page=showalam&ids=16480وعبد ��لمجيد بن سهيل،وعبيد الله بن سليمان العبدي،وعثمان بن حكيم،nindex.php?page=showalam&ids=16566وعطاء الخراساني،وعقبة بن حريث[ص:219 ]وعلي بن جدعان،وعلي بن نفيل الحراني،وعمارة بن عبد الله بن طعمة،nindex.php?page=showalam&ids=16709وعمرو بن شعيب،nindex.php?page=showalam&ids=16705وعمرو بن دينار،nindex.php?page=showalam&ids=16718وعمرو بن مرة،وعمرو بن مسلم الليثي،وغيلان بن جرير،والقاسم بن عاصم، وابنهمحمد بن سعيد،وقتادة،ومحمد بن صفوان،ومحمد بن عبد الرحمن بن أبي لبيبة،nindex.php?page=showalam&ids=11958وأبو جعفر محمد بن علي،ومحمد بن عمرو بن عطاء،nindex.php?page=showalam&ids=12300والزهري،nindex.php?page=showalam&ids=16920وابن المنكدر،ومعبد بن هرمز،ومعمر بن أبي حبيبة،nindex.php?page=showalam&ids=17183وموسى بن وردان،وميسرة الأشجعي،nindex.php?page=showalam&ids=17188وميمون بن مهران،وأبو سهيل نافع بن مالك،وأبو معشر نجيح السندي، وهو عندالترمذي،وهاشم بن هاشم الوقاصي،ويحيى بن سعيد الأنصاري،ويزيد بن قسيط،ويزيد بن نعيم بن هزال،nindex.php?page=showalam&ids=17384ويعقوب بن عبد الله بن الأشج،ويونس بن سيف،وأبو جعفر الخطميوأبو قرة الأسدي، من \" التهذيب \" .وعنه :الزهري،وقتادة،nindex.php?page=showalam&ids=16705وعمرو بن دينار،ويحيى بن سعيد الأنصاري،nindex.php?page=showalam&ids=15562وبكير بن الأشج،nindex.php?page=showalam&ids=15854وداود بن أبي هند،nindex.php?page=showalam&ids=228وسعد بن إبراهيم،nindex.php?page=showalam&ids=16621وعلي بن زيد بن جدعان،nindex.php?page=showalam&ids=16100وشريك بن أبي نمر،وعبد الرحمن بن حرملةوبشر كثير .وكان ممن برز في العلم والعمل ، وقع لنا جملة من عالي حديثه .أخبرناأبو المعالي أحمد بن إسحاق القرافي، أنبأناالفتح بن عبد الله الكاتب، أنبأنامحمد بن عمر الشافعي،ومحمد بن أحمد الطرائفي،ومحمد بن علي بن الداية، قالوا : أنبأناأبو جعفر محمد بن أحمد بن المسلمة، أنبأناعبيد الله بن عبد الرحمن الزهريسنة ثمانين وثلاث مائة ، أنبأناجعفر بن[ص:220 ]محمد الفريابي، حدثناإبراهيم بن الحجاج السامي، حدثناحماد بن سلمة، عنnindex.php?page=showalam&ids=15854داود بن أبي هند، عنnindex.php?page=showalam&ids=15990سعيد بن المسيب، عنnindex.php?page=showalam&ids=3أبي هريرة-رضي الله عنه- ، أن رسول الله -صلى الله عليه وسلم- قال :nindex.php?page=hadith&LINKID=880037ثلاث من كن فيه فهو منافق وإن صام وصلى ، وزعم أنه مسلم : من إذا حدث كذب ، وإذا وعد أخلف ، وإذا ائتمن خان.هذا صحيح ، عال ، فيه دليل على أن هذه الخصال من كبار الذنوب .أخرجهمسلمعنnindex.php?page=showalam&ids=12175أبي نصر التمار، عنحماد بن سلمة، فوقع لنا بدلا عاليا مع علوه في نفسهلمسلمولنا ; فإن أعلى أنواع الإبدال أن يكون الحديث من أعلى حديث صاحب ذلك الكتاب ، ويقع لك بإسناد آخر أعلى بدرجة أو أكثر . والله أعلم .أخبرناإسحاق الأسدي، أنبأنايوسف الآدمي( ح ) وأنبأناأحمد بن سلامةقالا : أنبأناأبو المكارم الأصبهاني، قاليوسف سماعا، وقال الآخر إجازة : أنبأناأبو علي الحداد، أنبأناأبو نعيم، حدثناسليمان بن أحمد، حدثناأحمد بن داود المكي، حدثناحبيب كاتب مالك، حدثنا ابن أخيالزهري، عنالزهري، عنnindex.php?page=showalam&ids=15990سعيد بن المسيب، عنأبي بن كعب، قال : قال رسول الله -صلى الله عليه وسلم- :قال ليجبريل: ليبك الإسلام على موتعمر.هذا حديث منكر ،وحبيبليس بثقة ، مع أنسعيداعنأبيمنقطع .عبد العزيز بن المختار، عنعلي بن زيد، حدثنيسعيد بن المسيب بن حزنnindex.php?page=hadith&LINKID=880039أن جدهحزناأتى النبي -صلى الله عليه وسلم- فقال : ما اسمك؟ قال :حزن. قال : بل أنتسهل. قال : يا رسول الله ، اسم سماني به أبواي وعرفت به في الناس ، فسكت عنه النبي صلى الله عليه وسلم قالسعيد: فما زلنا تعرف الحزونة فينا أهل البيت.[ص:221 ]هذا حديث مرسل ، ومراسيلسعيدمحتج بها ، لكنعلي بن زيدليس بالحجة ، وأما الحديث فمروي بإسناد صحيح ، متصل ، ولفظه :nindex.php?page=hadith&LINKID=880040أن النبي -صلى الله عليه وسلم- قال له : ما اسمك؟ قال :حزن. قال : أنتسهل. فقال لا أغير اسما سمانيه أبي قالسعيد: فما زالت تلك الحزونة فينا بعد.العطاف بن خالد: عنأبي حرملة، عنابن المسيبقال : ما فاتتني الصلاة في جماعة منذ أربعين سنة .nindex.php?page=showalam&ids=16004سفيان الثوري: عنعثمان بن حكيم، سمعتnindex.php?page=showalam&ids=15990سعيد بن المسيبيقول : ما أذن المؤذن منذ ثلاثين سنة إلا وأنا في المسجد . إسناده ثابت .حماد بن زيد: حدثنايزيد بن حازم، أنnindex.php?page=showalam&ids=15990سعيد بن المسيبكان يسرد الصوم .مسعرعنسعيد بن إبراهيم، سمعابن المسيبيقول : ما أحد أعلم بقضاء قضاه رسول الله -صلى الله عليه وسلم- ولاأبو بكر، ولاعمرمني .[ص:222 ]أسامة بن زيد: عننافع، أنابن عمرذكرnindex.php?page=showalam&ids=15990سعيد بن المسيبفقال : هو -والله- أحد المفتين .قالnindex.php?page=showalam&ids=12251أحمد بن حنبلوغير واحد : مرسلاتnindex.php?page=showalam&ids=15990سعيد بن المسيبصحاح .وقالقتادة،ومكحول،nindex.php?page=showalam&ids=12300والزهري، وآخرون ، واللفظلقتادة: ما رأيت أعلم منnindex.php?page=showalam&ids=15990سعيد بن المسيب.قالnindex.php?page=showalam&ids=16604علي بن المديني: لا أعلم في التابعين أحدا أوسع علما منابن المسيب، هو عندي أجل التابعين .عبد الرحمن بن حرملة: سمعتابن المسيبيقول : حججت أربعين حجة .قاليحيى بن سعيد الأنصاري: كانسعيديكثر أن يقول في مجلسه : اللهم سلم سلم .معن : سمعتnindex.php?page=showalam&ids=16867مالكايقول : قالابن المسيب: إن كنت لأسير الأيام والليالي في طلب الحديث الواحد .ابن عيينة: عنإبراهيم بن طريف، عنحميد بن يعقوب، سمعnindex.php?page=showalam&ids=15990سعيد بن المسيبيقول : سمعت منعمركلمة ما بقي أحد سمعها غيري .أبو إسحاق الشيباني: عنبكير بن الأخنس، عنسعيد بن[ص:223 ]المسيب، قال : سمعتعمرعلى المنبر وهو يقول : لا أجد أحدا جامع فلم يغتسل ، أنزل أو لم ينزل ، إلا عاقبته .ابن عيينة : عنيحيى بن سعيد، عنابن المسيب، قال : ولدت لسنتين مضتا من خلافةعمروكانت خلافته عشر سنين وأربعة أشهر .', 'سَعِيدُ بْنُ الْمُسَيَّبِ ( ع )ابْنُ حَزْنِ بْنِ أَبِي وَهْبِ بْنِ عَمْرِو بْنِ عَائِذِ بْنِ عِمْرَانَ بْنِ مَخْزُومِ بْنِ يَقَظَةَ ، الْإِمَامُ الْعَلَمُ ، أَبُو مُحَمَّدٍ الْقُرَشِيُّ الْمَخْزُومِيُّ ، عَالِمُأَهْلِالْمَدِينَةِ، وَسَيِّدُ التَّابِعِينَ فِي زَمَانِهِ . وُلِدَ[ص:218 ]لِسَنَتَيْنِ مَضَتَا مِنْ خِلَافَةِعُمَرَرَضِيَ اللَّهُ عَنْهُ وَقِيلَ : لِأَرْبَعٍ مَضَيْنَ مِنْهَابِالْمَدِينَةِ.رَأَىعُمَرَ، وَسَمِعَعُثْمَانَ،وَعَلِيًّا،nindex.php?page=showalam&ids=47وَزَيْدَ بْنَ ثَابِتٍ،وَأَبَا مُوسَى،وَسَعْدًا،nindex.php?page=showalam&ids=25وَعَائِشَةَnindex.php?page=showalam&ids=3وَأَبَا هُرَيْرَةَ،nindex.php?page=showalam&ids=11وَابْنَ عَبَّاسٍ،وَمُحَمَّدَ بْنَ مَسْلَمَةَ،nindex.php?page=showalam&ids=54وَأُمَّ سَلَمَةَ، وَخَلْقًا سِوَاهُمْ . وَقِيلَ : إِنَّهُ سَمِعَ مِنْعُمَرَ.وَرَوَى عَنْأُبَيِّ بْنِ كَعْبٍمُرْسَلًا ،وَبِلَالٍكَذَلِكَ ،nindex.php?page=showalam&ids=228وَسَعْدِ بْنِ عُبَادَةَكَذَلِكَ ،وَأَبِي ذَرٍّnindex.php?page=showalam&ids=4وَأَبِي الدَّرْدَاءِكَذَلِكَ ، وَرِوَايَتُهُ عَنْعَلِيٍّ،وَسَعْدٍ،وَعُثْمَانَ،وَأَبِي مُوسَى،nindex.php?page=showalam&ids=25وَعَائِشَةَ،nindex.php?page=showalam&ids=11598وَأُمِّ شَرِيكٍ،nindex.php?page=showalam&ids=12وَابْنِ عُمَرَ،nindex.php?page=showalam&ids=3وَأَبِي هُرَيْرَةَ،nindex.php?page=showalam&ids=11وَابْنِ عَبَّاسٍ،nindex.php?page=showalam&ids=137وَحَكِيمِ بْنِ حِزَامٍ،nindex.php?page=showalam&ids=12وَعَبْدِ اللَّهِ بْنِ عَمْرٍو، وَأَبِيهِالْمُسَيَّبِ،وَأَبِي سَعِيدٍفِي \" الصَّحِيحَيْنِ \" وَعَنْحَسَّانَ بْنِ ثَابِتٍ،nindex.php?page=showalam&ids=90وَصَفْوَانَ بْنِ أُمَيَّةَ،وَمَعْمَرِ بْنِ عَبْدِ اللَّهِ،وَمُعَاوِيَةَ،nindex.php?page=showalam&ids=54وَأُمِّ سَلَمَةَ، فِي صَحِيحِمُسْلِمٍ. وَرِوَايَتُهُ عَنْجُبَيْرِ بْنِ مُطْعِمٍوَجَابِرٍ، وَغَيْرِهِمَا فِيnindex.php?page=showalam&ids=12070الْبُخَارِيِّ. وَرِوَايَتُهُ عَنْعُمَرَفِي السُّنَنِ الْأَرْبَعَةِ .وَرَوَى -أَيْضًا- عَنْnindex.php?page=showalam&ids=47زَيْدِ بْنِ ثَابِتٍ،وَسُرَاقَةَ بْنِ مَالِكٍ،وَصُهَيْبٍ،وَالضَّحَّاكِ بْنِ سُفْيَانَ،وَعَبْدِ الرَّحْمَنِ بْنِ عُثْمَانَ التَّيْمِيِّ، وَرِوَايَتُهُ عَنْعَتَّابِ بْنِ أَسِيدٍفِي السُّنَنِ الْأَرْبَعَةِ ، وَهُوَ مُرْسَلٌ . وَأَرْسَلَ عَنِ النَّبِيِّ -صَلَّى اللَّهُ عَلَيْهِ وَسَلَّمَ- وَعَنْnindex.php?page=showalam&ids=1أَبِي بَكْرٍ الصِّدِّيقِوَكَانَ زَوْجَ بِنْتِnindex.php?page=showalam&ids=3أَبِي هُرَيْرَةَ، وَأَعْلَمَ النَّاسِ بِحَدِيثِهِ .رَوَى عَنْهُ خَلْقٌ : مِنْهُمْإِدْرِيسُ بْنُ صُبَيْحٍ،وَأُسَامَةُ بْنُ زَيْدٍ اللَّيْثِيُّ،وَإِسْمَاعِيلُ بْنُ أُمَيَّةَ، وَبَشِيرٌوَعَبْدُ الرَّحْمَنِ بْنُ حَرْمَلَةَوَعَبْدُ الرَّحْمَنِ بْنُ حُمَيْدِ بْنِ عَبْدِ الرَّحْمَنِ،nindex.php?page=showalam&ids=16395وَعَبْدُ الْكَرِيمِ الْجَزَرِيُّ،nindex.php?page=showalam&ids=16480وَعَبْدُ الْمَجِيدِ بْنُ سُهَيْلٍ،وَعُبَيْدُ اللَّهِ بْنُ سُلَيْمَانَ الْعَبْدِيُّ،وَعُثْمَانُ بْنُ حَكِيمٍ،nindex.php?page=showalam&ids=16566وَعَطَاءٌ الْخُرَاسَانِيُّ،وَعُقْبَةُ بْنُ حُرَيْثٍ[ص:219 ]وَعَلِيُّ بْنُ جُدْعَانَ،وَعَلِيُّ بْنُ نُفَيْلٍ الْحَرَّانِيُّ،وَعُمَارَةُ بْنُ عَبْدِ اللَّهِ بْنِ طُعْمَةَ،nindex.php?page=showalam&ids=16709وَعَمْرُو بْنُ شُعَيْبٍ،nindex.php?page=showalam&ids=16705وَعَمْرُو بْنُ دِينَارٍ،nindex.php?page=showalam&ids=16718وَعَمْرُو بْنُ مُرَّةَ،وَعَمْرُو بْنُ مُسْلِمٍ اللَّيْثِيُّ،وَغَيْلَانُ بْنُ جَرِيرٍ،وَالْقَاسِمُ بْنُ عَاصِمٍ، وَابْنُهُمُحَمَّدُ بْنُ سَعِيدٍ،وَقَتَادَةُ،وَمُحَمَّدُ بْنُ صَفْوَانَ،وَمُحَمَّدُ بْنُ عَبْدِ الرَّحْمَنِ بْنِ أَبِي لَبِيبَةَ،nindex.php?page=showalam&ids=11958وَأَبُو جَعْفَرٍ مُحَمَّدُ بْنُ عَلِيٍّ،وَمُحَمَّدُ بْنُ عَمْرِو بْنِ عَطَاءٍ،nindex.php?page=showalam&ids=12300وَالزُّهْرِيُّ،nindex.php?page=showalam&ids=16920وَابْنُ الْمُنْكَدِرِ،وَمَعْبَدُ بْنُ هُرْمُزٍ،وَمَعْمَرُ بْنُ أَبِي حَبِيبَةَ،nindex.php?page=showalam&ids=17183وَمُوسَى بْنُ وَرْدَانَ،وَمَيْسَرَةُ الْأَشْجَعِيُّ،nindex.php?page=showalam&ids=17188وَمَيْمُونُ بْنُ مِهْرَانَ،وَأَبُو سُهَيْلٍ نَافِعُ بْنُ مَالِكٍ،وَأَبُو مَعْشَرٍ نَجِيحُ السِّنْدِيُّ، وَهُوَ عِنْدَالتِّرْمِذِيِّ،وَهَاشِمُ بْنُ هَاشِمٍ الْوَقَّاصِي،وَيَحْيَى بْنُ سَعِيدٍ الْأَنْصَارِيُّ،وَيَزِيدُ بْنُ قُسَيْطٍ،وَيَزِيدُ بْنُ نُعَيْمِ بْنِ هُزَالٍ،nindex.php?page=showalam&ids=17384وَيَعْقُوبُ بْنُ عَبْدِ اللَّهِ بْنِ الْأَشَجِّ،وَيُونُسُ بْنُ سَيْفٍ،وَأَبُو جَعْفَرٍ الْخَطْمِيُّوَأَبُو قُرَّةَ الْأَسَدِيُّ، مِنَ \" التَّهْذِيبِ \" .وَعَنْهُ :الزُّهْرِيُّ،وَقَتَادَةُ،nindex.php?page=showalam&ids=16705وَعَمْرُو بْنُ دِينَارٍ،وَيَحْيَى بْنُ سَعِيدٍ الْأَنْصَارِيُّ،nindex.php?page=showalam&ids=15562وَبُكَيْرُ بْنُ الْأَشَجِّ،nindex.php?page=showalam&ids=15854وَدَاوُدُ بْنُ أَبِي هِنْدٍ،nindex.php?page=showalam&ids=228وَسَعْدُ بْنُ إِبْرَاهِيمَ،nindex.php?page=showalam&ids=16621وَعَلِيُّ بْنُ زَيْدِ بْنِ جُدْعَانَ،nindex.php?page=showalam&ids=16100وَشَرِيكُ بْنُ أَبِي نَمِرٍ،وَعَبْدُ الرَّحْمَنِ بْنُ حَرْمَلَةَوَبَشَرٌ كَثِيرٌ .وَكَانَ مِمَّنْ بَرَزَ فِي الْعِلْمِ وَالْعَمَلِ ، وَقَعَ لَنَا جُمْلَةٌ مِنْ عَالِي حَدِيثِهِ .أَخْبَرَنَاأَبُو الْمَعَالِي أَحْمَدُ بْنُ إِسْحَاقَ الْقَرَافِيُّ، أَنْبَأَنَاالْفَتْحُ بْنُ عَبْدِ اللَّهِ الْكَاتِبُ، أَنْبَأَنَامُحَمَّدُ بْنُ عُمَرَ الشَّافِعِيُّ،وَمُحَمَّدُ بْنُ أَحْمَدَ الطَّرَائِفِيُّ،وَمُحَمَّدُ بْنُ عَلِيِّ بْنِ الدَّايَةِ، قَالُوا : أَنْبَأَنَاأَبُو جَعْفَرٍ مُحَمَّدُ بْنُ أَحْمَدَ بْنِ الْمُسْلِمَةِ، أَنْبَأَنَاعُبَيْدُ اللَّهِ بْنُ عَبْدِ الرَّحْمَنِ الزُّهْرِيُّسَنَةَ ثَمَانِينَ وَثَلَاثِ مِائَةٍ ، أَنْبَأَنَاجَعْفَرُ بْنُ[ص:220 ]مُحَمَّدٍ الْفِرْيَابِيُّ، حَدَّثَنَاإِبْرَاهِيمُ بْنُ الْحَجَّاجِ السَّامِيُّ، حَدَّثَنَاحَمَّادُ بْنُ سَلَمَةَ، عَنْnindex.php?page=showalam&ids=15854دَاوُدَ بْنِ أَبِي هِنْدٍ، عَنْnindex.php?page=showalam&ids=15990سَعِيدِ بْنِ الْمُسَيَّبِ، عَنْnindex.php?page=showalam&ids=3أَبِي هُرَيْرَةَ-رَضِيَ اللَّهُ عَنْهُ- ، أَنَّ رَسُولَ اللَّهِ -صَلَّى اللَّهُ عَلَيْهِ وَسَلَّمَ- قَالَ :nindex.php?page=hadith&LINKID=880037ثَلَاثٌ مَنْ كُنَّ فِيهِ فَهُوَ مُنَافِقٌ وَإِنْ صَامَ وَصَلَّى ، وَزَعَمَ أَنَّهُ مُسْلِمٌ : مَنْ إِذَا حَدَّثَ كَذَبَ ، وَإِذَا وَعَدَ أَخْلَفَ ، وَإِذَا ائْتُمِنَ خَانَ.هَذَا صَحِيحٌ ، عَالٍ ، فِيهِ دَلِيلٌ عَلَى أَنَّ هَذِهِ الْخِصَالَ مِنْ كِبَارِ الذُّنُوبِ .أَخْرَجَهُمُسْلِمٌعَنْnindex.php?page=showalam&ids=12175أَبِي نَصْرٍ التَّمَّارِ، عَنْحَمَّادِ بْنِ سَلَمَةَ، فَوَقَعَ لَنَا بَدَلًا عَالِيًا مَعَ عُلُوِّهِ فِي نَفْسِهِلِمُسْلِمٍوَلَنَا ; فَإِنَّ أَعْلَى أَنْوَاعِ الْإِبْدَالِ أَنْ يَكُونَ الْحَدِيثُ مِنْ أَعْلَى حَدِيثِ صَاحِبِ ذَلِكَ الْكِتَابِ ، وَيَقَعُ لَكَ بِإِسْنَادٍ آخَرَ أَعْلَى بِدَرَجَةٍ أَوْ أَكْثَرَ . وَاللَّهُ أَعْلَمُ .أَخْبَرَنَاإِسْحَاقُ الْأَسَدِيُّ، أَنْبَأَنَايُوسُفُ الْآدَمِيُّ( ح ) وَأَنْبَأَنَاأَحْمَدُ بْنُ سَلَامَةَقَالَا : أَنْبَأَنَاأَبُو الْمَكَارِمِ الْأَصْبَهَانِيُّ، قَالَيُوسُفُ سَمَاعًا، وَقَالَ الْآخَرُ إِجَازَةً : أَنْبَأَنَاأَبُو عَلِيٍّ الْحَدَّادُ، أَنْبَأَنَاأَبُو نُعَيْمٍ، حَدَّثَنَاسُلَيْمَانُ بْنُ أَحْمَدَ، حَدَّثَنَاأَحْمَدُ بْنُ دَاوُدَ الْمَكِّيُّ، حَدَّثَنَاحَبِيبٌ كَاتِبُ مَالِكٍ، حَدَّثَنَا ابْنُ أَخِيالزُّهْرِيِّ، عَنِالزُّهْرِيِّ، عَنْnindex.php?page=showalam&ids=15990سَعِيدِ بْنِ الْمُسَيَّبِ، عَنْأُبَيِّ بْنِ كَعْبٍ، قَالَ : قَالَ رَسُولُ اللَّهِ -صَلَّى اللَّهُ عَلَيْهِ وَسَلَّمَ- :قَالَ لِيجِبْرِيلُ: لِيَبْكِ الْإِسْلَامُ عَلَى مَوْتِعُمَرَ.هَذَا حَدِيثٌ مُنْكَرٌ ،وَحَبِيبٌلَيْسَ بِثِقَةٍ ، مَعَ أَنَّسَعِيدًاعَنْأُبَيٍّمُنْقَطِعٌ .عَبْدُ الْعَزِيزِ بْنُ الْمُخْتَارِ، عَنْعَلِيِّ بْنِ زَيْدٍ، حَدَّثَنِيسَعِيدُ بْنُ الْمُسَيَّبِ بْنِ حَزْنٍnindex.php?page=hadith&LINKID=880039أَنَّ جَدَّهُحَزْنًاأَتَى النَّبِيَّ -صَلَّى اللَّهُ عَلَيْهِ وَسَلَّمَ- فَقَالَ : مَا اسْمُكَ؟ قَالَ :حَزْنٌ. قَالَ : بَلْ أَنْتَسَهْلٌ. قَالَ : يَا رَسُولَ اللَّهِ ، اسْمٌ سَمَّانِي بِهِ أَبَوَايَ وَعُرِفْتُ بِهِ فِي النَّاسِ ، فَسَكَتَ عَنْهُ النَّبِيُّ صَلَّى اللَّهُ عَلَيْهِ وَسَلَّمَ قَالَسَعِيدٌ: فَمَا زِلْنَا تُعْرَفُ الْحُزُونَةُ فِينَا أَهْلَ الْبَيْتِ.[ص:221 ]هَذَا حَدِيثٌ مُرْسَلٌ ، وَمَرَاسِيلُسَعِيدٍمُحْتَجٌّ بِهَا ، لَكِنَّعَلِيَّ بْنَ زَيْدٍلَيْسَ بِالْحُجَّةِ ، وَأَمَّا الْحَدِيثُ فَمَرْوِيٌّ بِإِسْنَادٍ صَحِيحٍ ، مُتَّصِلٍ ، وَلَفْظُهُ :nindex.php?page=hadith&LINKID=880040أَنَّ النَّبِيَّ -صَلَّى اللَّهُ عَلَيْهِ وَسَلَّمَ- قَالَ لَهُ : مَا اسْمُكَ؟ قَالَ :حَزْنٌ. قَالَ : أَنْتَسَهْلٌ. فَقَالَ لَا أُغَيِّرُ اسْمًا سَمَّانِيهِ أَبِي قَالَسَعِيدٌ: فَمَا زَالَتْ تِلْكَ الْحُزُونَةُ فِينَا بَعْدُ.الْعَطَّافُ بْنُ خَالِدٍ: عَنْأَبِي حَرْمَلَةَ، عَنِابْنِ الْمُسَيَّبِقَالَ : مَا فَاتَتْنِي الصَّلَاةُ فِي جَمَاعَةٍ مُنْذُ أَرْبَعِينَ سَنَةً .nindex.php?page=showalam&ids=16004سُفْيَانُ الثَّوْرِيُّ: عَنْعُثْمَانَ بْنِ حَكِيمٍ، سَمِعْتُnindex.php?page=showalam&ids=15990سَعِيدَ بْنَ الْمُسَيَّبِيَقُولُ : مَا أَذَّنَ الْمُؤَذِّنُ مُنْذُ ثَلَاثِينَ سَنَةً إِلَّا وَأَنَا فِي الْمَسْجِدِ . إِسْنَادُهُ ثَابِتٌ .حَمَّادُ بْنُ زَيْدٍ: حَدَّثَنَايَزِيدُ بْنُ حَازِمٍ، أَنَّnindex.php?page=showalam&ids=15990سَعِيدَ بْنَ الْمُسَيَّبِكَانَ يَسْرُدُ الصَّوْمَ .مِسْعَرٌعَنْسَعِيدِ بْنِ إِبْرَاهِيمَ، سَمِعَابْنَ الْمُسَيَّبِيَقُولُ : مَا أَحَدٌ أَعْلَمُ بِقَضَاءٍ قَضَاهُ رَسُولُ اللَّهِ -صَلَّى اللَّهُ عَلَيْهِ وَسَلَّمَ- وَلَاأَبُو بَكْرٍ، وَلَاعُمَرُمِنِّي .[ص:222 ]أُسَامَةُ بْنُ زَيْدٍ: عَنْنَافِعٍ، أَنَّابْنَ عُمَرَذَكَرَnindex.php?page=showalam&ids=15990سَعِيدَ بْنَ الْمُسَيَّبِفَقَالَ : هُوَ -وَاللَّهِ- أَحَدُ الْمُفْتِينَ .قَالَnindex.php?page=showalam&ids=12251أَحْمَدُ بْنُ حَنْبَلٍوَغَيْرُ وَاحِدٍ : مُرْسَلَاتُnindex.php?page=showalam&ids=15990سَعِيدِ بْنِ الْمُسَيَّبِصِحَاحٌ .وَقَالَقَتَادَةُ،وَمَكْحُولٌ،nindex.php?page=showalam&ids=12300وَالزُّهْرِيُّ، وَآخَرُونَ ، وَاللَّفْظُلِقَتَادَةَ: مَا رَأَيْتُ أَعْلَمَ مِنْnindex.php?page=showalam&ids=15990سَعِيدِ بْنِ الْمُسَيَّبِ.قَالَnindex.php?page=showalam&ids=16604عَلِيُّ بْنُ الْمَدِينِيِّ: لَا أَعْلَمُ فِي التَّابِعِينَ أَحَدًا أَوْسَعَ عِلْمًا مِنْابْنِ الْمُسَيَّبِ، هُوَ عِنْدِي أَجَلُّ التَّابِعِينَ .عَبْدُ الرَّحْمَنِ بْنُ حَرْمَلَةَ: سَمِعْتُابْنَ الْمُسَيَّبِيَقُولُ : حَجَجْتُ أَرْبَعِينَ حِجَّةً .قَالَيَحْيَى بْنُ سَعِيدٍ الْأَنْصَارِيُّ: كَانَسَعِيدٌيُكْثِرُ أَنْ يَقُولَ فِي مَجْلِسِهِ : اللَّهُمَّ سَلِّمْ سَلِّمْ .مَعْنٌ : سَمِعْتُnindex.php?page=showalam&ids=16867مَالِكًايَقُولُ : قَالَابْنُ الْمُسَيَّبِ: إِنُ كُنْتُ لَأَسِيرُ الْأَيَّامَ وَاللَّيَالِيَ فِي طَلَبِ الْحَدِيثِ الْوَاحِدِ .ابْنُ عُيَيْنَةَ: عَنْإِبْرَاهِيمَ بْنِ طَرِيفٍ، عَنْحُمَيْدِ بْنِ يَعْقُوبَ، سَمِعَnindex.php?page=showalam&ids=15990سَعِيدَ بْنَ الْمُسَيَّبِيَقُولُ : سَمِعْتُ مِنْعُمَرَكَلِمَةً مَا بَقِيَ أَحَدٌ سَمِعَهَا غَيْرِي .أَبُو إِسْحَاقَ الشَّيْبَانِيُّ: عَنْبُكَيْرِ بْنِ الْأَخْنَسِ، عَنْسَعِيدِ بْنِ[ص:223 ]الْمُسَيَّبِ، قَالَ : سَمِعْتُعُمَرَعَلَى الْمِنْبَرِ وَهُوَ يَقُولُ : لَا أَجِدُ أَحَدًا جَامَعَ فَلَمْ يَغْتَسِلْ ، أَنْزَلَ أَوْ لَمْ يُنْزِلْ ، إِلَّا عَاقَبْتُهُ .ابْنُ عُيَيْنَةَ : عَنْيَحْيَى بْنِ سَعِيدٍ، عَنِابْنِ الْمُسَيَّبِ، قَالَ : وُلِدْتُ لِسَنَتَيْنِ مَضَتَا مِنْ خِلَافَةِعُمَرَوَكَانَتْ خِلَافَتُهُ عَشْرَ سِنِينَ وَأَرْبَعَةَ أَشْهُرٍ .']\n"
|
| 42 |
+
]
|
| 43 |
+
},
|
| 44 |
+
{
|
| 45 |
+
"name": "stderr",
|
| 46 |
+
"output_type": "stream",
|
| 47 |
+
"text": [
|
| 48 |
+
"/tmp/ipykernel_13652/1161671790.py:1: RuntimeWarning: coroutine 'scrape_siyar_alaam_nubala' was never awaited\n",
|
| 49 |
+
" result = await scrape_siyar_alaam_nubala()\n",
|
| 50 |
+
"RuntimeWarning: Enable tracemalloc to get the object allocation traceback\n"
|
| 51 |
]
|
| 52 |
}
|
| 53 |
],
|
| 54 |
"source": [
|
| 55 |
+
"result = await scrape_siyar_alaam_nubala()\n",
|
| 56 |
+
"print(result)"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
]
|
| 58 |
},
|
| 59 |
{
|
| 60 |
"cell_type": "code",
|
| 61 |
"execution_count": null,
|
| 62 |
+
"id": "9ebfe517",
|
| 63 |
"metadata": {},
|
| 64 |
"outputs": [],
|
| 65 |
"source": []
|
docs/README_Enhanced_Narrator_Agent.md
DELETED
|
@@ -1,190 +0,0 @@
|
|
| 1 |
-
# Enhanced Narrator Analyzer Agent
|
| 2 |
-
|
| 3 |
-
## Overview
|
| 4 |
-
|
| 5 |
-
The Enhanced Narrator Analyzer Agent is an intelligent system that combines multiple data sources to provide comprehensive analysis of hadith narrators. It integrates:
|
| 6 |
-
|
| 7 |
-
1. **Shamela.ws Scraping**: Automatically extracts narrator information from Shamela.ws
|
| 8 |
-
2. **LLM Analysis**: Uses Google's Gemini model for intelligent reasoning and synthesis
|
| 9 |
-
3. **Scholarly Assessment**: Applies traditional hadith criticism methodologies
|
| 10 |
-
|
| 11 |
-
## Key Features
|
| 12 |
-
|
| 13 |
-
### 🔍 Multi-Source Analysis
|
| 14 |
-
- **Shamela Data**: Extracts biographical info and scholarly opinions from Shamela.ws
|
| 15 |
-
- **LLM Knowledge**: Leverages AI's knowledge of Islamic hadith literature
|
| 16 |
-
- **Data Synthesis**: Intelligently combines both sources for comprehensive assessment
|
| 17 |
-
|
| 18 |
-
### 🔗 Chain Analysis
|
| 19 |
-
- **Individual Narrator Analysis**: Detailed assessment of each narrator
|
| 20 |
-
- **Chain Synthesis**: Overall assessment considering the "weakest link" principle
|
| 21 |
-
- **Scholarly Methodology**: Applies traditional hadith criticism standards
|
| 22 |
-
|
| 23 |
-
### 📊 Structured Output
|
| 24 |
-
- **Reliability Grades**: Thiqah, Saduq, Da'if, Matruk, Majhul
|
| 25 |
-
- **Confidence Levels**: High, Medium, Low
|
| 26 |
-
- **Detailed Reasoning**: Comprehensive explanations for all assessments
|
| 27 |
-
- **Practical Recommendations**: Guidance for hadith scholars
|
| 28 |
-
|
| 29 |
-
## API Endpoints
|
| 30 |
-
|
| 31 |
-
### 1. Enhanced Narrator Analysis
|
| 32 |
-
```http
|
| 33 |
-
POST /api/v1/analyze-narrator
|
| 34 |
-
```
|
| 35 |
-
Analyzes a single narrator using both Shamela data and LLM knowledge.
|
| 36 |
-
|
| 37 |
-
**Request:**
|
| 38 |
-
```json
|
| 39 |
-
{
|
| 40 |
-
"narrator_name": "زرارة بن أعين"
|
| 41 |
-
}
|
| 42 |
-
```
|
| 43 |
-
|
| 44 |
-
**Response:**
|
| 45 |
-
```json
|
| 46 |
-
{
|
| 47 |
-
"narrator_name": "زرارة بن أعين",
|
| 48 |
-
"reliability_grade": "Thiqah",
|
| 49 |
-
"confidence_level": "High",
|
| 50 |
-
"reasoning": "Detailed analysis combining Shamela data and scholarly knowledge...",
|
| 51 |
-
"scholarly_consensus": "Analysis of what scholars have said...",
|
| 52 |
-
"biographical_info": "Basic biographical information...",
|
| 53 |
-
"recommendation": "Practical guidance for hadith scholars...",
|
| 54 |
-
"success": true,
|
| 55 |
-
"message": "Analysis completed using Shamela data (3 scholars) + LLM knowledge"
|
| 56 |
-
}
|
| 57 |
-
```
|
| 58 |
-
|
| 59 |
-
### 2. Narrator Chain Analysis
|
| 60 |
-
```http
|
| 61 |
-
POST /api/v1/analyze-narrator-chain
|
| 62 |
-
```
|
| 63 |
-
Analyzes a complete chain of narrators with synthesis.
|
| 64 |
-
|
| 65 |
-
**Request:**
|
| 66 |
-
```json
|
| 67 |
-
["زرارة بن أعين", "أبو جعفر الباقر", "جابر بن عبد الله"]
|
| 68 |
-
```
|
| 69 |
-
|
| 70 |
-
**Response:**
|
| 71 |
-
```json
|
| 72 |
-
{
|
| 73 |
-
"chain": ["narrator1", "narrator2", "narrator3"],
|
| 74 |
-
"individual_analyses": {
|
| 75 |
-
"narrator1": { "detailed analysis..." },
|
| 76 |
-
"narrator2": { "detailed analysis..." },
|
| 77 |
-
"narrator3": { "detailed analysis..." }
|
| 78 |
-
},
|
| 79 |
-
"chain_synthesis": {
|
| 80 |
-
"overall_assessment": "Comprehensive chain evaluation...",
|
| 81 |
-
"success": true
|
| 82 |
-
},
|
| 83 |
-
"metadata": {
|
| 84 |
-
"total_narrators": 3,
|
| 85 |
-
"successful_analyses": 3,
|
| 86 |
-
"analysis_method": "Enhanced agent with Shamela.ws + LLM"
|
| 87 |
-
}
|
| 88 |
-
}
|
| 89 |
-
```
|
| 90 |
-
|
| 91 |
-
### 3. Complete Hadith Analysis
|
| 92 |
-
```http
|
| 93 |
-
POST /api/v1/extract-and-analyze
|
| 94 |
-
```
|
| 95 |
-
Complete workflow: extract narrators from hadith text and analyze the chain.
|
| 96 |
-
|
| 97 |
-
**Request:**
|
| 98 |
-
```json
|
| 99 |
-
{
|
| 100 |
-
"hadith_text": "حدثنا زرارة بن أعين عن أبي جعفر الباقر..."
|
| 101 |
-
}
|
| 102 |
-
```
|
| 103 |
-
|
| 104 |
-
## Technical Architecture
|
| 105 |
-
|
| 106 |
-
### Data Flow
|
| 107 |
-
1. **Input**: Narrator name or hadith text
|
| 108 |
-
2. **Shamela Scraping**: Automated extraction of narrator data
|
| 109 |
-
3. **Data Formatting**: Structure Shamela data for LLM consumption
|
| 110 |
-
4. **LLM Analysis**: Intelligent reasoning combining all sources
|
| 111 |
-
5. **Output**: Structured, comprehensive assessment
|
| 112 |
-
|
| 113 |
-
### Integration Components
|
| 114 |
-
|
| 115 |
-
#### ShamelaNarratorExtractor
|
| 116 |
-
- Searches Google for Shamela.ws narrator pages
|
| 117 |
-
- Extracts biographical information and scholarly critiques
|
| 118 |
-
- Provides structured data with quality metadata
|
| 119 |
-
|
| 120 |
-
#### Enhanced LLM Service
|
| 121 |
-
- Formats Shamela data for optimal LLM understanding
|
| 122 |
-
- Uses sophisticated prompts for scholarly analysis
|
| 123 |
-
- Applies traditional hadith criticism methodologies
|
| 124 |
-
|
| 125 |
-
#### Chain Analysis Features
|
| 126 |
-
- Individual narrator assessment
|
| 127 |
-
- Chain-level synthesis considering cumulative effects
|
| 128 |
-
- Practical recommendations for hadith acceptance
|
| 129 |
-
|
| 130 |
-
## Scholarly Methodology
|
| 131 |
-
|
| 132 |
-
### Reliability Grades
|
| 133 |
-
- **Thiqah (ثقة)**: Trustworthy - strong consensus of reliability
|
| 134 |
-
- **Saduq (صدوق)**: Truthful - generally reliable with minor reservations
|
| 135 |
-
- **Da'if (ضعيف)**: Weak - significant concerns about reliability
|
| 136 |
-
- **Matruk (متروك)**: Abandoned - severe weakness, narrations rejected
|
| 137 |
-
- **Majhul (مجهول)**: Unknown - insufficient reliable information
|
| 138 |
-
|
| 139 |
-
### Analysis Framework
|
| 140 |
-
1. **Prioritize Classical Scholars**: Ibn Hajar, Dhahabi, Ibn Hibban, etc.
|
| 141 |
-
2. **Consider Scholarly Consensus**: Weight of agreement among authorities
|
| 142 |
-
3. **Balance Criticism vs Praise**: Appropriate evaluation of mixed opinions
|
| 143 |
-
4. **Historical Context**: Account for time period and scholarly methodology
|
| 144 |
-
5. **Practical Application**: Provide actionable guidance for hadith scholars
|
| 145 |
-
|
| 146 |
-
### Confidence Assessment
|
| 147 |
-
- **High**: Strong evidence from multiple reliable sources
|
| 148 |
-
- **Medium**: Good evidence with some limitations or disagreement
|
| 149 |
-
- **Low**: Limited evidence or significant uncertainty
|
| 150 |
-
|
| 151 |
-
## Usage Examples
|
| 152 |
-
|
| 153 |
-
### Testing the Enhanced Agent
|
| 154 |
-
```bash
|
| 155 |
-
python test_enhanced_narrator_agent.py
|
| 156 |
-
```
|
| 157 |
-
|
| 158 |
-
This will run comprehensive tests demonstrating:
|
| 159 |
-
- Single narrator analysis with Shamela integration
|
| 160 |
-
- Chain analysis with synthesis
|
| 161 |
-
- Unknown narrator handling
|
| 162 |
-
- Error handling and edge cases
|
| 163 |
-
|
| 164 |
-
### API Integration
|
| 165 |
-
The enhanced analyzer integrates seamlessly with existing FastAPI endpoints and maintains backward compatibility while adding powerful new features.
|
| 166 |
-
|
| 167 |
-
## Benefits
|
| 168 |
-
|
| 169 |
-
### For Researchers
|
| 170 |
-
- **Comprehensive Data**: Combines multiple authoritative sources
|
| 171 |
-
- **Time Saving**: Automated data gathering and analysis
|
| 172 |
-
- **Scholarly Rigor**: Applies traditional methodologies systematically
|
| 173 |
-
|
| 174 |
-
### For Developers
|
| 175 |
-
- **Structured Output**: Easy integration with other systems
|
| 176 |
-
- **Scalable**: Can analyze individual narrators or complete chains
|
| 177 |
-
- **Reliable**: Robust error handling and quality metadata
|
| 178 |
-
|
| 179 |
-
### For Students
|
| 180 |
-
- **Educational**: Detailed reasoning explains traditional methodology
|
| 181 |
-
- **Accessible**: Converts complex scholarly assessments into clear guidance
|
| 182 |
-
- **Comprehensive**: Provides both specific and general insights
|
| 183 |
-
|
| 184 |
-
## Future Enhancements
|
| 185 |
-
|
| 186 |
-
1. **Additional Sources**: Integration with more classical texts and databases
|
| 187 |
-
2. **Comparative Analysis**: Cross-referencing multiple narrator evaluation works
|
| 188 |
-
3. **Historical Timeline**: Temporal analysis of narrator relationships
|
| 189 |
-
4. **Visualization**: Graphical representation of narrator chains and assessments
|
| 190 |
-
5. **Caching**: Performance optimization for frequently analyzed narrators
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
docs/README_ShamelaNarratorExtractor.md
DELETED
|
@@ -1,246 +0,0 @@
|
|
| 1 |
-
# ShamelaNarratorExtractor Documentation
|
| 2 |
-
|
| 3 |
-
## Overview
|
| 4 |
-
|
| 5 |
-
The `ShamelaNarratorExtractor` class is a comprehensive tool for extracting narrator information from Shamela.ws (المكتبة الشاملة) that can be easily integrated into LangGraph workflows.
|
| 6 |
-
|
| 7 |
-
## Features
|
| 8 |
-
|
| 9 |
-
- **Automated Search**: Searches Google for narrator pages on Shamela.ws
|
| 10 |
-
- **Structured Extraction**: Extracts biographical information and scholarly critiques
|
| 11 |
-
- **Type Safety**: Uses dataclasses for type-safe data structures
|
| 12 |
-
- **LangGraph Ready**: Designed to work seamlessly with LangGraph nodes
|
| 13 |
-
- **Error Handling**: Robust error handling and reporting
|
| 14 |
-
- **Backwards Compatibility**: Provides legacy function support
|
| 15 |
-
|
| 16 |
-
## Data Structures
|
| 17 |
-
|
| 18 |
-
### NarratorComment
|
| 19 |
-
```python
|
| 20 |
-
@dataclass
|
| 21 |
-
class NarratorComment:
|
| 22 |
-
text: str # The comment text
|
| 23 |
-
highlighted: List[str] # Highlighted/bold text in the comment
|
| 24 |
-
```
|
| 25 |
-
|
| 26 |
-
### ScholarCritique
|
| 27 |
-
```python
|
| 28 |
-
@dataclass
|
| 29 |
-
class ScholarCritique:
|
| 30 |
-
scholar: str # Name of the scholar
|
| 31 |
-
comments: List[NarratorComment] # List of comments from this scholar
|
| 32 |
-
```
|
| 33 |
-
|
| 34 |
-
### NarratorInfo
|
| 35 |
-
```python
|
| 36 |
-
@dataclass
|
| 37 |
-
class NarratorInfo:
|
| 38 |
-
narrator_name: Optional[str] # Name of the narrator
|
| 39 |
-
biographical_info: Dict[str, str] # Biographical information
|
| 40 |
-
scholarly_critique: List[ScholarCritique] # Scholarly critiques
|
| 41 |
-
extraction_metadata: Dict[str, Any] # Extraction statistics and metadata
|
| 42 |
-
```
|
| 43 |
-
|
| 44 |
-
## Main Class: ShamelaNarratorExtractor
|
| 45 |
-
|
| 46 |
-
### Constructor
|
| 47 |
-
```python
|
| 48 |
-
extractor = ShamelaNarratorExtractor()
|
| 49 |
-
```
|
| 50 |
-
|
| 51 |
-
No parameters required. Initializes the extractor with default settings.
|
| 52 |
-
|
| 53 |
-
### Primary Method: search_and_extract()
|
| 54 |
-
|
| 55 |
-
```python
|
| 56 |
-
def search_and_extract(self, narrator_name: str) -> NarratorInfo:
|
| 57 |
-
"""
|
| 58 |
-
Main method for LangGraph nodes: searches for narrator and extracts information.
|
| 59 |
-
|
| 60 |
-
Args:
|
| 61 |
-
narrator_name: Name of the narrator to search for
|
| 62 |
-
|
| 63 |
-
Returns:
|
| 64 |
-
NarratorInfo object containing all extracted information
|
| 65 |
-
"""
|
| 66 |
-
```
|
| 67 |
-
|
| 68 |
-
**Example Usage:**
|
| 69 |
-
```python
|
| 70 |
-
extractor = ShamelaNarratorExtractor()
|
| 71 |
-
narrator_info = extractor.search_and_extract("الزهري")
|
| 72 |
-
|
| 73 |
-
print(f"Narrator: {narrator_info.narrator_name}")
|
| 74 |
-
print(f"Biographical fields: {len(narrator_info.biographical_info)}")
|
| 75 |
-
print(f"Number of scholars: {len(narrator_info.scholarly_critique)}")
|
| 76 |
-
```
|
| 77 |
-
|
| 78 |
-
### Utility Method: to_dict()
|
| 79 |
-
|
| 80 |
-
```python
|
| 81 |
-
def to_dict(self, narrator_info: NarratorInfo) -> Dict[str, Any]:
|
| 82 |
-
"""Convert NarratorInfo object to dictionary for backwards compatibility."""
|
| 83 |
-
```
|
| 84 |
-
|
| 85 |
-
Converts the structured `NarratorInfo` object to a dictionary format for easier serialization or backwards compatibility.
|
| 86 |
-
|
| 87 |
-
## LangGraph Integration
|
| 88 |
-
|
| 89 |
-
### Basic Node Implementation
|
| 90 |
-
|
| 91 |
-
```python
|
| 92 |
-
def narrator_extraction_node(state: GraphState) -> GraphState:
|
| 93 |
-
"""LangGraph node that extracts narrator information."""
|
| 94 |
-
try:
|
| 95 |
-
extractor = ShamelaNarratorExtractor()
|
| 96 |
-
narrator_name = state.get("narrator_name", "")
|
| 97 |
-
|
| 98 |
-
if not narrator_name:
|
| 99 |
-
return {**state, "error": "No narrator name provided"}
|
| 100 |
-
|
| 101 |
-
narrator_info = extractor.search_and_extract(narrator_name)
|
| 102 |
-
narrator_dict = extractor.to_dict(narrator_info)
|
| 103 |
-
|
| 104 |
-
return {
|
| 105 |
-
**state,
|
| 106 |
-
"narrator_info": narrator_dict,
|
| 107 |
-
"processed": True
|
| 108 |
-
}
|
| 109 |
-
except Exception as e:
|
| 110 |
-
return {**state, "error": str(e)}
|
| 111 |
-
```
|
| 112 |
-
|
| 113 |
-
### State Schema Example
|
| 114 |
-
|
| 115 |
-
```python
|
| 116 |
-
class GraphState(TypedDict):
|
| 117 |
-
narrator_name: str
|
| 118 |
-
narrator_info: Dict[str, Any]
|
| 119 |
-
error: str
|
| 120 |
-
processed: bool
|
| 121 |
-
```
|
| 122 |
-
|
| 123 |
-
## Error Handling
|
| 124 |
-
|
| 125 |
-
The class provides comprehensive error handling:
|
| 126 |
-
|
| 127 |
-
1. **Search Errors**: When no Shamela.ws pages are found
|
| 128 |
-
2. **Network Errors**: When requests fail
|
| 129 |
-
3. **Parsing Errors**: When HTML structure is unexpected
|
| 130 |
-
4. **General Errors**: Catches all other exceptions
|
| 131 |
-
|
| 132 |
-
Errors are reported in the `extraction_metadata` field:
|
| 133 |
-
|
| 134 |
-
```python
|
| 135 |
-
narrator_info = extractor.search_and_extract("invalid_name")
|
| 136 |
-
if "error" in narrator_info.extraction_metadata:
|
| 137 |
-
print(f"Error: {narrator_info.extraction_metadata['error']}")
|
| 138 |
-
```
|
| 139 |
-
|
| 140 |
-
## Extracted Information Types
|
| 141 |
-
|
| 142 |
-
### Biographical Information
|
| 143 |
-
- **الاسم** (Name)
|
| 144 |
-
- **الكنية** (Kunya/Nickname)
|
| 145 |
-
- **النسب** (Lineage)
|
| 146 |
-
- **بلد** (Country/Place)
|
| 147 |
-
- **تاريخ** (Date/Timeline)
|
| 148 |
-
- **طبقة** (Generation/Class)
|
| 149 |
-
- **علاقات** (Relationships)
|
| 150 |
-
|
| 151 |
-
### Scholarly Critique
|
| 152 |
-
- **Scholar Names**: Names of the critics (العجلي، ابن حبان، النسائي، etc.)
|
| 153 |
-
- **Comments**: Their evaluations and comments
|
| 154 |
-
- **Highlighted Terms**: Important terms marked in the original text
|
| 155 |
-
|
| 156 |
-
### Metadata
|
| 157 |
-
- **total_scholars**: Number of different scholars who commented
|
| 158 |
-
- **biographical_fields**: Number of biographical data points found
|
| 159 |
-
- **has_critique_section**: Whether scholarly critique section exists
|
| 160 |
-
- **total_comments**: Total number of individual comments
|
| 161 |
-
|
| 162 |
-
## Performance Considerations
|
| 163 |
-
|
| 164 |
-
- **Caching**: Consider implementing caching for repeated searches
|
| 165 |
-
- **Rate Limiting**: Google search has rate limits; implement delays if needed
|
| 166 |
-
- **Timeout**: Default timeout is 10 seconds for HTTP requests
|
| 167 |
-
- **Memory**: Large HTML pages may use significant memory
|
| 168 |
-
|
| 169 |
-
## Legacy Compatibility
|
| 170 |
-
|
| 171 |
-
The following legacy functions are maintained for backwards compatibility:
|
| 172 |
-
|
| 173 |
-
```python
|
| 174 |
-
# Legacy functions (use ShamelaNarratorExtractor instead)
|
| 175 |
-
extract_narrator_info(html_content: str) -> Dict[str, Any]
|
| 176 |
-
clean_biographical_info(bio_info: Dict[str, str]) -> Dict[str, str]
|
| 177 |
-
extract_narrator_info_enhanced(html_content: str) -> Dict[str, Any]
|
| 178 |
-
get_shamela_content(query: str) -> str
|
| 179 |
-
```
|
| 180 |
-
|
| 181 |
-
## Dependencies
|
| 182 |
-
|
| 183 |
-
```bash
|
| 184 |
-
pip install beautifulsoup4 requests googlesearch-python
|
| 185 |
-
```
|
| 186 |
-
|
| 187 |
-
## Example Complete Workflow
|
| 188 |
-
|
| 189 |
-
```python
|
| 190 |
-
from scrape_shamela import ShamelaNarratorExtractor
|
| 191 |
-
|
| 192 |
-
# Initialize extractor
|
| 193 |
-
extractor = ShamelaNarratorExtractor()
|
| 194 |
-
|
| 195 |
-
# Extract information
|
| 196 |
-
narrator_info = extractor.search_and_extract("أبو هريرة")
|
| 197 |
-
|
| 198 |
-
# Check for errors
|
| 199 |
-
if "error" in narrator_info.extraction_metadata:
|
| 200 |
-
print(f"Error: {narrator_info.extraction_metadata['error']}")
|
| 201 |
-
else:
|
| 202 |
-
# Access structured data
|
| 203 |
-
print(f"Name: {narrator_info.narrator_name}")
|
| 204 |
-
|
| 205 |
-
# Biographical info
|
| 206 |
-
for key, value in narrator_info.biographical_info.items():
|
| 207 |
-
print(f"{key}: {value}")
|
| 208 |
-
|
| 209 |
-
# Scholarly critiques
|
| 210 |
-
for critique in narrator_info.scholarly_critique:
|
| 211 |
-
print(f"Scholar: {critique.scholar}")
|
| 212 |
-
for comment in critique.comments:
|
| 213 |
-
print(f" Comment: {comment.text}")
|
| 214 |
-
if comment.highlighted:
|
| 215 |
-
print(f" Highlighted: {comment.highlighted}")
|
| 216 |
-
|
| 217 |
-
# Convert to dict if needed
|
| 218 |
-
narrator_dict = extractor.to_dict(narrator_info)
|
| 219 |
-
```
|
| 220 |
-
|
| 221 |
-
## Best Practices
|
| 222 |
-
|
| 223 |
-
1. **Always check for errors** in the extraction_metadata
|
| 224 |
-
2. **Use type hints** when working with the data structures
|
| 225 |
-
3. **Handle network timeouts** gracefully
|
| 226 |
-
4. **Cache results** when possible to avoid repeated searches
|
| 227 |
-
5. **Validate narrator names** before searching
|
| 228 |
-
6. **Use the structured objects** rather than legacy dictionary format when possible
|
| 229 |
-
|
| 230 |
-
## Troubleshooting
|
| 231 |
-
|
| 232 |
-
### Common Issues
|
| 233 |
-
|
| 234 |
-
1. **No search results**: Verify narrator name spelling and existence on Shamela.ws
|
| 235 |
-
2. **Network timeouts**: Check internet connection and consider increasing timeout
|
| 236 |
-
3. **Parsing errors**: Shamela.ws may have changed their HTML structure
|
| 237 |
-
4. **Rate limiting**: Implement delays between multiple searches
|
| 238 |
-
|
| 239 |
-
### Debug Mode
|
| 240 |
-
|
| 241 |
-
To enable debugging, modify the search method to print more information:
|
| 242 |
-
|
| 243 |
-
```python
|
| 244 |
-
# Add this after search_results in _get_shamela_content method
|
| 245 |
-
print(f"Found {len(search_results)} results: {search_results}")
|
| 246 |
-
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
test_api.py → tests/test_api.py
RENAMED
|
File without changes
|
test_enhanced_narrator_agent.py → tests/test_enhanced_narrator_agent.py
RENAMED
|
@@ -12,7 +12,7 @@ from pathlib import Path
|
|
| 12 |
project_root = Path(__file__).parent
|
| 13 |
sys.path.insert(0, str(project_root))
|
| 14 |
|
| 15 |
-
from services import LLMService
|
| 16 |
from pprint import pprint
|
| 17 |
|
| 18 |
|
|
|
|
| 12 |
project_root = Path(__file__).parent
|
| 13 |
sys.path.insert(0, str(project_root))
|
| 14 |
|
| 15 |
+
from app.agent.services import LLMService
|
| 16 |
from pprint import pprint
|
| 17 |
|
| 18 |
|