feat: Add Sigorta Tahkim Komisyonu MCP module (3 tools)
Add Insurance Arbitration Commission integration with Tavily search and direct PDF download for 64 quarterly journal issues (2010-2025). Tools: - search_sigorta_tahkim_decisions: Search via Tavily API - get_sigorta_tahkim_document_markdown: PDF download + paginated markdown - search_within_sigorta_tahkim_issue: Keyword search within individual decisions of a journal issue, with Turkish İ/I case folding support Total tools: 25 (was 22)
This commit is contained in:
+167
-1
@@ -329,6 +329,12 @@ from bddk_mcp_module.models import (
|
||||
BddkSearchRequest
|
||||
)
|
||||
|
||||
# Sigorta Tahkim Module Imports
|
||||
from sigorta_tahkim_mcp_module.client import SigortaTahkimApiClient
|
||||
from sigorta_tahkim_mcp_module.models import (
|
||||
SigortaTahkimSearchRequest
|
||||
)
|
||||
|
||||
|
||||
# Create a placeholder app that will be properly initialized after tools are defined
|
||||
|
||||
@@ -355,6 +361,7 @@ sayistay_client_instance = SayistayApiClient()
|
||||
sayistay_unified_client_instance = SayistayUnifiedClient()
|
||||
kvkk_client_instance = KvkkApiClient()
|
||||
bddk_client_instance = BddkApiClient()
|
||||
sigorta_tahkim_client_instance = SigortaTahkimApiClient()
|
||||
|
||||
# Health check client (singleton for reuse)
|
||||
_health_check_client: Optional[httpx.AsyncClient] = None
|
||||
@@ -1702,7 +1709,8 @@ def perform_cleanup():
|
||||
globals().get('sayistay_client_instance'),
|
||||
globals().get('sayistay_unified_client_instance'),
|
||||
globals().get('kvkk_client_instance'),
|
||||
globals().get('bddk_client_instance')
|
||||
globals().get('bddk_client_instance'),
|
||||
globals().get('sigorta_tahkim_client_instance')
|
||||
]
|
||||
async def close_all_clients_async():
|
||||
tasks = []
|
||||
@@ -2104,6 +2112,164 @@ async def get_bddk_document_markdown(
|
||||
"error": str(e)
|
||||
}
|
||||
|
||||
# --- MCP Tools for Sigorta Tahkim Komisyonu (Insurance Arbitration Commission) ---
|
||||
@app.tool(
|
||||
description="Search Sigorta Tahkim Komisyonu (Insurance Arbitration Commission) decisions from Hakem Karar Dergisi journals (64 issues, 2010-2025). Covers insurance disputes: traffic, health, fire, DASK, life insurance.",
|
||||
annotations={
|
||||
"readOnlyHint": True,
|
||||
"openWorldHint": True,
|
||||
"idempotentHint": True
|
||||
}
|
||||
)
|
||||
async def search_sigorta_tahkim_decisions(
|
||||
keywords: str = Field(..., description="Search keywords in Turkish (e.g., 'trafik sigortası', 'kasko', 'DASK')"),
|
||||
page: int = Field(1, ge=1, description="Page number")
|
||||
) -> dict:
|
||||
"""Search Sigorta Tahkim Komisyonu insurance arbitration decisions."""
|
||||
logger.info(f"Sigorta Tahkim search tool called with keywords: {keywords}, page: {page}")
|
||||
|
||||
pageSize = 10
|
||||
|
||||
try:
|
||||
search_request = SigortaTahkimSearchRequest(
|
||||
keywords=keywords,
|
||||
page=page,
|
||||
pageSize=pageSize
|
||||
)
|
||||
|
||||
result = await sigorta_tahkim_client_instance.search_decisions(search_request)
|
||||
logger.info(f"Sigorta Tahkim search completed. Found {len(result.decisions)} results on page {page}")
|
||||
|
||||
return {
|
||||
"decisions": [
|
||||
{
|
||||
"title": dec.title,
|
||||
"document_id": dec.document_id,
|
||||
"content": dec.content,
|
||||
"url": dec.url
|
||||
}
|
||||
for dec in result.decisions
|
||||
],
|
||||
"total_results": result.total_results,
|
||||
"page": result.page,
|
||||
"pageSize": result.pageSize
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.exception(f"Error searching Sigorta Tahkim decisions: {e}")
|
||||
return {
|
||||
"decisions": [],
|
||||
"total_results": 0,
|
||||
"page": page,
|
||||
"pageSize": pageSize,
|
||||
"error": str(e)
|
||||
}
|
||||
|
||||
@app.tool(
|
||||
description="Retrieve full PDF content of a Sigorta Tahkim Komisyonu Hakem Karar Dergisi issue by number. Returns paginated Markdown. Issues 1-64 available (2010-2025).",
|
||||
annotations={
|
||||
"readOnlyHint": True,
|
||||
"openWorldHint": False,
|
||||
"idempotentHint": True
|
||||
}
|
||||
)
|
||||
async def get_sigorta_tahkim_document_markdown(
|
||||
issue_number: str = Field(..., description="Journal issue number (1-64, e.g., '64')"),
|
||||
page_number: int = Field(1, ge=1, description="Page number for paginated content")
|
||||
) -> dict:
|
||||
"""Retrieve Sigorta Tahkim journal issue PDF as paginated Markdown."""
|
||||
logger.info(f"Sigorta Tahkim document retrieval for issue: {issue_number}, page: {page_number}")
|
||||
|
||||
if not issue_number or not issue_number.strip():
|
||||
return {
|
||||
"document_id": issue_number,
|
||||
"markdown_content": "",
|
||||
"page_number": page_number,
|
||||
"total_pages": 0,
|
||||
"source_url": "",
|
||||
"error": "Issue number is required"
|
||||
}
|
||||
|
||||
try:
|
||||
result = await sigorta_tahkim_client_instance.get_document_markdown(issue_number, page_number)
|
||||
logger.info(f"Sigorta Tahkim document retrieved. Page {result.page_number}/{result.total_pages}")
|
||||
|
||||
return {
|
||||
"document_id": result.document_id,
|
||||
"markdown_content": result.markdown_content,
|
||||
"page_number": result.page_number,
|
||||
"total_pages": result.total_pages,
|
||||
"source_url": result.source_url
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.exception(f"Error retrieving Sigorta Tahkim document: {e}")
|
||||
return {
|
||||
"document_id": issue_number,
|
||||
"markdown_content": "",
|
||||
"page_number": page_number,
|
||||
"total_pages": 0,
|
||||
"source_url": "",
|
||||
"error": str(e)
|
||||
}
|
||||
|
||||
@app.tool(
|
||||
description="Search within a specific Sigorta Tahkim Komisyonu journal issue for keywords. Downloads the PDF, splits into individual decisions, and returns matching decisions with excerpts sorted by relevance.",
|
||||
annotations={
|
||||
"readOnlyHint": True,
|
||||
"openWorldHint": False,
|
||||
"idempotentHint": True
|
||||
}
|
||||
)
|
||||
async def search_within_sigorta_tahkim_issue(
|
||||
issue_number: str = Field(..., description="Journal issue number (1-64, e.g., '64')"),
|
||||
keyword: str = Field(..., description="Search keyword in Turkish (e.g., 'trafik kazası', 'tazminat')"),
|
||||
max_results: int = Field(10, ge=1, le=25, description="Max matching decisions to return")
|
||||
) -> dict:
|
||||
"""Search for keywords within a specific Sigorta Tahkim journal issue's decisions."""
|
||||
logger.info(f"Sigorta Tahkim search_within called: issue={issue_number}, keyword={keyword}")
|
||||
|
||||
if not issue_number or not issue_number.strip():
|
||||
return {"issue_number": issue_number, "keyword": keyword, "matches": [], "error": "Issue number is required"}
|
||||
if not keyword or not keyword.strip():
|
||||
return {"issue_number": issue_number, "keyword": keyword, "matches": [], "error": "Keyword is required"}
|
||||
|
||||
try:
|
||||
result = await sigorta_tahkim_client_instance.search_within_issue(
|
||||
issue_number, keyword, max_results
|
||||
)
|
||||
logger.info(
|
||||
f"Sigorta Tahkim search_within completed: "
|
||||
f"{result.matching_decisions}/{result.total_decisions} decisions match"
|
||||
)
|
||||
|
||||
return {
|
||||
"issue_number": result.issue_number,
|
||||
"keyword": result.keyword,
|
||||
"total_decisions": result.total_decisions,
|
||||
"matching_decisions": result.matching_decisions,
|
||||
"matches": [
|
||||
{
|
||||
"decision_header": m.decision_header,
|
||||
"relevance_score": m.relevance_score,
|
||||
"excerpt": m.excerpt,
|
||||
"body_length": m.body_length
|
||||
}
|
||||
for m in result.matches
|
||||
]
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.exception(f"Error in search_within Sigorta Tahkim: {e}")
|
||||
return {
|
||||
"issue_number": issue_number,
|
||||
"keyword": keyword,
|
||||
"total_decisions": 0,
|
||||
"matching_decisions": 0,
|
||||
"matches": [],
|
||||
"error": str(e)
|
||||
}
|
||||
|
||||
# --- ChatGPT Deep Research Compatible Tools ---
|
||||
|
||||
def get_preview_text(markdown_content: str, skip_chars: int = 100, preview_chars: int = 200) -> str:
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
# sigorta_tahkim_mcp_module/__init__.py
|
||||
|
||||
from .client import SigortaTahkimApiClient
|
||||
from .models import (
|
||||
SigortaTahkimSearchRequest,
|
||||
SigortaTahkimDecisionSummary,
|
||||
SigortaTahkimSearchResult,
|
||||
SigortaTahkimDocumentMarkdown,
|
||||
SigortaTahkimSearchWithinMatch,
|
||||
SigortaTahkimSearchWithinResult
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"SigortaTahkimApiClient",
|
||||
"SigortaTahkimSearchRequest",
|
||||
"SigortaTahkimDecisionSummary",
|
||||
"SigortaTahkimSearchResult",
|
||||
"SigortaTahkimDocumentMarkdown",
|
||||
"SigortaTahkimSearchWithinMatch",
|
||||
"SigortaTahkimSearchWithinResult"
|
||||
]
|
||||
@@ -0,0 +1,340 @@
|
||||
# sigorta_tahkim_mcp_module/client.py
|
||||
|
||||
import httpx
|
||||
from typing import Optional
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import io
|
||||
import math
|
||||
from markitdown import MarkItDown
|
||||
|
||||
from .models import (
|
||||
SigortaTahkimSearchRequest,
|
||||
SigortaTahkimDecisionSummary,
|
||||
SigortaTahkimSearchResult,
|
||||
SigortaTahkimDocumentMarkdown,
|
||||
SigortaTahkimSearchWithinMatch,
|
||||
SigortaTahkimSearchWithinResult
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
if not logger.hasHandlers():
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
|
||||
|
||||
# Turkish-specific lowercase: İ→i, I→ı (Python's str.lower() doesn't handle these)
|
||||
_TR_UPPER = str.maketrans("İIÇĞÖŞÜ", "iıçğöşü")
|
||||
|
||||
|
||||
def _turkish_lower(text: str) -> str:
|
||||
"""Lowercase with Turkish İ/I handling."""
|
||||
return text.translate(_TR_UPPER).lower()
|
||||
|
||||
|
||||
class SigortaTahkimApiClient:
|
||||
"""
|
||||
API client for searching and retrieving Sigorta Tahkim Komisyonu
|
||||
(Insurance Arbitration Commission) decisions using Tavily Search API
|
||||
for discovery and direct PDF download for content retrieval.
|
||||
|
||||
The commission publishes quarterly PDF journals ("Hakem Karar Dergisi")
|
||||
containing arbitration decisions. There are 64 issues spanning 2010-2025.
|
||||
"""
|
||||
|
||||
TAVILY_API_URL = "https://api.tavily.com/search"
|
||||
BASE_URL = "https://www.sigortatahkim.org"
|
||||
PDF_BASE_URL = "https://www.sigortatahkim.org/content/CmsFiles/"
|
||||
DOCUMENT_MARKDOWN_CHUNK_SIZE = 5000
|
||||
|
||||
def __init__(self, request_timeout: float = 60.0):
|
||||
"""Initialize the Sigorta Tahkim API client."""
|
||||
self.tavily_api_key = os.getenv("TAVILY_API_KEY")
|
||||
if not self.tavily_api_key:
|
||||
self.tavily_api_key = "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"
|
||||
logger.info("Using fallback Tavily API token (development token)")
|
||||
else:
|
||||
logger.info("Using Tavily API key from environment variable")
|
||||
|
||||
self.http_client = httpx.AsyncClient(
|
||||
headers={
|
||||
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36"
|
||||
},
|
||||
timeout=httpx.Timeout(request_timeout)
|
||||
)
|
||||
self.markitdown = MarkItDown()
|
||||
|
||||
async def close_client_session(self):
|
||||
"""Close the HTTP client session."""
|
||||
await self.http_client.aclose()
|
||||
logger.info("SigortaTahkimApiClient: HTTP client session closed.")
|
||||
|
||||
def _get_pdf_filename(self, issue_number: int) -> str:
|
||||
"""Get the PDF filename for a given journal issue number."""
|
||||
if issue_number == 4:
|
||||
return "karardergisisayi4.pdf"
|
||||
elif 57 <= issue_number <= 61:
|
||||
return f"revizekd{issue_number}.pdf"
|
||||
else:
|
||||
return f"karardrgs{issue_number}.pdf"
|
||||
|
||||
def _extract_issue_number(self, url: str) -> Optional[str]:
|
||||
"""Extract journal issue number from a sigortatahkim.org URL."""
|
||||
# Pattern: karardrgs{N}.pdf
|
||||
match = re.search(r'karardrgs(\d+)\.pdf', url, re.IGNORECASE)
|
||||
if match:
|
||||
return match.group(1)
|
||||
|
||||
# Pattern: revizekd{N}.pdf
|
||||
match = re.search(r'revizekd(\d+)\.pdf', url, re.IGNORECASE)
|
||||
if match:
|
||||
return match.group(1)
|
||||
|
||||
# Pattern: karardergisisayi{N}.pdf
|
||||
match = re.search(r'karardergisisayi(\d+)\.pdf', url, re.IGNORECASE)
|
||||
if match:
|
||||
return match.group(1)
|
||||
|
||||
# Pattern: sayı or sayi in URL path with number
|
||||
match = re.search(r'say[ıi]\s*[-:]?\s*(\d+)', url, re.IGNORECASE)
|
||||
if match:
|
||||
return match.group(1)
|
||||
|
||||
return None
|
||||
|
||||
async def search_decisions(
|
||||
self,
|
||||
request: SigortaTahkimSearchRequest
|
||||
) -> SigortaTahkimSearchResult:
|
||||
"""
|
||||
Search for Sigorta Tahkim Komisyonu decisions using Tavily API.
|
||||
|
||||
Args:
|
||||
request: Search request parameters
|
||||
|
||||
Returns:
|
||||
SigortaTahkimSearchResult with matching decisions
|
||||
"""
|
||||
try:
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": f"Bearer {self.tavily_api_key}"
|
||||
}
|
||||
|
||||
payload = {
|
||||
"query": request.keywords,
|
||||
"country": "turkey",
|
||||
"include_domains": ["sigortatahkim.org"],
|
||||
"max_results": request.pageSize,
|
||||
"search_depth": "advanced"
|
||||
}
|
||||
|
||||
if request.page > 1:
|
||||
logger.warning(f"Tavily API doesn't support pagination. Page {request.page} requested.")
|
||||
|
||||
response = await self.http_client.post(
|
||||
self.TAVILY_API_URL,
|
||||
json=payload,
|
||||
headers=headers
|
||||
)
|
||||
response.raise_for_status()
|
||||
|
||||
data = response.json()
|
||||
logger.info(f"Tavily returned {len(data.get('results', []))} results for Sigorta Tahkim")
|
||||
|
||||
decisions = []
|
||||
for result in data.get("results", []):
|
||||
url = result.get("url", "")
|
||||
title = result.get("title", "").strip()
|
||||
content = result.get("content", "")[:500]
|
||||
|
||||
issue_num = self._extract_issue_number(url)
|
||||
doc_id = issue_num if issue_num else url
|
||||
|
||||
decision = SigortaTahkimDecisionSummary(
|
||||
title=title,
|
||||
document_id=doc_id,
|
||||
content=content,
|
||||
url=url
|
||||
)
|
||||
decisions.append(decision)
|
||||
|
||||
return SigortaTahkimSearchResult(
|
||||
decisions=decisions,
|
||||
total_results=len(data.get("results", [])),
|
||||
page=request.page,
|
||||
pageSize=request.pageSize
|
||||
)
|
||||
|
||||
except httpx.HTTPStatusError as e:
|
||||
logger.error(f"HTTP error searching Sigorta Tahkim decisions: {e}")
|
||||
if e.response.status_code == 401:
|
||||
raise Exception("Tavily API authentication failed. Check API key.")
|
||||
raise Exception(f"Failed to search Sigorta Tahkim decisions: {str(e)}")
|
||||
except Exception as e:
|
||||
logger.error(f"Error searching Sigorta Tahkim decisions: {e}")
|
||||
raise Exception(f"Failed to search Sigorta Tahkim decisions: {str(e)}")
|
||||
|
||||
# Regex pattern to split decisions within a journal issue
|
||||
DECISION_HEADER_PATTERN = re.compile(
|
||||
r'(\d{2}\.\d{2}\.\d{4}\s+Tarih\s+ve\s+K-\d{4}/\d+\s+Sayılı\s+Hakem\s+Kararı)'
|
||||
)
|
||||
# Minimum body length to distinguish real decisions from TOC entries
|
||||
MIN_DECISION_BODY_LENGTH = 1000
|
||||
|
||||
async def _download_and_convert_pdf(self, issue_number: str) -> tuple[str, str]:
|
||||
"""
|
||||
Download a journal issue PDF and convert to markdown.
|
||||
|
||||
Returns:
|
||||
Tuple of (markdown_content, pdf_url)
|
||||
"""
|
||||
issue_num = int(issue_number)
|
||||
filename = self._get_pdf_filename(issue_num)
|
||||
pdf_url = f"{self.PDF_BASE_URL}{filename}"
|
||||
|
||||
logger.info(f"Downloading Sigorta Tahkim PDF: {pdf_url}")
|
||||
|
||||
response = await self.http_client.get(pdf_url, follow_redirects=True)
|
||||
response.raise_for_status()
|
||||
|
||||
pdf_stream = io.BytesIO(response.content)
|
||||
result = self.markitdown.convert_stream(pdf_stream, file_extension=".pdf")
|
||||
return result.text_content.strip(), pdf_url
|
||||
|
||||
def _split_into_decisions(self, markdown_content: str) -> list[tuple[str, str]]:
|
||||
"""
|
||||
Split markdown content into individual decisions.
|
||||
|
||||
Returns:
|
||||
List of (header, body) tuples for decisions with substantial content.
|
||||
"""
|
||||
parts = self.DECISION_HEADER_PATTERN.split(markdown_content)
|
||||
decisions = []
|
||||
for i in range(1, len(parts) - 1, 2):
|
||||
header = parts[i].strip()
|
||||
body = parts[i + 1].strip() if i + 1 < len(parts) else ""
|
||||
if len(body) >= self.MIN_DECISION_BODY_LENGTH:
|
||||
decisions.append((header, body))
|
||||
return decisions
|
||||
|
||||
async def get_document_markdown(
|
||||
self,
|
||||
issue_number: str,
|
||||
page_number: int = 1
|
||||
) -> SigortaTahkimDocumentMarkdown:
|
||||
"""
|
||||
Retrieve a Sigorta Tahkim journal issue PDF and convert to Markdown.
|
||||
|
||||
Args:
|
||||
issue_number: Journal issue number (e.g., '64')
|
||||
page_number: Page number for paginated content (1-indexed)
|
||||
|
||||
Returns:
|
||||
SigortaTahkimDocumentMarkdown with paginated content
|
||||
"""
|
||||
try:
|
||||
markdown_content, pdf_url = await self._download_and_convert_pdf(issue_number)
|
||||
|
||||
total_length = len(markdown_content)
|
||||
total_pages = max(1, math.ceil(total_length / self.DOCUMENT_MARKDOWN_CHUNK_SIZE))
|
||||
|
||||
start_idx = (page_number - 1) * self.DOCUMENT_MARKDOWN_CHUNK_SIZE
|
||||
end_idx = start_idx + self.DOCUMENT_MARKDOWN_CHUNK_SIZE
|
||||
page_content = markdown_content[start_idx:end_idx]
|
||||
|
||||
return SigortaTahkimDocumentMarkdown(
|
||||
document_id=issue_number,
|
||||
markdown_content=page_content,
|
||||
page_number=page_number,
|
||||
total_pages=total_pages,
|
||||
source_url=pdf_url
|
||||
)
|
||||
|
||||
except ValueError:
|
||||
raise Exception(f"Invalid issue number: {issue_number}. Must be a number (e.g., '64').")
|
||||
except httpx.HTTPStatusError as e:
|
||||
logger.error(f"HTTP error fetching Sigorta Tahkim issue {issue_number}: {e}")
|
||||
raise Exception(f"Failed to fetch journal issue {issue_number}: {str(e)}")
|
||||
except Exception as e:
|
||||
logger.error(f"Error processing Sigorta Tahkim issue {issue_number}: {e}")
|
||||
raise Exception(f"Failed to process journal issue {issue_number}: {str(e)}")
|
||||
|
||||
async def search_within_issue(
|
||||
self,
|
||||
issue_number: str,
|
||||
keyword: str,
|
||||
max_results: int = 10
|
||||
) -> SigortaTahkimSearchWithinResult:
|
||||
"""
|
||||
Search for a keyword within a specific journal issue's decisions.
|
||||
|
||||
Downloads the PDF, splits into individual decisions, and returns
|
||||
matching decisions sorted by relevance (match count).
|
||||
|
||||
Args:
|
||||
issue_number: Journal issue number (e.g., '64')
|
||||
keyword: Search keyword or phrase in Turkish
|
||||
max_results: Maximum matching decisions to return
|
||||
|
||||
Returns:
|
||||
SigortaTahkimSearchWithinResult with matching decisions
|
||||
"""
|
||||
try:
|
||||
markdown_content, _ = await self._download_and_convert_pdf(issue_number)
|
||||
decisions = self._split_into_decisions(markdown_content)
|
||||
|
||||
logger.info(
|
||||
f"Searching '{keyword}' within issue {issue_number}: "
|
||||
f"{len(decisions)} decisions found"
|
||||
)
|
||||
|
||||
keyword_lower = _turkish_lower(keyword)
|
||||
matches = []
|
||||
|
||||
for header, body in decisions:
|
||||
body_lower = _turkish_lower(body)
|
||||
count = body_lower.count(keyword_lower)
|
||||
if count == 0:
|
||||
continue
|
||||
|
||||
# Extract excerpt around the first match
|
||||
first_pos = body_lower.find(keyword_lower)
|
||||
excerpt_start = max(0, first_pos - 200)
|
||||
excerpt_end = min(len(body), first_pos + len(keyword) + 200)
|
||||
excerpt = body[excerpt_start:excerpt_end].strip()
|
||||
if excerpt_start > 0:
|
||||
excerpt = "..." + excerpt
|
||||
if excerpt_end < len(body):
|
||||
excerpt = excerpt + "..."
|
||||
|
||||
matches.append(SigortaTahkimSearchWithinMatch(
|
||||
decision_header=header,
|
||||
relevance_score=count,
|
||||
excerpt=excerpt,
|
||||
body_length=len(body)
|
||||
))
|
||||
|
||||
# Sort by relevance (highest match count first)
|
||||
matches.sort(key=lambda m: m.relevance_score, reverse=True)
|
||||
matches = matches[:max_results]
|
||||
|
||||
return SigortaTahkimSearchWithinResult(
|
||||
issue_number=issue_number,
|
||||
keyword=keyword,
|
||||
total_decisions=len(decisions),
|
||||
matching_decisions=len(matches),
|
||||
matches=matches
|
||||
)
|
||||
|
||||
except ValueError:
|
||||
raise Exception(f"Invalid issue number: {issue_number}. Must be a number (e.g., '64').")
|
||||
except httpx.HTTPStatusError as e:
|
||||
logger.error(f"HTTP error in search_within issue {issue_number}: {e}")
|
||||
raise Exception(f"Failed to fetch journal issue {issue_number}: {str(e)}")
|
||||
except Exception as e:
|
||||
logger.error(f"Error in search_within issue {issue_number}: {e}")
|
||||
raise Exception(f"Failed to search within issue {issue_number}: {str(e)}")
|
||||
@@ -0,0 +1,59 @@
|
||||
# sigorta_tahkim_mcp_module/models.py
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import List
|
||||
|
||||
|
||||
class SigortaTahkimSearchRequest(BaseModel):
|
||||
"""Request model for searching Sigorta Tahkim Komisyonu decisions via Tavily API."""
|
||||
keywords: str = Field(..., description="Search keywords in Turkish")
|
||||
page: int = Field(1, ge=1, description="Page number (1-indexed)")
|
||||
pageSize: int = Field(10, ge=1, le=50, description="Results per page (1-50)")
|
||||
|
||||
|
||||
class SigortaTahkimDecisionSummary(BaseModel):
|
||||
"""Summary of a Sigorta Tahkim decision from search results."""
|
||||
title: str = Field(..., description="Decision title or journal issue info")
|
||||
document_id: str = Field(..., description="Journal issue number (e.g., '64')")
|
||||
content: str = Field(..., description="Decision summary/excerpt")
|
||||
url: str = Field("", description="Source URL")
|
||||
|
||||
|
||||
class SigortaTahkimSearchResult(BaseModel):
|
||||
"""Response model for Sigorta Tahkim decision search results."""
|
||||
decisions: List[SigortaTahkimDecisionSummary] = Field(
|
||||
default_factory=list,
|
||||
description="List of matching decisions"
|
||||
)
|
||||
total_results: int = Field(0, description="Total number of results")
|
||||
page: int = Field(1, description="Current page number")
|
||||
pageSize: int = Field(10, description="Results per page")
|
||||
|
||||
|
||||
class SigortaTahkimDocumentMarkdown(BaseModel):
|
||||
"""Sigorta Tahkim journal issue converted to Markdown format."""
|
||||
document_id: str = Field(..., description="Journal issue number")
|
||||
markdown_content: str = Field("", description="Document content in Markdown")
|
||||
page_number: int = Field(1, description="Current page number")
|
||||
total_pages: int = Field(1, description="Total number of pages")
|
||||
source_url: str = Field("", description="PDF source URL")
|
||||
|
||||
|
||||
class SigortaTahkimSearchWithinMatch(BaseModel):
|
||||
"""A single matching decision from search within a journal issue."""
|
||||
decision_header: str = Field(..., description="Decision header (date and K-number)")
|
||||
relevance_score: int = Field(0, description="Number of keyword matches")
|
||||
excerpt: str = Field("", description="Matching excerpt with context")
|
||||
body_length: int = Field(0, description="Full decision body length in chars")
|
||||
|
||||
|
||||
class SigortaTahkimSearchWithinResult(BaseModel):
|
||||
"""Response model for search within a journal issue."""
|
||||
issue_number: str = Field(..., description="Journal issue number searched")
|
||||
keyword: str = Field("", description="Search keyword used")
|
||||
total_decisions: int = Field(0, description="Total decisions in issue")
|
||||
matching_decisions: int = Field(0, description="Number of matching decisions")
|
||||
matches: List[SigortaTahkimSearchWithinMatch] = Field(
|
||||
default_factory=list,
|
||||
description="List of matching decisions sorted by relevance"
|
||||
)
|
||||
Reference in New Issue
Block a user