optimize token usage

This commit is contained in:
saidsurucu
2025-07-14 15:45:19 +03:00
parent 54e8d61f83
commit e34d81be26
11 changed files with 489 additions and 277 deletions
+20
View File
@@ -192,3 +192,23 @@ scripts/deploy-flyio.sh
docs/DEPLOYMENT_FLYIO.md
setup_jwt_template.py
mcp_server_main.py.backup
mcp_overhead_content.json
ANTHROPIC_TEST_README.md
extract_mcp_overhead.py
mcp_overhead_content.txt
mcp_overhead_summary.txt
run_http_server.py
run_local_test.py
# MCP overhead analysis files
mcp_overhead_*.json
mcp_overhead_*.txt
mcp_test_results_*.json
mcp_quick_test_*.json
# General text files (temporary notes, etc)
*.txt
analyze_playwright_mcp.py
measure_mcp_directly.py
playwright_mcp_overhead.json
simple_test.py
+49 -49
View File
@@ -90,59 +90,59 @@ class AnayasaNormDenetimiSearchRequest(BaseModel):
keywords_any: Optional[List[str]] = Field(default_factory=list, description="Keywords for OR logic (HerhangiBirKelimeAra[]).")
keywords_exclude: Optional[List[str]] = Field(default_factory=list, description="Keywords to exclude (BulunmayanKelimeAra[]).")
period: Optional[AnayasaDonemEnum] = Field(default=AnayasaDonemEnum.TUMU, description="Constitutional period (Donemler_id).")
case_number_esas: Optional[str] = Field(None, description="Case registry number (EsasNo), e.g., '2023/123'.")
decision_number_karar: Optional[str] = Field(None, description="Decision number (KararNo), e.g., '2023/456'.")
first_review_date_start: Optional[str] = Field(None, description="First review start date (IlkIncelemeTarihiIlk), format DD/MM/YYYY.")
first_review_date_end: Optional[str] = Field(None, description="First review end date (IlkIncelemeTarihiSon), format DD/MM/YYYY.")
decision_date_start: Optional[str] = Field(None, description="Decision start date (KararTarihiIlk), format DD/MM/YYYY.")
decision_date_end: Optional[str] = Field(None, description="Decision end date (KararTarihiSon), format DD/MM/YYYY.")
case_number_esas: str = Field("", description="Case registry number (EsasNo), e.g., '2023/123'.")
decision_number_karar: str = Field("", description="Decision number (KararNo), e.g., '2023/456'.")
first_review_date_start: str = Field("", description="First review start date (IlkIncelemeTarihiIlk), format DD/MM/YYYY.")
first_review_date_end: str = Field("", description="First review end date (IlkIncelemeTarihiSon), format DD/MM/YYYY.")
decision_date_start: str = Field("", description="Decision start date (KararTarihiIlk), format DD/MM/YYYY.")
decision_date_end: str = Field("", description="Decision end date (KararTarihiSon), format DD/MM/YYYY.")
application_type: Optional[AnayasaBasvuruTuruEnum] = Field(default=AnayasaBasvuruTuruEnum.TUMU, description="Type of application (BasvuruTurler_id).")
applicant_general_name: Optional[str] = Field(None, description="General applicant name (BasvuranGeneller_id).")
applicant_specific_name: Optional[str] = Field(None, description="Specific applicant name (BasvuranOzeller_id).")
official_gazette_date_start: Optional[str] = Field(None, description="Official Gazette start date (ResmiGazeteTarihiIlk), format DD/MM/YYYY.")
official_gazette_date_end: Optional[str] = Field(None, description="Official Gazette end date (ResmiGazeteTarihiSon), format DD/MM/YYYY.")
official_gazette_number_start: Optional[str] = Field(None, description="Official Gazette starting number (ResmiGazeteSayisiIlk).")
official_gazette_number_end: Optional[str] = Field(None, description="Official Gazette ending number (ResmiGazeteSayisiSon).")
applicant_general_name: str = Field("", description="General applicant name (BasvuranGeneller_id).")
applicant_specific_name: str = Field("", description="Specific applicant name (BasvuranOzeller_id).")
official_gazette_date_start: str = Field("", description="Official Gazette start date (ResmiGazeteTarihiIlk), format DD/MM/YYYY.")
official_gazette_date_end: str = Field("", description="Official Gazette end date (ResmiGazeteTarihiSon), format DD/MM/YYYY.")
official_gazette_number_start: str = Field("", description="Official Gazette starting number (ResmiGazeteSayisiIlk).")
official_gazette_number_end: str = Field("", description="Official Gazette ending number (ResmiGazeteSayisiSon).")
has_press_release: Optional[AnayasaVarYokEnum] = Field(default=AnayasaVarYokEnum.TUMU, description="Press release available (BasinDuyurusu).")
has_dissenting_opinion: Optional[AnayasaVarYokEnum] = Field(default=AnayasaVarYokEnum.TUMU, description="Dissenting opinion exists (KarsiOy).")
has_different_reasoning: Optional[AnayasaVarYokEnum] = Field(default=AnayasaVarYokEnum.TUMU, description="Different reasoning exists (FarkliGerekce).")
attending_members_names: Optional[List[str]] = Field(default_factory=list, description="List of attending members' exact names (Uyeler_id[]).")
rapporteur_name: Optional[str] = Field(None, description="Rapporteur's exact name (Raportorler_id).")
rapporteur_name: str = Field("", description="Rapporteur's exact name (Raportorler_id).")
norm_type: Optional[AnayasaNormTuruEnum] = Field(default=AnayasaNormTuruEnum.TUMU, description="Type of the reviewed norm (NormunTurler_id).")
norm_id_or_name: Optional[str] = Field(None, description="Number or name of the norm (NormunNumarasiAdlar_id).")
norm_article: Optional[str] = Field(None, description="Article number of the norm (NormunMaddeNumarasi).")
norm_id_or_name: str = Field("", description="Number or name of the norm (NormunNumarasiAdlar_id).")
norm_article: str = Field("", description="Article number of the norm (NormunMaddeNumarasi).")
review_outcomes: Optional[List[AnayasaIncelemeSonucuEnum]] = Field(default_factory=list, description="List of review types and outcomes (IncelemeTuruKararSonuclar_id[]).")
reason_for_final_outcome: Optional[AnayasaSonucGerekcesiEnum] = Field(default=AnayasaSonucGerekcesiEnum.TUMU, description="Main reason for the decision outcome (KararSonucununGerekcesi).")
basis_constitution_article_numbers: Optional[List[str]] = Field(default_factory=list, description="List of supporting Constitution article numbers (DayanakHukmu[]).")
results_per_page: Optional[int] = Field(10, ge=1, le=10, description="Results per page.")
page_to_fetch: Optional[int] = Field(1, ge=1, description="Page number to fetch for results list.")
sort_by_criteria: Optional[str] = Field("KararTarihi", description="Sort criteria. Options: 'KararTarihi', 'YayinTarihi', 'Toplam' (keyword count).")
results_per_page: int = Field(10, ge=1, le=10, description="Results per page.")
page_to_fetch: int = Field(1, ge=1, description="Page number to fetch for results list.")
sort_by_criteria: str = Field("KararTarihi", description="Sort criteria. Options: 'KararTarihi', 'YayinTarihi', 'Toplam' (keyword count).")
class AnayasaReviewedNormInfo(BaseModel):
"""Details of a norm reviewed within an AYM decision summary."""
norm_name_or_number: Optional[str] = None
article_number: Optional[str] = None
review_type_and_outcome: Optional[str] = None
outcome_reason: Optional[str] = None
norm_name_or_number: str = Field("", description="Norm name or number")
article_number: str = Field("", description="Article number")
review_type_and_outcome: str = Field("", description="Review type and outcome")
outcome_reason: str = Field("", description="Outcome reason")
basis_constitution_articles_cited: List[str] = Field(default_factory=list)
postponement_period: Optional[str] = None
postponement_period: str = Field("", description="Postponement period")
class AnayasaDecisionSummary(BaseModel):
"""Model for a single Anayasa Mahkemesi (Norm Denetimi) decision summary from search results."""
decision_reference_no: Optional[str] = None
decision_page_url: Optional[HttpUrl] = None
keywords_found_count: Optional[int] = None
application_type_summary: Optional[str] = None
applicant_summary: Optional[str] = None
decision_outcome_summary: Optional[str] = None
decision_date_summary: Optional[str] = None
decision_reference_no: str = Field("", description="Decision reference number")
decision_page_url: str = Field("", description="Decision page URL")
keywords_found_count: int = Field(0, description="Keywords found count")
application_type_summary: str = Field("", description="Application type summary")
applicant_summary: str = Field("", description="Applicant summary")
decision_outcome_summary: str = Field("", description="Decision outcome summary")
decision_date_summary: str = Field("", description="Decision date summary")
reviewed_norms: List[AnayasaReviewedNormInfo] = Field(default_factory=list)
class AnayasaSearchResult(BaseModel):
"""Model for the overall search result for Anayasa Mahkemesi Norm Denetimi decisions."""
decisions: List[AnayasaDecisionSummary]
total_records_found: Optional[int] = None
retrieved_page_number: Optional[int] = None
total_records_found: int = Field(0, description="Total records found")
retrieved_page_number: int = Field(1, description="Retrieved page number")
class AnayasaDocumentMarkdown(BaseModel):
"""
@@ -150,10 +150,10 @@ class AnayasaDocumentMarkdown(BaseModel):
and pagination information.
"""
source_url: HttpUrl
decision_reference_no_from_page: Optional[str] = Field(None, description="E.K. No parsed from the document page.")
decision_date_from_page: Optional[str] = Field(None, description="Decision date parsed from the document page.")
official_gazette_info_from_page: Optional[str] = Field(None, description="Official Gazette info parsed from the document page.")
markdown_chunk: Optional[str] = Field(None, description="A 5,000 character chunk of the Markdown content.") # Corrected chunk size
decision_reference_no_from_page: str = Field("", description="E.K. No parsed from the document page.")
decision_date_from_page: str = Field("", description="Decision date parsed from the document page.")
official_gazette_info_from_page: str = Field("", description="Official Gazette info parsed from the document page.")
markdown_chunk: str = Field("", description="A 5,000 character chunk of the Markdown content.") # Corrected chunk size
current_page: int = Field(description="The current page number of the markdown chunk (1-indexed).")
total_pages: int = Field(description="Total number of pages for the full markdown content.")
is_paginated: bool = Field(description="True if the full markdown content is split into multiple pages.")
@@ -168,27 +168,27 @@ class AnayasaBireyselReportSearchRequest(BaseModel):
class AnayasaBireyselReportDecisionDetail(BaseModel):
"""Details of a specific right/claim within a Bireysel Başvuru decision summary in a report."""
hak: Optional[str] = Field(None, description="İhlal edildiği iddia edilen hak (örneğin, Mülkiyet hakkı).")
mudahale_iddiasi: Optional[str] = Field(None, description="İhlale neden olan müdahale iddiası.")
sonuc: Optional[str] = Field(None, description="İnceleme sonucu (örneğin, İhlal, Düşme).")
giderim: Optional[str] = Field(None, description="Kararlaştırılan giderim (örneğin, Yeniden yargılama).")
hak: str = Field("", description="İhlal edildiği iddia edilen hak (örneğin, Mülkiyet hakkı).")
mudahale_iddiasi: str = Field("", description="İhlale neden olan müdahale iddiası.")
sonuc: str = Field("", description="İnceleme sonucu (örneğin, İhlal, Düşme).")
giderim: str = Field("", description="Kararlaştırılan giderim (örneğin, Yeniden yargılama).")
class AnayasaBireyselReportDecisionSummary(BaseModel):
"""Model for a single Anayasa Mahkemesi (Bireysel Başvuru) decision summary from a 'Karar Arama Raporu'."""
title: Optional[str] = Field(None, description="Başvurunun başlığı (e.g., 'HASAN DURMUŞ Başvurusuna İlişkin Karar').")
decision_reference_no: Optional[str] = Field(None, description="Başvuru Numarası (e.g., '2019/19126').")
decision_page_url: Optional[HttpUrl] = Field(None, description="URL to the full decision page.")
decision_type_summary: Optional[str] = Field(None, description="Karar Türü (Başvuru Sonucu) (e.g., 'Esas (İhlal)').")
decision_making_body: Optional[str] = Field(None, description="Kararı Veren Birim (e.g., 'Genel Kurul', 'Birinci Bölüm').")
application_date_summary: Optional[str] = Field(None, description="Başvuru Tarihi (DD/MM/YYYY).")
decision_date_summary: Optional[str] = Field(None, description="Karar Tarihi (DD/MM/YYYY).")
application_subject_summary: Optional[str] = Field(None, description="Başvuru konusunun özeti.")
title: str = Field("", description="Başvurunun başlığı (e.g., 'HASAN DURMUŞ Başvurusuna İlişkin Karar').")
decision_reference_no: str = Field("", description="Başvuru Numarası (e.g., '2019/19126').")
decision_page_url: str = Field("", description="URL to the full decision page.")
decision_type_summary: str = Field("", description="Karar Türü (Başvuru Sonucu) (e.g., 'Esas (İhlal)').")
decision_making_body: str = Field("", description="Kararı Veren Birim (e.g., 'Genel Kurul', 'Birinci Bölüm').")
application_date_summary: str = Field("", description="Başvuru Tarihi (DD/MM/YYYY).")
decision_date_summary: str = Field("", description="Karar Tarihi (DD/MM/YYYY).")
application_subject_summary: str = Field("", description="Başvuru konusunun özeti.")
details: List[AnayasaBireyselReportDecisionDetail] = Field(default_factory=list, description="İncelenen haklar ve sonuçlarına ilişkin detaylar.")
class AnayasaBireyselReportSearchResult(BaseModel):
"""Model for the overall search result for Anayasa Mahkemesi 'Karar Arama Raporu'."""
decisions: List[AnayasaBireyselReportDecisionSummary]
total_records_found: Optional[int] = Field(None, description="Raporda bulunan toplam karar sayısı.")
total_records_found: int = Field(0, description="Raporda bulunan toplam karar sayısı.")
retrieved_page_number: int = Field(description="Alınan rapor sayfa numarası.")
+2 -2
View File
@@ -76,8 +76,8 @@ class DanistayApiClient:
mevzuatNumarasi=params.mevzuatNumarasi or "",
mevzuatAdi=params.mevzuatAdi or "",
madde=params.madde or "",
siralama=params.siralama,
siralamaDirection=params.siralamaDirection,
siralama="1",
siralamaDirection="desc",
pageSize=params.pageSize,
pageNumber=params.pageNumber
)
+19 -21
View File
@@ -51,20 +51,18 @@ class DanistayDetailedSearchRequestData(BaseModel): # Internal data model for de
class DanistayDetailedSearchRequest(DanistayBaseSearchRequest): # MCP tool will accept this
"""Model for detailed search request for Danistay."""
daire: Optional[str] = Field(None, description="Chamber")
esasYil: Optional[str] = Field(None, description="Case year")
esasIlkSiraNo: Optional[str] = Field(None, description="Start case no")
esasSonSiraNo: Optional[str] = Field(None, description="End case no")
kararYil: Optional[str] = Field(None, description="Decision year")
kararIlkSiraNo: Optional[str] = Field(None, description="Start decision no")
kararSonSiraNo: Optional[str] = Field(None, description="End decision no")
baslangicTarihi: Optional[str] = Field(None, description="Start date")
bitisTarihi: Optional[str] = Field(None, description="End date")
mevzuatNumarasi: Optional[str] = Field(None, description="Law number")
mevzuatAdi: Optional[str] = Field(None, description="Law name")
madde: Optional[str] = Field(None, description="Article")
siralama: str = Field("1", description="Sort by")
siralamaDirection: str = Field("desc", description="Direction")
daire: str = Field("", description="Chamber")
esasYil: str = Field("", description="Case year")
esasIlkSiraNo: str = Field("", description="Start case no")
esasSonSiraNo: str = Field("", description="End case no")
kararYil: str = Field("", description="Decision year")
kararIlkSiraNo: str = Field("", description="Start decision no")
kararSonSiraNo: str = Field("", description="End decision no")
baslangicTarihi: str = Field("", description="Start date")
bitisTarihi: str = Field("", description="End date")
mevzuatNumarasi: str = Field("", description="Law number")
mevzuatAdi: str = Field("", description="Law name")
madde: str = Field("", description="Article")
# Add a general keyword field if detailed search also supports it
# arananKelime: Optional[str] = Field(None, description="General keyword for detailed search.")
@@ -76,11 +74,11 @@ class DanistayApiDecisionEntry(BaseModel):
id: str
# The API response for keyword search uses "daireKurul", detailed search example uses "daire".
# We use an alias to handle both and map to a consistent field name "chamber".
chamber: Optional[str] = Field(None, alias="daire", description="Chamber")
esasNo: Optional[str] = Field(None)
kararNo: Optional[str] = Field(None)
kararTarihi: Optional[str] = Field(None)
arananKelime: Optional[str] = Field(None, description="Keyword")
chamber: str = Field("", alias="daire", description="Chamber")
esasNo: str = Field("", description="Case number")
kararNo: str = Field("", description="Decision number")
kararTarihi: str = Field("", description="Decision date")
arananKelime: str = Field("", description="Keyword")
# index: Optional[int] = None # Present in response, can be added if needed by MCP tool
# siraNo: Optional[int] = None # Present in detailed response, can be added
@@ -93,7 +91,7 @@ class DanistayApiResponseInnerData(BaseModel):
data: List[DanistayApiDecisionEntry]
recordsTotal: int
recordsFiltered: int
draw: Optional[int] = Field(None, description="Draw counter")
draw: int = Field(0, description="Draw counter")
class DanistayApiResponse(BaseModel):
"""Model for the complete search response from the Danistay API."""
@@ -103,7 +101,7 @@ class DanistayApiResponse(BaseModel):
class DanistayDocumentMarkdown(BaseModel):
"""Model for a Danistay decision document, containing only Markdown content."""
id: str
markdown_content: Optional[str] = Field(None, description="The decision content (Karar İçeriği) converted to Markdown.")
markdown_content: str = Field("", description="The decision content (Karar İçeriği) converted to Markdown.")
source_url: HttpUrl
class CompactDanistaySearchResult(BaseModel):
+22 -22
View File
@@ -12,8 +12,8 @@ class EmsalDetailedSearchRequestData(BaseModel):
"""
arananKelime: Optional[str] = ""
Bam_Hukuk_Mahkemeleri: Optional[str] = Field(None, alias="Bam Hukuk Mahkemeleri")
Hukuk_Mahkemeleri: Optional[str] = Field(None, alias="Hukuk Mahkemeleri")
Bam_Hukuk_Mahkemeleri: str = Field("", alias="Bam Hukuk Mahkemeleri")
Hukuk_Mahkemeleri: str = Field("", alias="Hukuk Mahkemeleri")
# Add other specific court type fields from the form if they are separate keys in payload
# E.g., "Ceza Mahkemeleri", "İdari Mahkemeler" etc.
@@ -36,22 +36,22 @@ class EmsalDetailedSearchRequestData(BaseModel):
class EmsalSearchRequest(BaseModel): # This is the model the MCP tool will accept
"""Model for Emsal detailed search request, with user-friendly field names."""
keyword: Optional[str] = Field(None, description="Keyword")
keyword: str = Field("", description="Keyword")
selected_bam_civil_court: Optional[str] = Field(None, description="BAM Civil Court")
selected_civil_court: Optional[str] = Field(None, description="Civil Court")
selected_regional_civil_chambers: Optional[List[str]] = Field(default_factory=list, description="Regional chambers")
selected_bam_civil_court: str = Field("", description="BAM Civil Court")
selected_civil_court: str = Field("", description="Civil Court")
selected_regional_civil_chambers: List[str] = Field(default_factory=list, description="Regional chambers")
case_year_esas: Optional[str] = Field(None, description="Case year")
case_start_seq_esas: Optional[str] = Field(None, description="Start case no")
case_end_seq_esas: Optional[str] = Field(None, description="End case no")
case_year_esas: str = Field("", description="Case year")
case_start_seq_esas: str = Field("", description="Start case no")
case_end_seq_esas: str = Field("", description="End case no")
decision_year_karar: Optional[str] = Field(None, description="Decision year")
decision_start_seq_karar: Optional[str] = Field(None, description="Start decision no")
decision_end_seq_karar: Optional[str] = Field(None, description="End decision no")
decision_year_karar: str = Field("", description="Decision year")
decision_start_seq_karar: str = Field("", description="Start decision no")
decision_end_seq_karar: str = Field("", description="End decision no")
start_date: Optional[str] = Field(None, description="Start date (DD.MM.YYYY)")
end_date: Optional[str] = Field(None, description="End date (DD.MM.YYYY)")
start_date: str = Field("", description="Start date (DD.MM.YYYY)")
end_date: str = Field("", description="End date (DD.MM.YYYY)")
sort_criteria: str = Field("1", description="Sort by")
sort_direction: str = Field("desc", description="Direction")
@@ -63,12 +63,12 @@ class EmsalSearchRequest(BaseModel): # This is the model the MCP tool will accep
class EmsalApiDecisionEntry(BaseModel):
"""Model for an individual decision entry from the Emsal API search response."""
id: str
daire: Optional[str] = Field(None, description="Chamber")
esasNo: Optional[str] = Field(None)
kararNo: Optional[str] = Field(None)
kararTarihi: Optional[str] = Field(None)
arananKelime: Optional[str] = Field(None, description="Keyword")
durum: Optional[str] = Field(None, description="Status")
daire: str = Field("", description="Chamber")
esasNo: str = Field("", description="Case number")
kararNo: str = Field("", description="Decision number")
kararTarihi: str = Field("", description="Decision date")
arananKelime: str = Field("", description="Keyword")
durum: str = Field("", description="Status")
# index: Optional[int] = None # Present in Emsal response, can be added if tool needs it
document_url: Optional[HttpUrl] = Field(None, description="Document URL")
@@ -80,7 +80,7 @@ class EmsalApiResponseInnerData(BaseModel):
data: List[EmsalApiDecisionEntry]
recordsTotal: int
recordsFiltered: int
draw: Optional[int] = Field(None, description="Draw counter (Çizim Sayıcısı) from API, usually for DataTables.")
draw: int = Field(0, description="Draw counter (Çizim Sayıcısı) from API, usually for DataTables.")
class EmsalApiResponse(BaseModel):
"""Model for the complete search response from the Emsal API."""
@@ -90,7 +90,7 @@ class EmsalApiResponse(BaseModel):
class EmsalDocumentMarkdown(BaseModel):
"""Model for an Emsal decision document, containing only Markdown content."""
id: str
markdown_content: Optional[str] = Field(None, description="The decision content (Karar İçeriği) converted to Markdown.")
markdown_content: str = Field("", description="The decision content (Karar İçeriği) converted to Markdown.")
source_url: HttpUrl
class CompactEmsalSearchResult(BaseModel):
+13 -13
View File
@@ -13,16 +13,16 @@ class KikKararTipi(str, Enum):
class KikSearchRequest(BaseModel):
"""Model for KIK Decision search criteria."""
karar_tipi: KikKararTipi = Field(KikKararTipi.UYUSMAZLIK, description="Type")
karar_no: Optional[str] = Field(None, description="No")
karar_tarihi_baslangic: Optional[str] = Field(None, description="Start", pattern=r"^\d{2}\.\d{2}\.\d{4}$")
karar_tarihi_bitis: Optional[str] = Field(None, description="End", pattern=r"^\d{2}\.\d{2}\.\d{4}$")
resmi_gazete_sayisi: Optional[str] = Field(None, description="Gazette")
resmi_gazete_tarihi: Optional[str] = Field(None, description="Date", pattern=r"^\d{2}\.\d{2}\.\d{4}$")
basvuru_konusu_ihale: Optional[str] = Field(None, description="Subject")
basvuru_sahibi: Optional[str] = Field(None, description="Applicant")
ihaleyi_yapan_idare: Optional[str] = Field(None, description="Entity")
yil: Optional[str] = Field(None, description="Year")
karar_metni: Optional[str] = Field(None, description="Text")
karar_no: str = Field("", description="No")
karar_tarihi_baslangic: str = Field("", description="Start", pattern=r"^\d{2}\.\d{2}\.\d{4}$|^$")
karar_tarihi_bitis: str = Field("", description="End", pattern=r"^\d{2}\.\d{2}\.\d{4}$|^$")
resmi_gazete_sayisi: str = Field("", description="Gazette")
resmi_gazete_tarihi: str = Field("", description="Date", pattern=r"^\d{2}\.\d{2}\.\d{4}$|^$")
basvuru_konusu_ihale: str = Field("", description="Subject")
basvuru_sahibi: str = Field("", description="Applicant")
ihaleyi_yapan_idare: str = Field("", description="Entity")
yil: str = Field("", description="Year")
karar_metni: str = Field("", description="Text")
page: int = Field(1, ge=1, description="Page")
class KikDecisionEntry(BaseModel):
@@ -32,9 +32,9 @@ class KikDecisionEntry(BaseModel):
karar_tipi: KikKararTipi = Field(..., description="Decision type")
karar_tarihi_str: str = Field(..., alias="kararTarihi", description="Date")
idare_str: Optional[str] = Field(None, alias="idare", description="Entity")
basvuru_sahibi_str: Optional[str] = Field(None, alias="basvuruSahibi", description="Applicant")
ihale_konusu_str: Optional[str] = Field(None, alias="ihaleKonusu", description="Subject")
idare_str: str = Field("", alias="idare", description="Entity")
basvuru_sahibi_str: str = Field("", alias="basvuruSahibi", description="Applicant")
ihale_konusu_str: str = Field("", alias="ihaleKonusu", description="Subject")
@computed_field
@property
+326 -129
View File
@@ -4,9 +4,14 @@ import atexit
import logging
import os
import httpx
import json
import time
from collections import defaultdict
from pydantic import HttpUrl, Field
from typing import Optional, Dict, List, Literal, Any, Union
import urllib.parse
import tiktoken
from fastmcp.server.middleware import Middleware, MiddlewareContext
# --- Logging Configuration Start ---
LOG_DIRECTORY = os.path.join(os.path.dirname(os.path.abspath(__file__)), "logs")
@@ -32,6 +37,197 @@ root_logger.addHandler(console_handler)
logger = logging.getLogger(__name__)
# --- Logging Configuration End ---
# --- Token Counting Middleware ---
class TokenCountingMiddleware(Middleware):
"""Middleware for counting input/output tokens using tiktoken."""
def __init__(self, model: str = "cl100k_base"):
"""Initialize token counting middleware.
Args:
model: Tiktoken model name (cl100k_base for GPT-4/Claude compatibility)
"""
self.encoder = tiktoken.get_encoding(model)
self.model = model
self.token_stats = defaultdict(lambda: {"input": 0, "output": 0, "calls": 0})
self.logger = logging.getLogger("token_counter")
# Create separate log file for token metrics
token_log_path = os.path.join(LOG_DIRECTORY, "token_metrics.log")
token_handler = logging.FileHandler(token_log_path, mode='a', encoding='utf-8')
token_formatter = logging.Formatter('%(asctime)s - %(message)s')
token_handler.setFormatter(token_formatter)
token_handler.setLevel(logging.INFO)
self.logger.addHandler(token_handler)
self.logger.setLevel(logging.INFO)
def count_tokens(self, text: str) -> int:
"""Count tokens in text using tiktoken."""
if not text:
return 0
try:
return len(self.encoder.encode(str(text)))
except Exception as e:
logger.warning(f"Token counting failed: {e}")
return 0
def extract_text_content(self, data: Any) -> str:
"""Extract text content from various data types."""
if isinstance(data, str):
return data
elif isinstance(data, dict):
# Extract text from common response fields
text_parts = []
for key, value in data.items():
if isinstance(value, str):
text_parts.append(value)
elif isinstance(value, list):
for item in value:
if isinstance(item, str):
text_parts.append(item)
elif isinstance(item, dict) and 'text' in item:
text_parts.append(str(item['text']))
return ' '.join(text_parts)
elif isinstance(data, list):
text_parts = []
for item in data:
text_parts.append(self.extract_text_content(item))
return ' '.join(text_parts)
else:
return str(data)
def log_token_usage(self, operation: str, input_tokens: int, output_tokens: int,
tool_name: str = None, duration_ms: float = None):
"""Log token usage with structured format."""
log_data = {
"operation": operation,
"tool_name": tool_name,
"input_tokens": input_tokens,
"output_tokens": output_tokens,
"total_tokens": input_tokens + output_tokens,
"duration_ms": duration_ms,
"timestamp": time.time()
}
# Update statistics
key = tool_name if tool_name else operation
self.token_stats[key]["input"] += input_tokens
self.token_stats[key]["output"] += output_tokens
self.token_stats[key]["calls"] += 1
# Log as JSON for easy parsing
self.logger.info(json.dumps(log_data))
# Also log human-readable format to main logger
logger.info(f"Token Usage - {operation}" +
(f" ({tool_name})" if tool_name else "") +
f": {input_tokens} in + {output_tokens} out = {input_tokens + output_tokens} total")
async def on_call_tool(self, context: MiddlewareContext, call_next):
"""Count tokens for tool calls."""
start_time = time.perf_counter()
# Extract tool name and arguments
tool_name = getattr(context.message, 'name', 'unknown_tool')
tool_args = getattr(context.message, 'arguments', {})
# Count input tokens (tool arguments)
input_text = self.extract_text_content(tool_args)
input_tokens = self.count_tokens(input_text)
try:
# Execute the tool
result = await call_next(context)
# Count output tokens (tool result)
output_text = self.extract_text_content(result)
output_tokens = self.count_tokens(output_text)
# Calculate duration
duration_ms = (time.perf_counter() - start_time) * 1000
# Log token usage
self.log_token_usage("tool_call", input_tokens, output_tokens,
tool_name, duration_ms)
return result
except Exception as e:
duration_ms = (time.perf_counter() - start_time) * 1000
self.log_token_usage("tool_call_error", input_tokens, 0,
tool_name, duration_ms)
raise
async def on_read_resource(self, context: MiddlewareContext, call_next):
"""Count tokens for resource reads."""
start_time = time.perf_counter()
# Extract resource URI
resource_uri = getattr(context.message, 'uri', 'unknown_resource')
try:
# Execute the resource read
result = await call_next(context)
# Count output tokens (resource content)
output_text = self.extract_text_content(result)
output_tokens = self.count_tokens(output_text)
# Calculate duration
duration_ms = (time.perf_counter() - start_time) * 1000
# Log token usage (no input tokens for resource reads)
self.log_token_usage("resource_read", 0, output_tokens,
resource_uri, duration_ms)
return result
except Exception as e:
duration_ms = (time.perf_counter() - start_time) * 1000
self.log_token_usage("resource_read_error", 0, 0,
resource_uri, duration_ms)
raise
async def on_get_prompt(self, context: MiddlewareContext, call_next):
"""Count tokens for prompt retrievals."""
start_time = time.perf_counter()
# Extract prompt name
prompt_name = getattr(context.message, 'name', 'unknown_prompt')
try:
# Execute the prompt retrieval
result = await call_next(context)
# Count output tokens (prompt content)
output_text = self.extract_text_content(result)
output_tokens = self.count_tokens(output_text)
# Calculate duration
duration_ms = (time.perf_counter() - start_time) * 1000
# Log token usage
self.log_token_usage("prompt_get", 0, output_tokens,
prompt_name, duration_ms)
return result
except Exception as e:
duration_ms = (time.perf_counter() - start_time) * 1000
self.log_token_usage("prompt_get_error", 0, 0,
prompt_name, duration_ms)
raise
def get_token_stats(self) -> Dict[str, Any]:
"""Get current token usage statistics."""
return dict(self.token_stats)
def reset_token_stats(self):
"""Reset token usage statistics."""
self.token_stats.clear()
# --- End Token Counting Middleware ---
# Create FastMCP app directly without authentication wrapper
from fastmcp import FastMCP
@@ -113,6 +309,11 @@ from kvkk_mcp_module.models import (
app = create_app()
# --- Add Token Counting Middleware ---
token_counter = TokenCountingMiddleware()
app.add_middleware(token_counter)
logger.info("Token counting middleware added to MCP server")
# --- Tool Documentation Resources ---
@app.resource("docs://tools/yargitay")
async def get_yargitay_tools_documentation() -> str:
@@ -863,7 +1064,7 @@ KARAR_TURU_ADI_TO_GUID_ENUM_MAP = {
# --- MCP Tools for Yargitay ---
@app.tool(
description="Search Yargıtay decisions using primary API with 52 chamber filtering and advanced operators. Before using, read docs://tools/yargitay",
description="Search Yargıtay decisions with 52 chamber filtering and advanced operators",
annotations={
"readOnlyHint": True,
"openWorldHint": True,
@@ -871,10 +1072,8 @@ KARAR_TURU_ADI_TO_GUID_ENUM_MAP = {
}
)
async def search_yargitay_detailed(
arananKelime: str = Field("", description="Search keyword with OR/AND/wildcard operators"),
arananKelime: str = Field("", description="Turkish search keyword. Supports +required -excluded \"exact phrase\" operators"),
birimYrgKurulDaire: str = Field("ALL", description="Chamber selection (52 options: Civil/Criminal chambers, General Assemblies)"),
birimYrgHukukDaire: str = Field("", description="Legacy field - use birimYrgKurulDaire instead"),
birimYrgCezaDaire: str = Field("", description="Legacy field - use birimYrgKurulDaire instead"),
esasYil: str = Field("", description="Case year for 'Esas No'."),
esasIlkSiraNo: str = Field("", description="Starting sequence number for 'Esas No'."),
esasSonSiraNo: str = Field("", description="Ending sequence number for 'Esas No'."),
@@ -883,8 +1082,6 @@ async def search_yargitay_detailed(
kararSonSiraNo: str = Field("", description="Ending sequence number for 'Karar No'."),
baslangicTarihi: str = Field("", description="Start date for decision search (DD.MM.YYYY)."),
bitisTarihi: str = Field("", description="End date for decision search (DD.MM.YYYY)."),
siralama: str = Field("3", description="Sorting criteria (1: Esas No, 2: Karar No, 3: Karar Tarihi)."),
siralamaDirection: str = Field("desc", description="Sorting direction ('asc' or 'desc')."),
pageSize: int = Field(10, ge=1, le=10, description="Number of results per page."),
pageNumber: int = Field(1, ge=1, description="Page number to retrieve.")
) -> CompactYargitaySearchResult:
@@ -897,8 +1094,6 @@ async def search_yargitay_detailed(
search_query = YargitayDetailedSearchRequest(
arananKelime=arananKelime,
birimYrgKurulDaire=birimYrgKurulDaire,
birimYrgHukukDaire=birimYrgHukukDaire,
birimYrgCezaDaire=birimYrgCezaDaire,
esasYil=esasYil,
esasIlkSiraNo=esasIlkSiraNo,
esasSonSiraNo=esasSonSiraNo,
@@ -907,8 +1102,8 @@ async def search_yargitay_detailed(
kararSonSiraNo=kararSonSiraNo,
baslangicTarihi=baslangicTarihi,
bitisTarihi=bitisTarihi,
siralama=siralama,
siralamaDirection=siralamaDirection,
siralama="3",
siralamaDirection="desc",
pageSize=pageSize,
pageNumber=pageNumber
)
@@ -941,7 +1136,7 @@ async def search_yargitay_detailed(
raise
@app.tool(
description="Retrieve full text of a Yargıtay decision in Markdown format. Before using, read docs://tools/yargitay",
description="Get Yargıtay decision text in Markdown format",
annotations={
"readOnlyHint": True,
"idempotentHint": True
@@ -959,7 +1154,7 @@ async def get_yargitay_document_markdown(id: str) -> YargitayDocumentMarkdown:
# --- MCP Tools for Danistay ---
@app.tool(
description="Search Danıştay decisions using keyword logic with AND/OR/NOT operators. Before using, read docs://tools/danistay",
description="Search Danıştay decisions with keyword logic (AND/OR/NOT operators)",
annotations={
"readOnlyHint": True,
"openWorldHint": True,
@@ -1001,7 +1196,7 @@ async def search_danistay_by_keyword(
raise
@app.tool(
description="Search Danıştay decisions with detailed criteria including chamber selection and case numbers. Before using, read docs://tools/danistay",
description="Search Danıştay decisions with detailed criteria (chamber selection, case numbers)",
annotations={
"readOnlyHint": True,
"openWorldHint": True,
@@ -1009,20 +1204,18 @@ async def search_danistay_by_keyword(
}
)
async def search_danistay_detailed(
daire: Optional[str] = Field(None, description="Chamber/Department name (e.g., '1. Daire')."),
esasYil: Optional[str] = Field(None, description="Case year for 'Esas No'."),
esasIlkSiraNo: Optional[str] = Field(None, description="Starting sequence for 'Esas No'."),
esasSonSiraNo: Optional[str] = Field(None, description="Ending sequence for 'Esas No'."),
kararYil: Optional[str] = Field(None, description="Decision year for 'Karar No'."),
kararIlkSiraNo: Optional[str] = Field(None, description="Starting sequence for 'Karar No'."),
kararSonSiraNo: Optional[str] = Field(None, description="Ending sequence for 'Karar No'."),
baslangicTarihi: Optional[str] = Field(None, description="Start date for decision (DD.MM.YYYY)."),
bitisTarihi: Optional[str] = Field(None, description="End date for decision (DD.MM.YYYY)."),
mevzuatNumarasi: Optional[str] = Field(None, description="Legislation number."),
mevzuatAdi: Optional[str] = Field(None, description="Legislation name."),
madde: Optional[str] = Field(None, description="Article number."),
siralama: str = Field("1", description="Sorting criteria (e.g., 1: Esas No, 3: Karar Tarihi)."),
siralamaDirection: str = Field("desc", description="Sorting direction ('asc' or 'desc')."),
daire: str = Field("", description="Chamber/Department name (e.g., '1. Daire')."),
esasYil: str = Field("", description="Case year for 'Esas No'."),
esasIlkSiraNo: str = Field("", description="Starting sequence for 'Esas No'."),
esasSonSiraNo: str = Field("", description="Ending sequence for 'Esas No'."),
kararYil: str = Field("", description="Decision year for 'Karar No'."),
kararIlkSiraNo: str = Field("", description="Starting sequence for 'Karar No'."),
kararSonSiraNo: str = Field("", description="Ending sequence for 'Karar No'."),
baslangicTarihi: str = Field("", description="Start date for decision (DD.MM.YYYY)."),
bitisTarihi: str = Field("", description="End date for decision (DD.MM.YYYY)."),
mevzuatNumarasi: str = Field("", description="Legislation number."),
mevzuatAdi: str = Field("", description="Legislation name."),
madde: str = Field("", description="Article number."),
pageNumber: int = Field(1, ge=1, description="Page number."),
pageSize: int = Field(10, ge=1, le=10, description="Results per page.")
) -> CompactDanistaySearchResult:
@@ -1041,8 +1234,8 @@ async def search_danistay_detailed(
mevzuatNumarasi=mevzuatNumarasi,
mevzuatAdi=mevzuatAdi,
madde=madde,
siralama=siralama,
siralamaDirection=siralamaDirection,
siralama="3",
siralamaDirection="desc",
pageNumber=pageNumber,
pageSize=pageSize
)
@@ -1063,7 +1256,7 @@ async def search_danistay_detailed(
raise
@app.tool(
description="Retrieve full text of a Danıştay decision in Markdown format. Before using, read docs://tools/danistay",
description="Get Danıştay decision text in Markdown format",
annotations={
"readOnlyHint": True,
"idempotentHint": True
@@ -1081,7 +1274,7 @@ async def get_danistay_document_markdown(id: str) -> DanistayDocumentMarkdown:
# --- MCP Tools for Emsal ---
@app.tool(
description="Search Emsal precedent decisions with detailed criteria across Turkish courts. Before using, read docs://tools/emsal",
description="Search Emsal precedent decisions with detailed criteria",
annotations={
"readOnlyHint": True,
"openWorldHint": True,
@@ -1089,18 +1282,18 @@ async def get_danistay_document_markdown(id: str) -> DanistayDocumentMarkdown:
}
)
async def search_emsal_detailed_decisions(
keyword: Optional[str] = Field(None, description="Keyword to search."),
selected_bam_civil_court: Optional[str] = Field(None, description="Selected BAM Civil Court."),
selected_civil_court: Optional[str] = Field(None, description="Selected Civil Court."),
keyword: str = Field("", description="Keyword to search."),
selected_bam_civil_court: str = Field("", description="Selected BAM Civil Court."),
selected_civil_court: str = Field("", description="Selected Civil Court."),
selected_regional_civil_chambers: List[str] = Field(default_factory=list, description="Selected Regional Civil Chambers."),
case_year_esas: Optional[str] = Field(None, description="Case year for 'Esas No'."),
case_start_seq_esas: Optional[str] = Field(None, description="Starting sequence for 'Esas No'."),
case_end_seq_esas: Optional[str] = Field(None, description="Ending sequence for 'Esas No'."),
decision_year_karar: Optional[str] = Field(None, description="Decision year for 'Karar No'."),
decision_start_seq_karar: Optional[str] = Field(None, description="Starting sequence for 'Karar No'."),
decision_end_seq_karar: Optional[str] = Field(None, description="Ending sequence for 'Karar No'."),
start_date: Optional[str] = Field(None, description="Start date for decision (DD.MM.YYYY)."),
end_date: Optional[str] = Field(None, description="End date for decision (DD.MM.YYYY)."),
case_year_esas: str = Field("", description="Case year for 'Esas No'."),
case_start_seq_esas: str = Field("", description="Starting sequence for 'Esas No'."),
case_end_seq_esas: str = Field("", description="Ending sequence for 'Esas No'."),
decision_year_karar: str = Field("", description="Decision year for 'Karar No'."),
decision_start_seq_karar: str = Field("", description="Starting sequence for 'Karar No'."),
decision_end_seq_karar: str = Field("", description="Ending sequence for 'Karar No'."),
start_date: str = Field("", description="Start date for decision (DD.MM.YYYY)."),
end_date: str = Field("", description="End date for decision (DD.MM.YYYY)."),
sort_criteria: str = Field("1", description="Sorting criteria (e.g., 1: Esas No)."),
sort_direction: str = Field("desc", description="Sorting direction ('asc' or 'desc')."),
page_number: int = Field(1, ge=1, description="Page number (accepts int)."),
@@ -1144,7 +1337,7 @@ async def search_emsal_detailed_decisions(
raise
@app.tool(
description="Retrieve full text of an Emsal precedent decision in Markdown format. Before using, read docs://tools/emsal",
description="Get Emsal precedent decision text in Markdown format",
annotations={
"readOnlyHint": True,
"idempotentHint": True
@@ -1162,7 +1355,7 @@ async def get_emsal_document_markdown(id: str) -> EmsalDocumentMarkdown:
# --- MCP Tools for Uyusmazlik ---
@app.tool(
description="Search Uyuşmazlık Mahkemesi decisions for jurisdictional disputes between court systems. Before using, read docs://tools/uyusmazlik",
description="Search Uyuşmazlık Mahkemesi decisions for jurisdictional disputes",
annotations={
"readOnlyHint": True,
"openWorldHint": True,
@@ -1233,7 +1426,7 @@ async def search_uyusmazlik_decisions(
raise
@app.tool(
description="Retrieve full text of an Uyuşmazlık Mahkemesi decision from URL in Markdown format. Before using, read docs://tools/uyusmazlik",
description="Get Uyuşmazlık Mahkemesi decision text from URL in Markdown format",
annotations={
"readOnlyHint": True,
"idempotentHint": True
@@ -1254,7 +1447,7 @@ async def get_uyusmazlik_document_markdown_from_url(
# --- MCP Tools for Anayasa Mahkemesi (Norm Denetimi) ---
@app.tool(
description="Search Constitutional Court norm control decisions with comprehensive filtering and legal criteria. Before using, read docs://tools/constitutional_court",
description="Search Constitutional Court norm control decisions with comprehensive filtering",
annotations={
"readOnlyHint": True,
"openWorldHint": True,
@@ -1262,34 +1455,34 @@ async def get_uyusmazlik_document_markdown_from_url(
}
)
async def search_anayasa_norm_denetimi_decisions(
keywords_all: List[str] = Field(default_factory=list, description="AND keywords"),
keywords_any: List[str] = Field(default_factory=list, description="OR keywords"),
keywords_exclude: List[str] = Field(default_factory=list, description="Exclude keywords"),
keywords_all: List[str] = Field(default_factory=list, description="AND"),
keywords_any: List[str] = Field(default_factory=list, description="OR"),
keywords_exclude: List[str] = Field(default_factory=list, description="NOT"),
period: Literal["ALL", "1", "2"] = Field("ALL", description="Period"),
case_number_esas: Optional[str] = Field(None, description="Case number"),
decision_number_karar: Optional[str] = Field(None, description="Decision number"),
first_review_date_start: Optional[str] = Field(None, description="Review start date"),
first_review_date_end: Optional[str] = Field(None, description="Review end date"),
decision_date_start: Optional[str] = Field(None, description="Decision start"),
decision_date_end: Optional[str] = Field(None, description="Decision end"),
case_number_esas: str = Field("", description="Case number"),
decision_number_karar: str = Field("", description="Decision number"),
first_review_date_start: str = Field("", description="Start"),
first_review_date_end: str = Field("", description="End"),
decision_date_start: str = Field("", description="Start"),
decision_date_end: str = Field("", description="End"),
application_type: Literal["ALL", "1", "2", "3"] = Field("ALL", description="App type"),
applicant_general_name: Optional[str] = Field(None, description="Applicant"),
applicant_specific_name: Optional[str] = Field(None, description="Specific name"),
official_gazette_date_start: Optional[str] = Field(None, description="Gazette start"),
official_gazette_date_end: Optional[str] = Field(None, description="Gazette end"),
official_gazette_number_start: Optional[str] = Field(None, description="Gazette no start"),
official_gazette_number_end: Optional[str] = Field(None, description="Gazette no end"),
has_press_release: Literal["ALL", "0", "1"] = Field("ALL", description="Press release"),
applicant_general_name: str = Field("", description="Applicant"),
applicant_specific_name: str = Field("", description="Name"),
official_gazette_date_start: str = Field("", description="Start"),
official_gazette_date_end: str = Field("", description="End"),
official_gazette_number_start: str = Field("", description="Start"),
official_gazette_number_end: str = Field("", description="End"),
has_press_release: Literal["ALL", "0", "1"] = Field("ALL", description="Press"),
has_dissenting_opinion: Literal["ALL", "0", "1"] = Field("ALL", description="Dissenting"),
has_different_reasoning: Literal["ALL", "0", "1"] = Field("ALL", description="Different reason"),
has_different_reasoning: Literal["ALL", "0", "1"] = Field("ALL", description="Reason"),
attending_members_names: List[str] = Field(default_factory=list, description="Members"),
rapporteur_name: Optional[str] = Field(None, description="Rapporteur"),
rapporteur_name: str = Field("", description="Rapporteur"),
norm_type: Literal["ALL", "1", "2", "14", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "0", "13"] = Field("ALL", description="Norm type"),
norm_id_or_name: Optional[str] = Field(None, description="Norm name"),
norm_article: Optional[str] = Field(None, description="Article"),
norm_id_or_name: str = Field("", description="Norm name"),
norm_article: str = Field("", description="Article"),
review_outcomes: List[Literal["ALL", "1", "2", "3", "4", "5", "6", "7", "8", "12"]] = Field(default_factory=list, description="Outcomes"),
reason_for_final_outcome: Literal["ALL", "29", "1", "2", "30", "3", "4", "27", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26"] = Field("ALL", description="Reason"),
basis_constitution_article_numbers: List[str] = Field(default_factory=list, description="Articles"),
basis_constitution_article_numbers: List[str] = Field(default_factory=list, description="Arts"),
results_per_page: int = Field(10, description="Count"),
page_to_fetch: int = Field(1, ge=1, description="Page"),
sort_by_criteria: str = Field("KararTarihi", description="Sort")
@@ -1348,7 +1541,7 @@ async def search_anayasa_norm_denetimi_decisions(
raise
@app.tool(
description="Retrieve full text of a Constitutional Court norm control decision in paginated Markdown format. Before using, read docs://tools/constitutional_court",
description="Get Constitutional Court norm control decision text in paginated Markdown format",
annotations={
"readOnlyHint": True,
"idempotentHint": True
@@ -1371,7 +1564,7 @@ async def get_anayasa_norm_denetimi_document_markdown(
# --- MCP Tools for Anayasa Mahkemesi (Bireysel Başvuru Karar Raporu & Belgeler) ---
@app.tool(
description="Search Constitutional Court individual application decisions for human rights violation reports. Before using, read docs://tools/constitutional_court",
description="Search Constitutional Court individual application decisions for human rights violations",
annotations={
"readOnlyHint": True,
"openWorldHint": True,
@@ -1397,7 +1590,7 @@ async def search_anayasa_bireysel_basvuru_report(
raise
@app.tool(
description="Parameter description",
description="Get Constitutional Court individual application decision text in paginated Markdown format",
annotations={
"readOnlyHint": True,
"idempotentHint": True
@@ -1427,7 +1620,7 @@ async def get_anayasa_bireysel_basvuru_document_markdown(
# --- MCP Tools for KIK (Kamu İhale Kurulu) ---
@app.tool(
description="Search Public Procurement Authority (KİK) decisions for procurement law and administrative disputes. Before using, read docs://tools/kik",
description="Search Public Procurement Authority (KİK) decisions for procurement law disputes",
annotations={
"readOnlyHint": True,
"openWorldHint": True,
@@ -1436,21 +1629,23 @@ async def get_anayasa_bireysel_basvuru_document_markdown(
)
async def search_kik_decisions(
karar_tipi: Literal["rbUyusmazlik", "rbDuzenleyici", "rbMahkeme"] = Field("rbUyusmazlik", description="Type of KIK Decision."),
karar_no: Optional[str] = Field(None, description="Decision Number (e.g., '2024/UH.II-1766')."),
karar_tarihi_baslangic: Optional[str] = Field(None, description="Decision Date Start (DD.MM.YYYY)."),
karar_tarihi_bitis: Optional[str] = Field(None, description="Decision Date End (DD.MM.YYYY)."),
basvuru_sahibi: Optional[str] = Field(None, description="Applicant."),
ihaleyi_yapan_idare: Optional[str] = Field(None, description="Procuring Entity."),
basvuru_konusu_ihale: Optional[str] = Field(None, description="Tender subject of the application."),
karar_metni: Optional[str] = Field(None, description="""
karar_no: str = Field("", description="Decision Number (e.g., '2024/UH.II-1766')."),
karar_tarihi_baslangic: str = Field("", description="Decision Date Start (DD.MM.YYYY)."),
karar_tarihi_bitis: str = Field("", description="Decision Date End (DD.MM.YYYY)."),
basvuru_sahibi: str = Field("", description="Applicant."),
ihaleyi_yapan_idare: str = Field("", description="Procuring Entity."),
basvuru_konusu_ihale: str = Field("", description="Tender subject of the application."),
karar_metni: str = Field("", description="""
Keyword/phrase in decision text. Advanced search operators supported:
- word1+word2 = AND logic (+anayasa +mahkeme -> both words required)
- +"required" -"excluded" = Include and exclude (+ihale -"iptal")
Examples: anayasa | +anayasa +mahkeme | +ihale -"iptal"
- "exact phrase" = Exact match ("kamu yararı" for exact phrase)
- OR/AND/wildcard operators supported
Examples: anayasa | +anayasa +mahkeme | +ihale -"iptal" | "tam cümle"
"""),
yil: Optional[str] = Field(None, description="Year of the decision."),
resmi_gazete_tarihi: Optional[str] = Field(None, description="Official Gazette Date (DD.MM.YYYY)."),
resmi_gazete_sayisi: Optional[str] = Field(None, description="Official Gazette Number."),
yil: str = Field("", description="Year of the decision."),
resmi_gazete_tarihi: str = Field("", description="Official Gazette Date (DD.MM.YYYY)."),
resmi_gazete_sayisi: str = Field("", description="Official Gazette Number."),
page: int = Field(1, ge=1, description="Results page number.")
) -> KikSearchResult:
"""Search Public Procurement Authority (KIK) decisions."""
@@ -1486,7 +1681,7 @@ async def search_kik_decisions(
return KikSearchResult(decisions=[], total_records=0, current_page=current_page_val)
@app.tool(
description="Retrieve full text of a Public Procurement Authority (KİK) decision in paginated Markdown format. Before using, read docs://tools/kik",
description="Get Public Procurement Authority (KİK) decision text in paginated Markdown format",
annotations={
"readOnlyHint": True,
"idempotentHint": True
@@ -1526,7 +1721,7 @@ async def get_kik_document_markdown(
is_paginated=False
)
@app.tool(
description="Search Competition Authority (Rekabet Kurumu) decisions for competition law and antitrust research. Before using, read docs://tools/rekabet",
description="Search Competition Authority (Rekabet Kurumu) decisions for competition law and antitrust",
annotations={
"readOnlyHint": True,
"openWorldHint": True,
@@ -1534,10 +1729,10 @@ async def get_kik_document_markdown(
}
)
async def search_rekabet_kurumu_decisions(
sayfaAdi: Optional[str] = Field(None, description="Search in decision title (Başlık)."),
YayinlanmaTarihi: Optional[str] = Field(None, description="Publication date (Yayım Tarihi), e.g., DD.MM.YYYY."),
PdfText: Optional[str] = Field(
None,
sayfaAdi: str = Field("", description="Search in decision title (Başlık)."),
YayinlanmaTarihi: str = Field("", description="Publication date (Yayım Tarihi), e.g., DD.MM.YYYY."),
PdfText: str = Field(
"",
description='Search in decision text. Use "\\"kesin cümle\\"" for precise matching.'
),
KararTuru: Literal[
@@ -1548,8 +1743,8 @@ async def search_rekabet_kurumu_decisions(
"Özelleştirme",
"Rekabet İhlali"
] = Field("ALL", description="Parameter description"),
KararSayisi: Optional[str] = Field(None, description="Decision number (Karar Sayısı)."),
KararTarihi: Optional[str] = Field(None, description="Decision date (Karar Tarihi), e.g., DD.MM.YYYY."),
KararSayisi: str = Field("", description="Decision number (Karar Sayısı)."),
KararTarihi: str = Field("", description="Decision date (Karar Tarihi), e.g., DD.MM.YYYY."),
page: int = Field(1, ge=1, description="Page number to fetch for the results list.")
) -> RekabetSearchResult:
"""Search Competition Authority decisions."""
@@ -1582,7 +1777,7 @@ async def search_rekabet_kurumu_decisions(
return RekabetSearchResult(decisions=[], retrieved_page_number=page, total_records_found=0, total_pages=0)
@app.tool(
description="Retrieve full text of a Competition Authority decision in paginated Markdown format. Before using, read docs://tools/rekabet",
description="Get Competition Authority decision text in paginated Markdown format",
annotations={
"readOnlyHint": True,
"idempotentHint": True
@@ -1606,7 +1801,7 @@ async def get_rekabet_kurumu_document(
# --- MCP Tools for Bedesten (Unified Search Across All Courts) ---
@app.tool(
description="Search Yargıtay, Danıştay, Local Courts, Appeals Courts, and KYB decisions",
description="Search multiple Turkish courts (Yargıtay, Danıştay, Local Courts, Appeals Courts, KYB)",
annotations={
"readOnlyHint": True,
"openWorldHint": True,
@@ -1614,7 +1809,7 @@ async def get_rekabet_kurumu_document(
}
)
async def search_bedesten_unified(
phrase: str = Field(..., description="Search phrase. Use \"exact phrase\" for precise matching"),
phrase: str = Field(..., description="Turkish search phrase. Supports +required -excluded \"exact phrase\" operators"),
court_types: List[BedestenCourtTypeEnum] = Field(
default=["YARGITAYKARARI", "DANISTAYKARAR"],
description="Court types: YARGITAYKARARI, DANISTAYKARAR, YERELHUKUK, ISTINAFHUKUK, KYB"
@@ -1622,8 +1817,8 @@ async def search_bedesten_unified(
pageSize: int = Field(10, ge=1, le=10, description="Results per page (1-10)"),
pageNumber: int = Field(1, ge=1, description="Page number"),
birimAdi: Optional[Union[YargitayBirimEnum, DanistayBirimEnum]] = Field(None, description="Chamber filter (optional)"),
kararTarihiStart: Optional[str] = Field(None, description="Start date (ISO 8601 format)"),
kararTarihiEnd: Optional[str] = Field(None, description="End date (ISO 8601 format)")
kararTarihiStart: str = Field("", description="Start date (ISO 8601 format)"),
kararTarihiEnd: str = Field("", description="End date (ISO 8601 format)")
) -> dict:
"""Search Turkish legal databases via unified Bedesten API."""
search_data = BedestenSearchData(
@@ -1665,7 +1860,7 @@ async def search_bedesten_unified(
raise
@app.tool(
description="Retrieve legal decision document from Bedesten API in Markdown format",
description="Get legal decision document from Bedesten API in Markdown format",
annotations={
"readOnlyHint": True,
"idempotentHint": True
@@ -1689,7 +1884,7 @@ async def get_bedesten_document_markdown(
# --- MCP Tools for Sayıştay (Turkish Court of Accounts) ---
@app.tool(
description="Search Sayıştay Genel Kurul decisions for audit and accountability regulations. Before using, read docs://tools/sayistay",
description="Search Sayıştay Genel Kurul decisions for audit and accountability regulations",
annotations={
"readOnlyHint": True,
"openWorldHint": True,
@@ -1697,11 +1892,11 @@ async def get_bedesten_document_markdown(
}
)
async def search_sayistay_genel_kurul(
karar_no: Optional[str] = Field(None, description="Decision number to search for (e.g., '5415')"),
karar_ek: Optional[str] = Field(None, description="Decision appendix number (max 99, e.g., '1')"),
karar_tarih_baslangic: Optional[str] = Field(None, description="See docs for details"),
karar_tarih_bitis: Optional[str] = Field(None, description="See docs for details"),
karar_tamami: Optional[str] = Field(None, description="See docs for details"),
karar_no: str = Field("", description="Decision number to search for (e.g., '5415')"),
karar_ek: str = Field("", description="Decision appendix number (max 99, e.g., '1')"),
karar_tarih_baslangic: str = Field("", description="Start date (DD.MM.YYYY)"),
karar_tarih_bitis: str = Field("", description="End date (DD.MM.YYYY)"),
karar_tamami: str = Field("", description="Full text search"),
start: int = Field(0, description="Starting record for pagination (0-based)"),
length: int = Field(10, description="Number of records per page (1-100)")
) -> GenelKurulSearchResponse:
@@ -1724,7 +1919,7 @@ async def search_sayistay_genel_kurul(
raise
@app.tool(
description="Search Sayıştay Temyiz Kurulu decisions with chamber filtering and comprehensive search criteria. Before using, read docs://tools/sayistay",
description="Search Sayıştay Temyiz Kurulu decisions with chamber filtering and comprehensive criteria",
annotations={
"readOnlyHint": True,
"openWorldHint": True,
@@ -1732,16 +1927,16 @@ async def search_sayistay_genel_kurul(
}
)
async def search_sayistay_temyiz_kurulu(
ilam_dairesi: DaireEnum = Field("ALL", description="See docs for details"),
yili: Optional[str] = Field(None, description="See docs for details"),
karar_tarih_baslangic: Optional[str] = Field(None, description="See docs for details"),
karar_tarih_bitis: Optional[str] = Field(None, description="See docs for details"),
kamu_idaresi_turu: KamuIdaresiTuruEnum = Field("ALL", description="See docs for details"),
ilam_no: Optional[str] = Field(None, description="Audit report number (İlam No, max 50 chars)"),
dosya_no: Optional[str] = Field(None, description="File number for the case"),
temyiz_tutanak_no: Optional[str] = Field(None, description="Appeals board meeting minutes number"),
temyiz_karar: Optional[str] = Field(None, description="See docs for details"),
web_karar_konusu: WebKararKonusuEnum = Field("ALL", description="See docs for details"),
ilam_dairesi: DaireEnum = Field("ALL", description="Audit chamber selection"),
yili: str = Field("", description="Year (YYYY)"),
karar_tarih_baslangic: str = Field("", description="Start date (DD.MM.YYYY)"),
karar_tarih_bitis: str = Field("", description="End date (DD.MM.YYYY)"),
kamu_idaresi_turu: KamuIdaresiTuruEnum = Field("ALL", description="Public admin type"),
ilam_no: str = Field("", description="Audit report number (İlam No, max 50 chars)"),
dosya_no: str = Field("", description="File number for the case"),
temyiz_tutanak_no: str = Field("", description="Appeals board meeting minutes number"),
temyiz_karar: str = Field("", description="Appeals decision text"),
web_karar_konusu: WebKararKonusuEnum = Field("ALL", description="Decision subject"),
start: int = Field(0, description="Starting record for pagination (0-based)"),
length: int = Field(10, description="Number of records per page (1-100)")
) -> TemyizKuruluSearchResponse:
@@ -1769,7 +1964,7 @@ async def search_sayistay_temyiz_kurulu(
raise
@app.tool(
description="Search Sayıştay Daire decisions with chamber filtering and subject categorization. Before using, read docs://tools/sayistay",
description="Search Sayıştay Daire decisions with chamber filtering and subject categorization",
annotations={
"readOnlyHint": True,
"openWorldHint": True,
@@ -1777,14 +1972,14 @@ async def search_sayistay_temyiz_kurulu(
}
)
async def search_sayistay_daire(
yargilama_dairesi: DaireEnum = Field("ALL", description="See docs for details"),
karar_tarih_baslangic: Optional[str] = Field(None, description="See docs for details"),
karar_tarih_bitis: Optional[str] = Field(None, description="See docs for details"),
ilam_no: Optional[str] = Field(None, description="Audit report number (İlam No, max 50 chars)"),
kamu_idaresi_turu: KamuIdaresiTuruEnum = Field("ALL", description="See docs for details"),
hesap_yili: Optional[str] = Field(None, description="See docs for details"),
web_karar_konusu: WebKararKonusuEnum = Field("ALL", description="See docs for details"),
web_karar_metni: Optional[str] = Field(None, description="See docs for details"),
yargilama_dairesi: DaireEnum = Field("ALL", description="Chamber selection"),
karar_tarih_baslangic: str = Field("", description="Start date (DD.MM.YYYY)"),
karar_tarih_bitis: str = Field("", description="End date (DD.MM.YYYY)"),
ilam_no: str = Field("", description="Audit report number (İlam No, max 50 chars)"),
kamu_idaresi_turu: KamuIdaresiTuruEnum = Field("ALL", description="Public admin type"),
hesap_yili: str = Field("", description="Fiscal year"),
web_karar_konusu: WebKararKonusuEnum = Field("ALL", description="Decision subject"),
web_karar_metni: str = Field("", description="Decision text search"),
start: int = Field(0, description="Starting record for pagination (0-based)"),
length: int = Field(10, description="Number of records per page (1-100)")
) -> DaireSearchResponse:
@@ -1810,7 +2005,7 @@ async def search_sayistay_daire(
raise
@app.tool(
description="Retrieve Sayıştay Genel Kurul decision document in Markdown format. Before using, read docs://tools/sayistay",
description="Get Sayıştay Genel Kurul decision document in Markdown format",
annotations={
"readOnlyHint": True,
"openWorldHint": False,
@@ -1833,7 +2028,7 @@ async def get_sayistay_genel_kurul_document_markdown(
raise
@app.tool(
description="Retrieve Sayıştay Temyiz Kurulu decision document in Markdown format. Before using, read docs://tools/sayistay",
description="Get Sayıştay Temyiz Kurulu decision document in Markdown format",
annotations={
"readOnlyHint": True,
"openWorldHint": False,
@@ -1856,7 +2051,7 @@ async def get_sayistay_temyiz_kurulu_document_markdown(
raise
@app.tool(
description="Retrieve Sayıştay Daire decision document in Markdown format. Before using, read docs://tools/sayistay",
description="Get Sayıştay Daire decision document in Markdown format",
annotations={
"readOnlyHint": True,
"openWorldHint": False,
@@ -2085,7 +2280,7 @@ async def check_government_servers_health() -> Dict[str, Any]:
# --- MCP Tools for KVKK ---
@app.tool(
description="Search KVKK decisions using Brave Search API for data protection authority decisions. Before using, read docs://tools/kvkk",
description="Search KVKK data protection authority decisions",
annotations={
"readOnlyHint": True,
"openWorldHint": True,
@@ -2093,7 +2288,7 @@ async def check_government_servers_health() -> Dict[str, Any]:
}
)
async def search_kvkk_decisions(
keywords: str = Field(..., description="Turkish keywords for KVKK decision search"),
keywords: str = Field(..., description="Turkish keywords. Supports +required -excluded \"exact phrase\" operators"),
page: int = Field(1, ge=1, le=50, description="Page number for results (1-50)."),
pageSize: int = Field(10, ge=1, le=20, description="Number of results per page (1-20).")
) -> KvkkSearchResult:
@@ -2122,7 +2317,7 @@ async def search_kvkk_decisions(
)
@app.tool(
description="Retrieve KVKK decision document in Markdown format with metadata extraction. Before using, read docs://tools/kvkk",
description="Get KVKK decision document in Markdown format with metadata extraction",
annotations={
"readOnlyHint": True,
"openWorldHint": False,
@@ -2130,7 +2325,7 @@ async def search_kvkk_decisions(
}
)
async def get_kvkk_document_markdown(
decision_url: str = Field(..., description="See docs for details"),
decision_url: str = Field(..., description="KVKK decision URL from search results"),
page_number: Union[int, str] = Field(1, description="Page number for paginated Markdown content (1-indexed, accepts int). Default is 1 (first 5,000 characters).")
) -> KvkkDocumentMarkdown:
"""Get KVKK decision as paginated Markdown."""
@@ -2499,6 +2694,8 @@ async def fetch(
logger.exception(f"Error fetching ChatGPT Deep Research document {id}")
raise
# --- Token Metrics Tool Removed for Optimization ---
def ensure_playwright_browsers():
"""Ensure Playwright browsers are installed for KIK tool functionality."""
try:
+1
View File
@@ -30,6 +30,7 @@ dependencies = [
"pypdf>=5.5.0",
"fastapi>=0.115.14",
"PyJWT>=2.8.0",
"tiktoken>=0.5.0",
]
[project.optional-dependencies]
+16 -16
View File
@@ -25,31 +25,31 @@ class RekabetKararTuruAdiEnum(str, Enum):
class RekabetKurumuSearchRequest(BaseModel):
"""Model for Rekabet Kurumu (Turkish Competition Authority) search request."""
sayfaAdi: Optional[str] = Field(None, description="Title")
YayinlanmaTarihi: Optional[str] = Field(None, description="Date")
PdfText: Optional[str] = Field(None, description="Text")
KararTuruID: Optional[RekabetKararTuruGuidEnum] = Field(RekabetKararTuruGuidEnum.TUMU, description="Type")
KararSayisi: Optional[str] = Field(None, description="No")
KararTarihi: Optional[str] = Field(None, description="Date")
sayfaAdi: str = Field("", description="Title")
YayinlanmaTarihi: str = Field("", description="Date")
PdfText: str = Field("", description="Text")
KararTuruID: RekabetKararTuruGuidEnum = Field(RekabetKararTuruGuidEnum.TUMU, description="Type")
KararSayisi: str = Field("", description="No")
KararTarihi: str = Field("", description="Date")
page: int = Field(1, ge=1, description="Page")
class RekabetDecisionSummary(BaseModel):
"""Model for a single Rekabet Kurumu decision summary from search results."""
publication_date: Optional[str] = Field(None, description="Pub date")
decision_number: Optional[str] = Field(None, description="Number")
decision_date: Optional[str] = Field(None, description="Date")
decision_type_text: Optional[str] = Field(None, description="Type")
title: Optional[str] = Field(None, description="Title")
decision_url: Optional[HttpUrl] = Field(None, description="URL")
karar_id: Optional[str] = Field(None, description="ID")
related_cases_url: Optional[HttpUrl] = Field(None, description="Cases URL")
publication_date: str = Field("", description="Pub date")
decision_number: str = Field("", description="Number")
decision_date: str = Field("", description="Date")
decision_type_text: str = Field("", description="Type")
title: str = Field("", description="Title")
decision_url: str = Field("", description="URL")
karar_id: str = Field("", description="ID")
related_cases_url: str = Field("", description="Cases URL")
class RekabetSearchResult(BaseModel):
"""Model for the overall search result for Rekabet Kurumu decisions."""
decisions: List[RekabetDecisionSummary]
total_records_found: Optional[int] = Field(None, description="Total")
total_records_found: int = Field(0, description="Total")
retrieved_page_number: int = Field(description="Page")
total_pages: Optional[int] = Field(None, description="Pages")
total_pages: int = Field(0, description="Pages")
class RekabetDocument(BaseModel):
"""
+21 -21
View File
@@ -16,14 +16,14 @@ class GenelKurulSearchRequest(BaseModel):
of the Turkish Court of Accounts, typically addressing interpretation of
audit and accountability regulations.
"""
karar_no: Optional[str] = Field(None, description="Decision no")
karar_ek: Optional[str] = Field(None, description="Appendix no")
karar_no: str = Field("", description="Decision no")
karar_ek: str = Field("", description="Appendix no")
karar_tarih_baslangic: Optional[str] = Field(None, description="Start year (YYYY)")
karar_tarih_baslangic: str = Field("", description="Start year (YYYY)")
karar_tarih_bitis: Optional[str] = Field(None, description="End year")
karar_tarih_bitis: str = Field("", description="End year")
karar_tamami: Optional[str] = Field(None, description="Value")
karar_tamami: str = Field("", description="Value")
# DataTables pagination
start: int = Field(0, description="Starting record for pagination (0-based)")
@@ -56,19 +56,19 @@ class TemyizKuruluSearchRequest(BaseModel):
"""
ilam_dairesi: DaireEnum = Field("ALL", description="Value")
yili: Optional[str] = Field(None, description="Value")
yili: str = Field("", description="Value")
karar_tarih_baslangic: Optional[str] = Field(None, description="Value")
karar_tarih_baslangic: str = Field("", description="Value")
karar_tarih_bitis: Optional[str] = Field(None, description="End year")
karar_tarih_bitis: str = Field("", description="End year")
kamu_idaresi_turu: KamuIdaresiTuruEnum = Field("ALL", description="Value")
ilam_no: Optional[str] = Field(None, description="Audit report number (İlam No, max 50 chars)")
dosya_no: Optional[str] = Field(None, description="File number for the case")
temyiz_tutanak_no: Optional[str] = Field(None, description="Appeals board meeting minutes number")
ilam_no: str = Field("", description="Audit report number (İlam No, max 50 chars)")
dosya_no: str = Field("", description="File number for the case")
temyiz_tutanak_no: str = Field("", description="Appeals board meeting minutes number")
temyiz_karar: Optional[str] = Field(None, description="Value")
temyiz_karar: str = Field("", description="Value")
web_karar_konusu: WebKararKonusuEnum = Field("ALL", description="Value")
@@ -103,19 +103,19 @@ class DaireSearchRequest(BaseModel):
"""
yargilama_dairesi: DaireEnum = Field("ALL", description="Value")
karar_tarih_baslangic: Optional[str] = Field(None, description="Value")
karar_tarih_baslangic: str = Field("", description="Value")
karar_tarih_bitis: Optional[str] = Field(None, description="End year")
karar_tarih_bitis: str = Field("", description="End year")
ilam_no: Optional[str] = Field(None, description="Audit report number (İlam No, max 50 chars)")
ilam_no: str = Field("", description="Audit report number (İlam No, max 50 chars)")
kamu_idaresi_turu: KamuIdaresiTuruEnum = Field("ALL", description="Value")
hesap_yili: Optional[str] = Field(None, description="Value")
hesap_yili: str = Field("", description="Value")
web_karar_konusu: WebKararKonusuEnum = Field("ALL", description="Value")
web_karar_metni: Optional[str] = Field(None, description="Value")
web_karar_metni: str = Field("", description="Value")
# DataTables pagination
start: int = Field(0, description="Starting record for pagination (0-based)")
@@ -127,7 +127,7 @@ class DaireDecision(BaseModel):
yargilama_dairesi: int = Field(..., description="Chamber number (1-8)")
karar_tarih: str = Field(..., description="Decision date in DD.MM.YYYY format")
karar_no: str = Field(..., description="Decision number")
ilam_no: Optional[str] = Field(None, description="Audit report number (may be null)")
ilam_no: str = Field("", description="Audit report number (may be null)")
madde_no: int = Field(..., description="Article/item number within the decision")
kamu_idaresi_turu: str = Field(..., description="Public administration type")
hesap_yili: int = Field(..., description="Account year being audited")
@@ -155,6 +155,6 @@ class SayistayDocumentMarkdown(BaseModel):
decision_id: str = Field(..., description="Unique decision identifier")
decision_type: str = Field(..., description="Value")
source_url: str = Field(..., description="Original URL where the document was retrieved")
markdown_content: Optional[str] = Field(None, description="Full decision text converted to Markdown format")
retrieval_date: Optional[str] = Field(None, description="Date when document was retrieved (ISO format)")
error_message: Optional[str] = Field(None, description="Error message if document retrieval failed")
markdown_content: str = Field("", description="Full decision text converted to Markdown format")
retrieval_date: str = Field("", description="Date when document was retrieved (ISO format)")
error_message: str = Field("", description="Error message if document retrieval failed")
-4
View File
@@ -37,8 +37,6 @@ class YargitayDetailedSearchRequest(BaseModel):
arananKelime: Optional[str] = Field("", description="Turkish keywords (supports +word -word \"phrase\" operators)")
# Department/Board selection - Complete Court of Cassation chamber hierarchy
birimYrgKurulDaire: Optional[str] = Field("ALL", description="Chamber (ALL or specific chamber name)")
birimYrgHukukDaire: Optional[str] = Field("", description="Legacy field")
birimYrgCezaDaire: Optional[str] = Field("", description="Legacy field")
esasYil: Optional[str] = Field("", description="Case year (YYYY)")
esasIlkSiraNo: Optional[str] = Field("", description="Start case no")
@@ -51,8 +49,6 @@ class YargitayDetailedSearchRequest(BaseModel):
baslangicTarihi: Optional[str] = Field("", description="Start date (DD.MM.YYYY)")
bitisTarihi: Optional[str] = Field("", description="End date (DD.MM.YYYY)")
siralama: Optional[str] = Field("3", description="Sort by (1=case, 2=decision, 3=date)")
siralamaDirection: Optional[str] = Field("desc", description="Direction (asc/desc)")
pageSize: int = Field(10, ge=1, le=10, description="Results per page (1-100)")
pageNumber: int = Field(1, ge=1, description="Page number (1-indexed)")