Both sites were rebuilt and their old endpoints now 404:
- AYM moved to a single-page app backed by a JSON API
(POST /api/core/public/search, kararTipi NormDenetimi/BireyselBasvuru;
full text via {id, size:1} -> "icerik" HTML). Old /Ara, /ND/, /BB/ gone.
- Uyuşmazlık moved to ASP.NET WebForms (viewstate postback to /, GridView
results, decisions served as /Uploads/{EsasNo}.pdf). Old /Arama/Search gone.
Changes:
- New anayasa_mcp_module/api_client.py: shared KBB JSON client, base64url
document-id codec, HTML->Markdown + HTML text stripping helpers.
- Rewrite anayasa client/bireysel_client/unified_client over the new API,
preserving public method names and response models.
- Rewrite uyusmazlik client for the postback flow + GridView parse + PDF
document conversion; simplify request model to text + scope + paging.
- Trim search_anayasa_unified and search_uyusmazlik_decisions tool signatures
to parameters the new APIs actually support; drop dead enums.
Verified live via FastMCP client: search + document retrieval work for AYM
norm/bireysel and Uyuşmazlık.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
119 lines
5.1 KiB
Python
119 lines
5.1 KiB
Python
# anayasa_mcp_module/unified_client.py
|
|
# Unified client for both Norm Denetimi and Bireysel Başvuru, backed by the new
|
|
# KBB JSON API. Routing between the two is by the "decision_type" discriminator
|
|
# on search, and by the document URL (?type=...) on document retrieval.
|
|
|
|
import logging
|
|
from typing import Optional, Tuple
|
|
|
|
from .models import (
|
|
AnayasaUnifiedSearchRequest,
|
|
AnayasaUnifiedSearchResult,
|
|
AnayasaUnifiedDocumentMarkdown,
|
|
AnayasaNormDenetimiSearchRequest,
|
|
AnayasaBireyselReportSearchRequest,
|
|
)
|
|
from .client import AnayasaMahkemesiApiClient
|
|
from .bireysel_client import AnayasaBireyselBasvuruApiClient
|
|
from .api_client import (
|
|
KARAR_TIPI_NORM,
|
|
KARAR_TIPI_BIREYSEL,
|
|
parse_document_url,
|
|
)
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def normalize_anayasa_document_url(document_url: str) -> Tuple[Optional[str], str]:
|
|
"""Detect the AYM decision type from a document URL.
|
|
|
|
Returns ``(decision_type, document_url)`` where ``decision_type`` is
|
|
``"norm_denetimi"``, ``"bireysel_basvuru"``, or ``None`` if it cannot be
|
|
determined. The URL is returned unchanged (kept for backwards compatibility
|
|
with callers that expect a possibly-normalized URL).
|
|
"""
|
|
karar_tipi, _ = parse_document_url(document_url)
|
|
if karar_tipi == KARAR_TIPI_NORM:
|
|
return "norm_denetimi", document_url
|
|
if karar_tipi == KARAR_TIPI_BIREYSEL:
|
|
return "bireysel_basvuru", document_url
|
|
return None, document_url
|
|
|
|
|
|
class AnayasaUnifiedClient:
|
|
"""Unified client that handles both Norm Denetimi and Bireysel Başvuru searches."""
|
|
|
|
def __init__(self, request_timeout: float = 60.0):
|
|
self.norm_client = AnayasaMahkemesiApiClient(request_timeout)
|
|
self.bireysel_client = AnayasaBireyselBasvuruApiClient(request_timeout)
|
|
|
|
async def search_unified(self, params: AnayasaUnifiedSearchRequest) -> AnayasaUnifiedSearchResult:
|
|
"""Unified search that routes to the appropriate client based on decision_type."""
|
|
|
|
if params.decision_type == "norm_denetimi":
|
|
norm_params = AnayasaNormDenetimiSearchRequest(
|
|
keywords_all=params.keywords_all or params.keywords,
|
|
keywords_any=params.keywords_any,
|
|
page_to_fetch=params.page_to_fetch,
|
|
results_per_page=params.results_per_page,
|
|
)
|
|
result = await self.norm_client.search_norm_denetimi_decisions(norm_params)
|
|
|
|
return AnayasaUnifiedSearchResult(
|
|
decision_type="norm_denetimi",
|
|
decisions=[d.model_dump() for d in result.decisions],
|
|
total_records_found=result.total_records_found,
|
|
retrieved_page_number=result.retrieved_page_number,
|
|
)
|
|
|
|
elif params.decision_type == "bireysel_basvuru":
|
|
bireysel_params = AnayasaBireyselReportSearchRequest(
|
|
keywords=params.keywords or params.keywords_all,
|
|
page_to_fetch=params.page_to_fetch,
|
|
results_per_page=params.results_per_page,
|
|
)
|
|
result = await self.bireysel_client.search_bireysel_basvuru_report(bireysel_params)
|
|
|
|
return AnayasaUnifiedSearchResult(
|
|
decision_type="bireysel_basvuru",
|
|
decisions=[d.model_dump() for d in result.decisions],
|
|
total_records_found=result.total_records_found,
|
|
retrieved_page_number=result.retrieved_page_number,
|
|
)
|
|
|
|
raise ValueError(f"Unsupported decision type: {params.decision_type}")
|
|
|
|
async def get_document_unified(self, document_url: str, page_number: int = 1) -> AnayasaUnifiedDocumentMarkdown:
|
|
"""Unified document retrieval that auto-detects the decision type from the URL."""
|
|
|
|
decision_type, _ = normalize_anayasa_document_url(document_url)
|
|
|
|
if decision_type == "bireysel_basvuru":
|
|
result = await self.bireysel_client.get_decision_document_as_markdown(document_url, page_number)
|
|
return AnayasaUnifiedDocumentMarkdown(
|
|
decision_type="bireysel_basvuru",
|
|
source_url=result.source_url,
|
|
document_data=result.model_dump(mode="json"),
|
|
markdown_chunk=result.markdown_chunk,
|
|
current_page=result.current_page,
|
|
total_pages=result.total_pages,
|
|
is_paginated=result.is_paginated,
|
|
)
|
|
|
|
# Default to norm_denetimi (also covers explicit norm_denetimi detection).
|
|
result = await self.norm_client.get_decision_document_as_markdown(document_url, page_number)
|
|
return AnayasaUnifiedDocumentMarkdown(
|
|
decision_type="norm_denetimi",
|
|
source_url=result.source_url,
|
|
document_data=result.model_dump(mode="json"),
|
|
markdown_chunk=result.markdown_chunk,
|
|
current_page=result.current_page,
|
|
total_pages=result.total_pages,
|
|
is_paginated=result.is_paginated,
|
|
)
|
|
|
|
async def close_client_session(self):
|
|
"""Close both client sessions."""
|
|
await self.norm_client.close_client_session()
|
|
await self.bireysel_client.close_client_session()
|