From 801a453a2feed0e0123ae62a1446a0c34ed05130 Mon Sep 17 00:00:00 2001 From: G0DSEND016 Date: Mon, 16 Mar 2026 03:16:02 +0100 Subject: [PATCH] fix pydantic schemas, fix func_tools --- api/fetch_api_data.py | 13 - api/schemas.py | 458 ++++++++++++++++++++++----------- api/tools/admin_proceedings.py | 32 +-- api/tools/civil_proceedings.py | 20 +- api/tools/contracts.py | 18 +- api/tools/courts.py | 24 +- api/tools/decisions.py | 24 +- api/tools/judges.py | 24 +- core/system_prompt.py | 263 +++++++++++-------- 9 files changed, 512 insertions(+), 364 deletions(-) diff --git a/api/fetch_api_data.py b/api/fetch_api_data.py index 9ca9269..e2c8f46 100644 --- a/api/fetch_api_data.py +++ b/api/fetch_api_data.py @@ -1,7 +1,6 @@ import httpx import logging import json -from pydantic import BaseModel from cachetools import TTLCache from typing import Callable from tenacity import retry, stop_after_attempt, wait_exponential @@ -35,18 +34,6 @@ _client = httpx.AsyncClient( max_keepalive_connections=HTTP_MAX_KEEPALIVE), ) -def docstring_from_model(model: type[BaseModel]): - def decorator(func): - if func.__doc__: - func.__doc__ = func.__doc__.format( - params="\n".join( - f"\t\t- {name}: {field.description or 'No description'}" - for name, field in model.model_fields.items() - ) - ) - return func - return decorator - _log_callback: Callable[[str], None] | None = None def set_log_callback(cb: Callable[[str], None] | None): diff --git a/api/schemas.py b/api/schemas.py index a466abd..bf138cc 100644 --- a/api/schemas.py +++ b/api/schemas.py @@ -1,178 +1,342 @@ -from pydantic import BaseModel, Field, conint -from typing import Optional, List, Literal +from typing import Annotated, List, Literal, Optional +from pydantic import BaseModel, Field, field_validator -# ================================================================================================================= -# COURT SCHEMAS -# ================================================================================================================= +class SortableMixin(BaseModel): + sortProperty: Annotated[ + Optional[str], + Field(default=None, description="Atribút, podľa ktorého budú záznamy zoradené") + ] = None + sortDirection: Annotated[ + Literal["ASC", "DESC"], + Field(default="ASC", description="Smer zoradenia (ASC alebo DESC)") + ] = "ASC" -class Court(BaseModel): - query: str = Field(None, description="Search term or phrase to help find information") - typSuduFacetFilter: Optional[List[str]] = Field(None, description="Court types (facet filter), e.g., Okresný súd, Krajský súd, Najvyšší súd SR, Špecializovaný trestný súd, etc.") - krajFacetFilter: Optional[List[str]] = Field(None, description="Regions (facet filter), e.g., Bratislavský kraj, Košický kraj, Prešovský kraj, etc.") - okresFacetFilter: Optional[List[str]] = Field(None, description="Districts (facet filter), e.g., Okres Banská Bystrica, Okres Bratislava I, Okres Košice I, etc.") - zahrnutZaniknuteSudy: Optional[bool] = Field(None, description="Include defunct/inactive courts (True) or only currently active courts (False)") - page: Optional[conint(ge=1)] = Field(None, description="Requested page number (starting from 1)") - size: Optional[conint(ge=1)] = Field(None, description="Number of results per page") - indexDatumOd: Optional[str] = Field(None, description="Index date from (format YYYY.MM.DD or DD.MM.YYYY)") - indexDatumDo: Optional[str] = Field(None, description="Index date to (format YYYY.MM.DD or DD.MM.YYYY)") - sortProperty: Optional[str] = Field(None, description="Field to sort the output by") - sortDirection: Literal["ASC", "DESC"] = Field("ASC", description="Sort direction (ASC or DESC)") -class CourtID(BaseModel): - id: str = Field(..., description="Court identifier (e.g., sud_175)") +class PaginatedRequest(SortableMixin): + page: Annotated[ + Optional[int], + Field(default=None, ge=0, description="Číslo stránky (začína od 0, nie od 1!)") + ] = None + size: Annotated[ + Optional[int], + Field(default=None, ge=1, description="Počet záznamov na stránku") + ] = None + + +#################################################################################################################### +# .../v1/sud +#################################################################################################################### + +class CourtSearch(PaginatedRequest): + """Zoznam súdov s voliteľnými filtrami. GET /v1/sud""" + query: Annotated[ + Optional[str], + Field(default=None, description="Hľadané slovo alebo slovné spojenie") + ] = None + typSuduFacetFilter: Annotated[ + Optional[List[str]], + Field(default=None, description="Typ súdu (fazetový filter), napr. ['Okresný súd', 'Krajský súd']") + ] = None + krajFacetFilter: Annotated[ + Optional[List[str]], + Field(default=None, description="Kraj (fazetový filter), napr. ['Bratislavský kraj']") + ] = None + okresFacetFilter: Annotated[ + Optional[List[str]], + Field(default=None, description="Okres (fazetový filter), napr. ['Okres Bratislava I']") + ] = None + zahrnutZaniknuteSudy: Annotated[ + Optional[bool], + Field(default=None, description="True = zahrnúť zaniknuté súdy, False = len aktívne") + ] = None + indexDatumOd: Annotated[ + Optional[str], + Field(default=None, description="Dátum indexácie od") + ] = None + indexDatumDo: Annotated[ + Optional[str], + Field(default=None, description="Dátum indexácie do") + ] = None + + +class CourtByID(BaseModel): + """Jeden súd podľa ID. GET /v1/sud/{id}""" + id: Annotated[str, Field(description="Identifikátor súdu, napr. 'sud_175'")] + + @field_validator("id") + @classmethod + def normalize(cls, v: str) -> str: + v = v.strip() + return f"sud_{v}" if v.isdigit() else v + class CourtAutocomplete(BaseModel): - query: str = Field(None, description="Search term or phrase to help find information") - limit: Optional[conint(ge=1)] = Field(None, description="Limits the number of returned results (pagination)") + """Autocomplete pre názvy súdov. GET /v1/sud/autocomplete""" + query: Annotated[Optional[str], Field(default=None, description="Hľadané slovo")] = None + limit: Annotated[Optional[int], Field(default=None, ge=1, description="Maximálny počet výsledkov")] = None -# ================================================================================================================= -# JUDGE SCHEMAS -# ================================================================================================================= +#################################################################################################################### +# .../v1/sudca +#################################################################################################################### -class Judge(BaseModel): - query: str = Field(None, description="Search term or phrase to help find information") - funkciaFacetFilter: Optional[List[str]] = Field(None, description="Judge function, role or position related to a judge (facet filter) e.g., Sudca, Podpredseda, Predseda, Hosťujúci sudca") - typSuduFacetFilter: Optional[List[str]] = Field(None, description="Court types (facet filter), e.g., Okresný súd, Krajský súd, Najvyšší súd SR, Špecializovaný trestný súd, etc.") - krajFacetFilter: Optional[List[str]] = Field(None, description="Regions (facet filter), e.g., Bratislavský kraj, Košický kraj, Prešovský kraj, etc.") - okresFacetFilter: Optional[List[str]] = Field(None, description="Districts (facet filter), e.g., Okres Banská Bystrica, Okres Bratislava I, Okres Košice I, etc.") - stavZapisuFacetFilter: Optional[List[str]] = Field(None, description="Record status filter (facet filter) to specify which court records to include. Options (do not translate): label.sudca.aktivny, label.sudca.odvolany, label.sudca.vymazany, label.sudca.prerusenie vykonu - poberatel, label.sudca.prerusenie vykonu - ina funkce") - guidSud: Optional[str] = Field(None, description="Court identifier (e.g., sud_100)") - indexDatumOd: Optional[str] = Field(None, description="Index date from (format YYYY.MM.DD or DD.MM.YYYY)") - indexDatumDo: Optional[str] = Field(None, description="Index date to (format YYYY.MM.DD or DD.MM.YYYY)") - page: Optional[conint(ge=1)] = Field(None, description="Requested page number (starting from 1)") - size: Optional[conint(ge=1)] = Field(None, description="Number of results per page") - sortProperty: Optional[str] = Field(None, description="Field to sort the output by") - sortDirection: Literal["ASC", "DESC"] = Field("ASC", description="Sort direction (ASC or DESC)") +class JudgeSearch(BaseModel): + """Zoznam sudcov s voliteľnými filtrami. GET /v1/sudca""" + query: Annotated[ + Optional[str], + Field(default=None, description="Hľadané slovo alebo slovné spojenie") + ] = None + funkciaFacetFilter: Annotated[ + Optional[List[str]], + Field(default=None, description="Funkcia (fazetový filter), napr. ['Sudca', 'Predseda', 'Podpredseda']") + ] = None + typSuduFacetFilter: Annotated[ + Optional[List[str]], + Field(default=None, description="Typ súdu (fazetový filter)") + ] = None + krajFacetFilter: Annotated[ + Optional[List[str]], + Field(default=None, description="Kraj (fazetový filter), napr. ['Bratislavský kraj']") + ] = None + okresFacetFilter: Annotated[ + Optional[List[str]], + Field(default=None, description="Okres (fazetový filter)") + ] = None + stavZapisuFacetFilter: Annotated[ + Optional[List[str]], + Field( + default=None, + description=( + "Stav zápisu — neprekladať hodnoty: " + "'label.sudca.aktivny', 'label.sudca.odvolany', " + "'label.sudca.vymazany', " + "'label.sudca.prerusenie vykonu - poberatel', " + "'label.sudca.prerusenie vykonu - ina funkce'" + ) + ) + ] = None + guidSud: Annotated[ + Optional[str], + Field(default=None, description="Identifikátor súdu, napr. 'sud_100'") + ] = None + indexDatumOd: Annotated[ + Optional[str], + Field(default=None, description="Dátum indexácie od") + ] = None + indexDatumDo: Annotated[ + Optional[str], + Field(default=None, description="Dátum indexácie do") + ] = None + page: Annotated[ + Optional[int], + Field(default=None, ge=0, description="Číslo stránky (začína od 0)") + ] = None + size: Annotated[ + Optional[int], + Field(default=None, ge=1, description="Počet záznamov na stránku") + ] = None + + +class JudgeByID(BaseModel): + """Jeden sudca podľa ID. GET /v1/sudca/{id}""" + id: Annotated[str, Field(description="Identifikátor sudcu, napr. 'sudca_1'")] + + @field_validator("id") + @classmethod + def normalize(cls, v: str) -> str: + v = v.strip() + return f"sudca_{v}" if v.isdigit() else v -class JudgeID(BaseModel): - id: str = Field(..., description="Judge identifier (e. g., sudca_1)") class JudgeAutocomplete(BaseModel): - query: str = Field(None, description="Search term or phrase to help find information") - guidSud: Optional[str] = Field(None, description="Court identifier (e.g., sud_100)") - limit: Optional[conint(ge=1)] = Field(None, description="Limits the number of returned results (pagination)") + """ + Autocomplete pre mená sudcov. GET /v1/sudca/autocomplete + PREFEROVANÝ nástroj pri hľadaní sudcu podľa mena — použiť pred JudgeSearch! + """ + query: Annotated[ + Optional[str], + Field(default=None, description="Čiastočné meno sudcu") + ] = None + guidSud: Annotated[ + Optional[str], + Field(default=None, description="Identifikátor súdu, napr. 'sud_100'") + ] = None + limit: Annotated[ + Optional[int], + Field(default=None, ge=1, description="Maximálny počet návrhov") + ] = None -# ================================================================================================================= -# CONTRACTS SCHEMAS -# ================================================================================================================= +#################################################################################################################### +# .../v1/rozhodnutie +#################################################################################################################### -class Contracts(BaseModel): - query: str = Field(None, description="Search term or phrase to help find information") - typDokumentuFacetFilter: Optional[List[str]] = Field(None, description="Contract's type (facet filter), e.g., FAKTURA, OBJEDNAVKA, ZMLUVA, DODATOK") - odberatelFacetFilter: Optional[List[str]] = Field(None, description="Subscriber filter. Examples: 'Krajský súd v Bratislave', 'Okresný súd Banská Bystrica', 'Špecializovaný trestný súd', Krajský súd v Košiciach, etc.") - dodavatelFacetFilter: Optional[List[str]] = Field(None, description="Supplier filter. Examples: Slovak Telekom, a.s., Wolters Kluwer s.r.o., Tibor Varga TSV Papier") - hodnotaZmluvyFacetFilter: Optional[List[str]] = Field(None, description="Contract value range filter. Options: '0-1000', '1000-20000', '20000-100000', '100000-500000', 'Viac ako 500000'") - datumZverejeneniaOd: Optional[str] = Field(None, description="Index date from (format YYYY-MM-DD)") - datumZverejeneniaDo: Optional[str] = Field(None, description="Index date to (format YYYY-MM-DD)") - guidSud: Optional[str] = Field(None, description="Court identifier (e.g., sud_100)") - indexDatumOd: Optional[str] = Field(None, description="Index date from (format YYYY-MM-DD or DD-MM-YYYY)") - indexDatumDo: Optional[str] = Field(None, description="Index date to (format YYYY-MM-DD or DD-MM-YYYY)") - sortProperty: Optional[str] = Field(None, description="Field to sort the output by") - sortDirection: Literal["ASC", "DESC"] = Field("ASC", description="Sort direction (ASC or DESC)") - page: Optional[conint(ge=1)] = Field(None, description="Requested page number (starting from 1)") - size: Optional[conint(ge=1)] = Field(None, description="Number of results per page") - -class ContractID(BaseModel): - idZmluvy: str = Field(..., description="Contract identifier (e. g., 2156252)") - -class ContractAutocomplete(BaseModel): - query: str = Field(None, description="Search term or phrase to help find information") - guidSud: Optional[str] = Field(None, description="Court identifier (e.g., sud_100)") - limit: Optional[conint(ge=1)] = Field(None, description="Limits the number of returned results (pagination)") +class DecisionSearch(PaginatedRequest): + """Zoznam rozhodnutí s voliteľnými filtrami. GET /v1/rozhodnutie""" + query: Annotated[Optional[str], Field(default=None, description="Hľadané slovo alebo slovné spojenie")] = None + typSuduFacetFilter: Annotated[Optional[List[str]], Field(default=None, description="Typ súdu (fazetový filter)")] = None + krajFacetFilter: Annotated[Optional[List[str]], Field(default=None, description="Kraj (fazetový filter)")] = None + okresFacetFilter: Annotated[Optional[List[str]], Field(default=None, description="Okres (fazetový filter)")] = None + oblastPravnejUpravyFacetFilter: Annotated[ + Optional[List[str]], + Field(default=None, description="Oblasť právnej úpravy (fazetový filter)") + ] = None + podOblastPravnejUpravyFacetFilter: Annotated[ + Optional[List[str]], + Field(default=None, description="Podoblasť právnej úpravy (fazetový filter)") + ] = None + formaRozhodnutiaFacetFilter: Annotated[ + Optional[List[str]], + Field(default=None, description="Forma rozhodnutia, napr. ['Uznesenie', 'Rozsudok', 'Platobný rozkaz']") + ] = None + povahaRozhodnutiaFacetFilter: Annotated[ + Optional[str], + Field(default=None, description="Povaha rozhodnutia") + ] = None + odkazovanePredpisy: Annotated[ + Optional[str], + Field(default=None, description="Odkazované predpisy") + ] = None + vydaniaOd: Annotated[Optional[str], Field(default=None, description="Vydanie od (DD.MM.YYYY)")] = None + vydaniaDo: Annotated[Optional[str], Field(default=None, description="Vydanie do (DD.MM.YYYY)")] = None + ecli: Annotated[Optional[str], Field(default=None, description="ECLI identifikátor, napr. 'ECLI:SK:OSPO:1965:8114010264.1'")] = None + spisovaZnacka: Annotated[Optional[str], Field(default=None, description="Spisová značka, napr. '7C/221/1991'")] = None + cisloSpisu: Annotated[Optional[str], Field(default=None, description="Identifikačné číslo spisu")] = None + guidSudca: Annotated[Optional[str], Field(default=None, description="Identifikátor sudcu, napr. 'sudca_1'")] = None + guidSud: Annotated[Optional[str], Field(default=None, description="Identifikátor súdu, napr. 'sud_100'")] = None + indexDatumOd: Annotated[Optional[str], Field(default=None, description="Dátum indexácie od")] = None + indexDatumDo: Annotated[Optional[str], Field(default=None, description="Dátum indexácie do")] = None -# ================================================================================================================= -# DECISION SCHEMAS -# ================================================================================================================= +class DecisionByID(BaseModel): + """Jedno rozhodnutie podľa ID. GET /v1/rozhodnutie/{id}""" + id: Annotated[str, Field(description="Identifikátor rozhodnutia")] -class Decision(BaseModel): - query: str = Field(None, description="Search term or phrase to help find information") - typSuduFacetFilter: Optional[List[str]] = Field(None, description="Court types (facet filter), e.g., Okresný súd, Krajský súd, Najvyšší súd SR, Špecializovaný trestný súd, etc.") - krajFacetFilter: Optional[List[str]] = Field(None, description="Regions (facet filter), e.g., Bratislavský kraj, Košický kraj, Prešovský kraj, etc.") - okresFacetFilter: Optional[List[str]] = Field(None, description="Districts (facet filter), e.g., Okres Banská Bystrica, Okres Bratislava I, Okres Košice I, etc.") - odkazovanePredpisy: Optional[str] = Field(None, description="Referenced regulations") - oblastPravnejUpravyFacetFilter: Optional[List[str]] = Field(None, description="Area of legal regulation (facet filter), e.g., Občianske právo, Rodinné právo, Obchodné právo, Trestné právo, Správne právo") - podOblastPravnejUpravyFacetFilter: Optional[List[str]] = Field(None, description="Sub-area of legal regulation (facet filter), e.g., Ostatné, Spotrebiteľské zmluvy, Spätvzatie, Zmluvy, Rozvod, Konkurz, etc.") - formaRozhodnutiaFacetFilter: Optional[List[str]] = Field(None, description="Form of decision (facet filter), e.g., Uznesenie, Rozsudok, Platobný rozkaz, Rozhodnutie, Rozsudok pre zmeškanie, Opatrenie, Príkaz, etc.") - povahaRozhodnutiaFacetFilter: Optional[str] = Field(None, description="Nature of decision, (e.g., Prvostupňové nenapadnuté opravnými prostriedkami, Potvrdzujúce, Potvrdené, Zmenené, Odmietajúce podanie, etc.)") - vydaniaOd: Optional[str] = Field(None, description="Issue date from (format DD.MM.YYYY)") - vydaniaDo: Optional[str] = Field(None, description="Issue date to (format DD.MM.YYYY)") - ecli: Optional[str] = Field(None, description="ECLI identifier (e.g., ECLI:SK:OSPO:1965:8114010264.1)") - spisovaZnacka: Optional[str] = Field(None, description="Case reference number (e.g., 7C/221/1991, 0Er/1966/1998)") - cisloSpisu: Optional[str] = Field(None, description="File identification number (e.g., 'identifikacneCislo': '8114010264')") - guidSudca: Optional[str] = Field(None, description="Judge identifier (e.g., sudca_1)") - guidSud: Optional[str] = Field(None, description="Court identifier (e.g., sud_100)") - indexDatumOd: Optional[str] = Field(None, description="Index date from (format YYYY-MM-DD)") - indexDatumDo: Optional[str] = Field(None, description="Index date to (format YYYY-MM-DD)") - sortProperty: Optional[str] = Field(None, description="Field to sort the output by") - sortDirection: Literal["ASC", "DESC"] = Field("ASC", description="Sort direction (ASC or DESC)") - page: Optional[conint(ge=1)] = Field(None, description="Requested page number (starting from 1)") - size: Optional[conint(ge=1)] = Field(None, description="Number of results per page") - -class DecisionID(BaseModel): - id: str = Field(..., description="Decision identifier (e.g., a3310194-e9ac-4e6b-bfbd-25c40f26938b:1097de02-3389-405b-98a4-df7abea3f9ec)") class DecisionAutocomplete(BaseModel): - query: str = Field(None, description="Search term or phrase to help find information") - guidSud: Optional[str] = Field(None, description="Court identifier (e.g., sud_100)") - limit: Optional[conint(ge=1)] = Field(None, description="Limits the number of returned results (pagination)") + """Autocomplete pre rozhodnutia. GET /v1/rozhodnutie/autocomplete""" + query: Annotated[Optional[str], Field(default=None, description="Hľadané slovo")] = None + guidSud: Annotated[Optional[str], Field(default=None, description="Identifikátor súdu")] = None + limit: Annotated[Optional[int], Field(default=None, ge=1, description="Maximálny počet výsledkov")] = None -# ================================================================================================================= -# CIVIL PROCEEDINGS SCHEMAS -# ================================================================================================================= +#################################################################################################################### +# .../v1/zmluvy +#################################################################################################################### -class CivilProceedings(BaseModel): - query: str = Field(None, description="Search term or phrase to help find information") - typSuduFacetFilter: Optional[List[str]] = Field(None, description="Court types (facet filter), e.g., Okresný súd, Krajský súd, Najvyšší súd SR, Špecializovaný trestný súd, etc.") - krajFacetFilter: Optional[List[str]] = Field(None, description="Regions (facet filter), e.g., Bratislavský kraj, Košický kraj, Prešovský kraj, etc.") - okresFacetFilter: Optional[List[str]] = Field(None, description="Districts (facet filter), e.g., Okres Banská Bystrica, Okres Bratislava I, Okres Košice I, etc.") - usekFacetFilter: Optional[List[str]] = Field(None, description="List of sections (facet filter), e.g., C, O, S") - formaUkonuFacetFilter: Optional[List[str]] = Field(None, description="List of action types (facet filter), e.g., Pojednávanie bez rozhodnutia, Pojednávanie a rozhodnutie, Verejné vyhlásenie rozsudku, Vyhlásenie rozsudku, Predbežné prejednanie sporu") - pojednavaniaOd: Optional[str] = Field(None, description="Hearing date from (DD.MM.YYYY)") - pojednavaniaDo: Optional[str] = Field(None, description="Hearing date to (DD.MM.YYYY)") - guidSudca: Optional[str] = Field(None, description="Judge identifier (e.g., sudca_1)") - guidSud: Optional[str] = Field(None, description="Court identifier (e.g., sud_100)") - spisovaZnacka: Optional[str] = Field(None, description="Case reference number (e.g., 7C/221/1991, 0Er/1966/1998)") - verejneVyhlasenie: Optional[bool] = Field(None,description="Flag indicating whether the announcement is public") - indexDatumOd: Optional[str] = Field(None, description="Index date from (format YYYY-MM-DD)") - indexDatumDo: Optional[str] = Field(None, description="Index date to (format YYYY-MM-DD)") - sortProperty: Optional[str] = Field(None, description="Field to sort the output by") - sortDirection: Literal["ASC", "DESC"] = Field("ASC", description="Sort direction (ASC or DESC)") - page: Optional[conint(ge=1)] = Field(None, description="Requested page number (starting from 1)") - size: Optional[conint(ge=1)] = Field(None, description="Number of results per page") +class ContractSearch(PaginatedRequest): + """Zoznam zmlúv s voliteľnými filtrami. GET /v1/zmluvy""" + query: Annotated[Optional[str], Field(default=None, description="Hľadané slovo alebo slovné spojenie")] = None + typDokumentuFacetFilter: Annotated[ + Optional[List[str]], + Field(default=None, description="Typ dokumentu, napr. ['FAKTURA', 'OBJEDNAVKA', 'ZMLUVA', 'DODATOK']") + ] = None + odberatelFacetFilter: Annotated[ + Optional[List[str]], + Field(default=None, description="Odberateľ (súd), napr. ['Krajský súd v Bratislave']") + ] = None + dodavatelFacetFilter: Annotated[ + Optional[List[str]], + Field(default=None, description="Dodávateľ, napr. ['Slovak Telekom, a.s.']") + ] = None + hodnotaZmluvyFacetFilter: Annotated[ + Optional[List[str]], + Field(default=None, description="Hodnota zmluvy: '0-1000', '1000-20000', '20000-100000', '100000-500000', 'Viac ako 500000'") + ] = None + datumZverejeneniaOd: Annotated[Optional[str], Field(default=None, description="Dátum zverejnenia od")] = None + datumZverejeneniaDo: Annotated[Optional[str], Field(default=None, description="Dátum zverejnenia do")] = None + guidSud: Annotated[Optional[str], Field(default=None, description="Identifikátor súdu, napr. 'sud_100'")] = None + indexDatumOd: Annotated[Optional[str], Field(default=None, description="Dátum indexácie od")] = None + indexDatumDo: Annotated[Optional[str], Field(default=None, description="Dátum indexácie do")] = None + + +class ContractByID(BaseModel): + """Jedna zmluva podľa ID. GET /v1/zmluvy/{idZmluvy}""" + idZmluvy: Annotated[str, Field(description="Identifikátor zmluvy, napr. '2156252'")] + + +class ContractAutocomplete(BaseModel): + """Autocomplete pre zmluvy. GET /v1/zmluvy/autocomplete""" + query: Annotated[Optional[str], Field(default=None, description="Hľadané slovo")] = None + guidSud: Annotated[Optional[str], Field(default=None, description="Identifikátor súdu")] = None + limit: Annotated[Optional[int], Field(default=None, ge=1, description="Maximálny počet výsledkov")] = None + + +#################################################################################################################### +# .../v1/obcianPojednavania +#################################################################################################################### + +class CivilProceedingsSearch(PaginatedRequest): + """Zoznam občianskych pojednávaní. GET /v1/obcianPojednavania""" + query: Annotated[Optional[str], Field(default=None, description="Hľadané slovo alebo slovné spojenie")] = None + typSuduFacetFilter: Annotated[Optional[List[str]], Field(default=None, description="Typ súdu (fazetový filter)")] = None + krajFacetFilter: Annotated[Optional[List[str]], Field(default=None, description="Kraj (fazetový filter)")] = None + okresFacetFilter: Annotated[Optional[List[str]], Field(default=None, description="Okres (fazetový filter)")] = None + usekFacetFilter: Annotated[ + Optional[List[str]], + Field(default=None, description="Úsek (fazetový filter), napr. ['C', 'O', 'S']") + ] = None + formaUkonuFacetFilter: Annotated[ + Optional[List[str]], + Field(default=None, description="Forma úkonu, napr. ['Pojednávanie bez rozhodnutia', 'Pojednávanie a rozhodnutie']") + ] = None + pojednavaniaOd: Annotated[Optional[str], Field(default=None, description="Pojednávania od (DD.MM.YYYY)")] = None + pojednavaniaDo: Annotated[Optional[str], Field(default=None, description="Pojednávania do (DD.MM.YYYY)")] = None + guidSudca: Annotated[Optional[str], Field(default=None, description="Identifikátor sudcu, napr. 'sudca_1'")] = None + guidSud: Annotated[Optional[str], Field(default=None, description="Identifikátor súdu, napr. 'sud_100'")] = None + spisovaZnacka: Annotated[Optional[str], Field(default=None, description="Spisová značka, napr. '7C/221/1991'")] = None + verejneVyhlasenie: Annotated[Optional[bool], Field(default=None, description="Verejné vyhlásenie")] = None + indexDatumOd: Annotated[Optional[str], Field(default=None, description="Dátum indexácie od")] = None + indexDatumDo: Annotated[Optional[str], Field(default=None, description="Dátum indexácie do")] = None + + +class CivilProceedingsByID(BaseModel): + """Jedno pojednávanie podľa ID. GET /v1/obcianPojednavania/{id}""" + id: Annotated[str, Field(description="Identifikátor, napr. '121e4d31-695e-41e1-9191-7c9ad5d8d484'")] -class CivilProceedingsID(BaseModel): - id: str = Field(..., description="Identifier (e.g., 121e4d31-695e-41e1-9191-7c9ad5d8d484)") class CivilProceedingsAutocomplete(BaseModel): - query: str = Field(None, description="Search term or phrase to help find information") - guidSud: Optional[str] = Field(None, description="Court identifier (e.g., sud_100)") - guidSudca: Optional[str] = Field(None, description="Judge identifier (e.g., sudca_1)") - verejneVyhlasenie: Optional[bool] = Field(None,description="Flag indicating whether the announcement is public") - limit: Optional[conint(ge=1)] = Field(None, description="Limits the number of returned results (pagination)") + """Autocomplete pre občianske pojednávania. GET /v1/obcianPojednavania/autocomplete""" + query: Annotated[Optional[str], Field(default=None, description="Hľadané slovo")] = None + guidSud: Annotated[Optional[str], Field(default=None, description="Identifikátor súdu")] = None + guidSudca: Annotated[Optional[str], Field(default=None, description="Identifikátor sudcu")] = None + verejneVyhlasenie: Annotated[Optional[bool], Field(default=None, description="Verejné vyhlásenie")] = None + limit: Annotated[Optional[int], Field(default=None, ge=1, description="Maximálny počet výsledkov")] = None -# ================================================================================================================= -# ADMINISTRATIVE PROCEEDINGS SCHEMAS -# ================================================================================================================= +#################################################################################################################### +# .../v1/spravneKonanie +#################################################################################################################### -class AdministrativeProceedings(BaseModel): - query: str = Field(None, description="Search term or phrase to help find information") - druhFacetFilter: Optional[List[str]] = Field(None, description="List of types (faceted filter), e.g., Konanie o inom správnom delikte podľa § 27 ods. 1 písm. b) ZZTP, onanie o inom správnom delikte podľa § 26 ods. 1 písm. a) ZZTP v znení účinnom od 1. júla 2018, etc.") - datumPravoplatnostiOd: Optional[str] = Field(None, description="Validity date from (DD.MM.YYYY)") - datumPravoplatnostiDo: Optional[str] = Field(None, description="Validity date to (DD.MM.YYYY)") - page: Optional[conint(ge=1)] = Field(None, description="Requested page number (starting from 1)") - size: Optional[conint(ge=1)] = Field(None, description="Number of results per page") - sortProperty: Optional[str] = Field(None, description="Field to sort the output by") - sortDirection: Literal["ASC", "DESC"] = Field("ASC", description="Sort direction (ASC or DESC)") +class AdminProceedingsSearch(PaginatedRequest): + """Zoznam správnych konaní. GET /v1/spravneKonanie""" + query: Annotated[Optional[str], Field(default=None, description="Hľadané slovo alebo slovné spojenie")] = None + druhFacetFilter: Annotated[ + Optional[List[str]], + Field(default=None, description="Druh konania (fazetový filter)") + ] = None + datumPravoplatnostiOd: Annotated[ + Optional[str], + Field(default=None, description="Dátum právoplatnosti od (DD.MM.YYYY)") + ] = None + datumPravoplatnostiDo: Annotated[ + Optional[str], + Field(default=None, description="Dátum právoplatnosti do (DD.MM.YYYY)") + ] = None -class AdministrativeProceedingsID(BaseModel): - id: str = Field(..., description="Identifier of administrative proceedings (e.g., spravneKonanie_103)") -class AdministrativeProceedingsAutocomplete(BaseModel): - query: str = Field(None, description="Search term or phrase to help find information") - limit: Optional[conint(ge=1)] = Field(None, description="Limits the number of returned results (pagination)") +class AdminProceedingsByID(BaseModel): + """Jedno správne konanie podľa ID. GET /v1/spravneKonanie/{id}""" + id: Annotated[str, Field(description="Identifikátor, napr. 'spravneKonanie_103'")] + @field_validator("id") + @classmethod + def normalize(cls, v: str) -> str: + v = v.strip() + return f"spravneKonanie_{v}" if v.isdigit() else v + + +class AdminProceedingsAutocomplete(BaseModel): + """Autocomplete pre správne konania. GET /v1/spravneKonanie/autocomplete""" + query: Annotated[Optional[str], Field(default=None, description="Hľadané slovo")] = None + limit: Annotated[Optional[int], Field(default=None, ge=1, description="Maximálny počet výsledkov")] = None \ No newline at end of file diff --git a/api/tools/admin_proceedings.py b/api/tools/admin_proceedings.py index b70ee75..18f5096 100644 --- a/api/tools/admin_proceedings.py +++ b/api/tools/admin_proceedings.py @@ -1,23 +1,19 @@ -from api.fetch_api_data import fetch_api_data, docstring_from_model +from api.fetch_api_data import fetch_api_data -from api.schemas import (AdministrativeProceedings, - AdministrativeProceedingsID, - AdministrativeProceedingsAutocomplete) +from api.schemas import (AdminProceedingsSearch, + AdminProceedingsByID, + AdminProceedingsAutocomplete) from agents import function_tool from api.config import JUSTICE_API_BASE class AdminProceedingsAPI: @function_tool - @docstring_from_model(AdministrativeProceedings) - async def admin_proceedings(self, params: AdministrativeProceedings) -> dict: + async def admin_proceedings(self, params: AdminProceedingsSearch) -> dict: """ Fetch a list of administrative proceedings from the Justice API with optional filtering. - Args: - params (AdministrativeProceedings): Filtering and pagination parameters. - {params} - + params (AdminProceedingsSearch): Filtering and pagination parameters. Returns: dict: A dictionary containing a list of administrative proceedings and related metadata. """ @@ -26,15 +22,11 @@ class AdminProceedingsAPI: return await fetch_api_data(icon="✒️", url=url, params=params.model_dump(exclude_none=True)) @function_tool - @docstring_from_model(AdministrativeProceedingsID) - async def admin_proceedings_id(self, params: AdministrativeProceedingsID) -> dict: + async def admin_proceedings_id(self, params: AdminProceedingsByID) -> dict: """ Fetch detailed information about a specific administrative proceeding by its identifier. - Args: - params (AdministrativeProceedingsID): Unique identifier of the administrative proceeding. - {params} - + params (AdminProceedingsByID): Unique identifier of the administrative proceeding. Returns: dict: Details of the specified administrative proceeding. """ @@ -43,15 +35,11 @@ class AdminProceedingsAPI: return await fetch_api_data(icon="✒️", url=url, params={}) @function_tool - @docstring_from_model(AdministrativeProceedingsAutocomplete) - async def admin_proceedings_autocomplete(self, params: AdministrativeProceedingsAutocomplete) -> dict: + async def admin_proceedings_autocomplete(self, params: AdminProceedingsAutocomplete) -> dict: """ Fetch autocomplete suggestions for administrative proceeding search terms. - Args: - params (AdministrativeProceedingsAutocomplete): Parameters for autocomplete query (e.g., partial text). - {params} - + params (AdminProceedingsAutocomplete): Parameters for autocomplete query (e.g., partial text). Returns: dict: Suggested values matching the input query. """ diff --git a/api/tools/civil_proceedings.py b/api/tools/civil_proceedings.py index 68c37b2..4c4d805 100644 --- a/api/tools/civil_proceedings.py +++ b/api/tools/civil_proceedings.py @@ -1,6 +1,6 @@ -from api.fetch_api_data import fetch_api_data, docstring_from_model -from api.schemas import (CivilProceedings, - CivilProceedingsID, +from api.fetch_api_data import fetch_api_data +from api.schemas import (CivilProceedingsSearch, + CivilProceedingsByID, CivilProceedingsAutocomplete) from agents import function_tool from api.config import JUSTICE_API_BASE @@ -9,15 +9,13 @@ from api.config import JUSTICE_API_BASE class CivilProceedingsAPI: @function_tool - @docstring_from_model(CivilProceedings) - async def civil_proceedings(self, params: CivilProceedings) -> dict: + async def civil_proceedings(self, params: CivilProceedingsSearch) -> dict: """ Fetch a list of civil proceedings and publicly announced judgments from the Justice API with optional filtering. Args: - params (CivilProceedings): Filtering and pagination parameters. - {params} + params (CivilProceedingsSearch): Filtering and pagination parameters. Returns: dict: A dictionary containing a list of civil proceedings and related metadata. @@ -27,15 +25,13 @@ class CivilProceedingsAPI: return await fetch_api_data(icon="🖊️", url=url, params=params.model_dump(exclude_none=True)) @function_tool - @docstring_from_model(CivilProceedingsID) - async def civil_proceedings_id(self, params: CivilProceedingsID) -> dict: + async def civil_proceedings_id(self, params: CivilProceedingsByID) -> dict: """ Fetch detailed information about a specific civil proceeding and publicly announced judgment by its identifier. Args: - params (CivilProceedingsID): Unique identifier of the civil proceeding. - {params} + params (CivilProceedingsByID): Unique identifier of the civil proceeding. Returns: dict: Details of the specified civil proceeding and judgment. @@ -45,14 +41,12 @@ class CivilProceedingsAPI: return await fetch_api_data(icon="🖊️", url=url, params={}) @function_tool - @docstring_from_model(CivilProceedingsAutocomplete) async def civil_proceedings_autocomplete(self, params: CivilProceedingsAutocomplete) -> dict: """ Fetch autocomplete suggestions for civil proceeding search terms. Args: params (CivilProceedingsAutocomplete): Parameters for autocomplete query (e.g., partial text). - {params} Returns: dict: Suggested values matching the input query. diff --git a/api/tools/contracts.py b/api/tools/contracts.py index fa56efd..107226b 100644 --- a/api/tools/contracts.py +++ b/api/tools/contracts.py @@ -1,19 +1,17 @@ -from api.fetch_api_data import fetch_api_data, docstring_from_model -from api.schemas import Contracts, ContractID, ContractAutocomplete +from api.fetch_api_data import fetch_api_data +from api.schemas import ContractSearch, ContractByID, ContractAutocomplete from agents import function_tool from api.config import JUSTICE_API_BASE class ContractsAPI: @function_tool - @docstring_from_model(Contracts) - async def contract(self, params: Contracts) -> dict: + async def contract(self, params: ContractSearch) -> dict: """ Fetch a list of contracts from the Justice API with optional filtering. Args: - params (Contracts): Filtering and pagination parameters. - {params} + params (ContractSearch): Filtering and pagination parameters. Returns: dict: A dictionary containing a list of contracts and related metadata. @@ -24,14 +22,12 @@ class ContractsAPI: @function_tool - @docstring_from_model(ContractID) - async def contract_id(self, params: ContractID) -> dict: + async def contract_id(self, params: ContractByID) -> dict: """ Fetch detailed information about a specific contract by its identifier. Args: - params (ContractID): Unique identifier of the contract. - {params} + params (ContractByID): Unique identifier of the contract. Returns: dict: Details of the specified contract. @@ -41,14 +37,12 @@ class ContractsAPI: return await fetch_api_data(icon="📃", url=url, params={}) @function_tool - @docstring_from_model(ContractAutocomplete) async def contract_autocomplete(self, params: ContractAutocomplete) -> dict: """ Fetch autocomplete suggestions for contract-related search terms. Args: params (ContractAutocomplete): Parameters for autocomplete query (e.g., partial text). - {params} Returns: dict: Suggested values matching the input query. diff --git a/api/tools/courts.py b/api/tools/courts.py index 06c943e..ac64668 100644 --- a/api/tools/courts.py +++ b/api/tools/courts.py @@ -1,20 +1,16 @@ -from api.fetch_api_data import fetch_api_data, docstring_from_model -from api.schemas import Court, CourtID, CourtAutocomplete +from api.fetch_api_data import fetch_api_data +from api.schemas import CourtSearch, CourtByID, CourtAutocomplete from agents import function_tool from api.config import JUSTICE_API_BASE class CourtsAPI: @function_tool - @docstring_from_model(Court) - async def court(self, params: Court) -> dict: + async def court(self, params: CourtSearch) -> dict: """ Fetch a list of courts from the Justice API with optional filtering. - Args: - params (Court): Filtering and pagination parameters. - {params} - + params (CourtSearch): Filtering and pagination parameters. Returns: dict: A dictionary containing a list of courts and related metadata. """ @@ -23,15 +19,11 @@ class CourtsAPI: return await fetch_api_data(icon="🏛️️", url=url, params=params.model_dump(exclude_none=True)) @function_tool - @docstring_from_model(CourtID) - async def court_id(self, params: CourtID) -> dict: + async def court_id(self, params: CourtByID) -> dict: """ Fetch detailed information about a specific court by its identifier. - Args: - params (CourtID): Unique identifier of the court. - {params} - + params (CourtByID): Unique identifier of the court. Returns: dict: Details of the specified court. """ @@ -40,15 +32,11 @@ class CourtsAPI: return await fetch_api_data(icon="🏛️️", url=url, params={}, remove_keys=['foto']) @function_tool - @docstring_from_model(CourtAutocomplete) async def court_autocomplete(self, params: CourtAutocomplete) -> dict: """ Fetch autocomplete suggestions for court names. - Args: params (CourtAutocomplete): Parameters for autocomplete. - {params} - Returns: dict: Suggested court names matching the input query. """ diff --git a/api/tools/decisions.py b/api/tools/decisions.py index 255e57d..63c2b69 100644 --- a/api/tools/decisions.py +++ b/api/tools/decisions.py @@ -1,20 +1,16 @@ -from api.fetch_api_data import fetch_api_data, docstring_from_model -from api.schemas import Decision, DecisionID, DecisionAutocomplete +from api.fetch_api_data import fetch_api_data +from api.schemas import DecisionSearch, DecisionByID, DecisionAutocomplete from agents import function_tool from api.config import JUSTICE_API_BASE class DecisionsAPI: @function_tool - @docstring_from_model(Decision) - async def decision(self, params: Decision) -> dict: + async def decision(self, params: DecisionSearch) -> dict: """ Fetch a list of decisions from the Justice API with optional filtering. - Args: - params (Decision): Filtering and pagination parameters. - {params} - + params (DecisionSearch): Filtering and pagination parameters. Returns: dict: A dictionary containing a list of decisions and related metadata. """ @@ -24,15 +20,11 @@ class DecisionsAPI: @function_tool - @docstring_from_model(DecisionID) - async def decision_id(self, params: DecisionID) -> dict: + async def decision_id(self, params: DecisionByID) -> dict: """ Fetch detailed information about a specific decision by its identifier. - Args: - params (DecisionID): Unique identifier of the decision. - {params} - + params (DecisionByID): Unique identifier of the decision. Returns: dict: Details of the specified decision. """ @@ -41,15 +33,11 @@ class DecisionsAPI: return await fetch_api_data(icon="⚖️️", url=url, params={}) @function_tool - @docstring_from_model(DecisionAutocomplete) async def decision_autocomplete(self, params: DecisionAutocomplete) -> dict: """ Fetch autocomplete suggestions for decision-related search terms. - Args: params (DecisionAutocomplete): Parameters for autocomplete. - {params} - Returns: dict: Suggested values matching the input query. """ diff --git a/api/tools/judges.py b/api/tools/judges.py index a83968c..534beaa 100644 --- a/api/tools/judges.py +++ b/api/tools/judges.py @@ -1,5 +1,5 @@ -from api.fetch_api_data import fetch_api_data, docstring_from_model -from api.schemas import Judge, JudgeID, JudgeAutocomplete +from api.fetch_api_data import fetch_api_data +from api.schemas import JudgeSearch, JudgeByID, JudgeAutocomplete from agents import function_tool from api.config import JUSTICE_API_BASE @@ -7,15 +7,11 @@ from api.config import JUSTICE_API_BASE class JudgesAPI: @function_tool - @docstring_from_model(Judge) - async def judge(self, params: Judge) -> dict: + async def judge(self, params: JudgeSearch) -> dict: """ Fetch a list of judges from the Justice API with optional filtering. - Args: - params (Judge): Filtering and pagination parameters. - {params} - + params (JudgeSearch): Filtering and pagination parameters. Returns: dict: A dictionary containing a list of judges and related metadata. """ @@ -25,15 +21,11 @@ class JudgesAPI: @function_tool - @docstring_from_model(JudgeID) - async def judge_id(self, params: JudgeID) -> dict: + async def judge_id(self, params: JudgeByID) -> dict: """ Fetch detailed information about a specific judge by their identifier. - Args: - params (JudgeID): Unique identifier of the judge. - {params} - + params (JudgeByID): Unique identifier of the judge. Returns: dict: Details of the specified judge. """ @@ -43,15 +35,11 @@ class JudgesAPI: @function_tool - @docstring_from_model(JudgeAutocomplete) async def judge_autocomplete(self, params: JudgeAutocomplete) -> dict: """ Fetch autocomplete suggestions for judges' names. - Args: params (JudgeAutocomplete): Parameters for autocomplete query (e.g., partial name). - {params} - Returns: dict: Suggested judge names matching the input query. """ diff --git a/core/system_prompt.py b/core/system_prompt.py index f4e7ae6..a52a6a8 100644 --- a/core/system_prompt.py +++ b/core/system_prompt.py @@ -1,106 +1,163 @@ def get_system_prompt(model_name: str) -> str: - system_prompt = f""" - # Legal AI Assistant – Slovak Ministry of Justice API - - ## Role - You are a **Legal AI Assistant** powered by {model_name}, integrated with the **official public APIs of the Ministry of Justice of the Slovak Republic**. - - Your primary responsibility is to: - - Extract **structured parameters** from **natural-language user queries** - - Validate those parameters - - Retrieve data **exclusively** via registered API tools - - Present results in a **clear, human-friendly Slovak language format** - - You act strictly as an **API data interpreter**, not as a legal advisor. - - --- - - ## Operational Constraints - - ✅ You **can** briefly explain your AI model, its creator, and how it differs from others - - ✅ You **can** list what you are not allowed to disclose if the user wants to know. - - ✅ Use **only data returned by official Ministry of Justice APIs** - - ❌ Do **not** use external legal knowledge - - ❌ Do **not** infer, speculate, or fill gaps beyond API responses - - ❌ Do **not** mention APIs, tools, schemas, function names, or internal logic in final answers - - --- - - ## Supported Legal Domains - You may process queries related to: - - Judges - - Courts - - Judicial Decisions - - Contracts - - Civil Proceedings - - Administrative Proceedings - - Each domain provides: - - General search - - Search by ID - - Autocomplete / suggestion search - - **Rule:** Always use the most specific tool available. - - --- - - ## Mandatory Processing Workflow - 1. **Intent Detection** - Identify the legal domain and user intent. - - 2. **Parameter Extraction** - Extract names, IDs, keywords, court levels, regions, dates, statuses, and filters. - - 3. **Input Normalization** - Automatically normalize common user errors when possible: - - `Okresný súd v Košice` → `Okresný súd v Košiciach` - - `12 decembra 2024` → `12.12.2024` - - `175` → `sud_175` - - 4. **Validation** - Validate parameters against expected schemas. - - 5. **Tool Invocation** - Call the appropriate registered API tool. - - 6. **Result Handling** - - ✅ Success → summarize results clearly - - ⚠️ Empty result → explain that no data was found - - ❌ Error → explain the issue politely and clearly - - 7. **Response Generation** - Produce a **final response in Slovak**, understandable to non-experts. - - --- - - ## Response Requirements - Final responses must: - - Be written **only in Slovak** - - Be friendly, clear, and concise - - Use emojis **sparingly** for readability - - Present multiple results as lists or structured sections - - Never expose internal system details - - --- - - ## Error Recovery Guidance - If no data is found: - - Calmly explain the reason - - Suggest corrected spellings, formats, or rephrasing when applicable - - --- - - ## Example Behavior - - **User:** - “Find judge Novák in Bratislava” - - **Expected Handling:** - - Domain: Judges - - Use autocomplete or filtered search - - Return a list of matching judges with court, status, and region - - Output in Slovak with clear formatting - """ - - return system_prompt \ No newline at end of file + return f""" + # Legal AI Assistant – Slovak Ministry of Justice API + **Powered by:** {model_name} + + ## Role + You are a Legal AI Assistant integrated with the official public APIs of the Ministry of Justice of the Slovak Republic. + You extract structured parameters from natural-language queries, call the correct API tools, and present results clearly in Slovak. + You are strictly an API data interpreter — not a legal advisor. + + --- + + ## Operational Constraints + - ✅ Use ONLY data returned by official Ministry of Justice APIs + - ✅ You may briefly explain your AI model and how it differs from others + - ✅ You may list what you are not allowed to disclose + - ❌ Do NOT use external legal knowledge or training data to answer legal questions + - ❌ Do NOT infer, speculate, or fill gaps beyond API responses + - ❌ Do NOT mention APIs, tools, schemas, function names, or internal logic in final answers + - ❌ Do NOT expose pagination details, raw JSON, or technical errors to the user + + --- + + ## Supported Legal Domains + Judges (Sudcovia) : search, search by ID, autocomplete + Courts (Súdy) : search, search by ID, autocomplete + Decisions (Rozhodnutia) : search, search by ID, autocomplete + Contracts (Zmluvy) : search, search by ID, autocomplete + Civil Proceedings (Občianske konania) : search, search by ID, autocomplete + Administrative Proceedings (Správne konania) : search, search by ID, autocomplete + + **Rule:** Always use the most specific tool available. Prefer autocomplete for name-based lookups. + + --- + + ## Mandatory Processing Workflow + + ### Step 1 — Intent Detection + Identify the legal domain and intent: + - Searching by name → use autocomplete first + - Searching by known ID → use ID-specific tool + - Broad search with filters → use general search + + ### Step 2 — Input Normalization + Automatically fix common user errors BEFORE calling any tool: + - Slovak diacritics: `Novak` → try both `Novak` AND `Novák`; `Kos` → `Košice` + - Court names: `Okresný súd v Košice` → `Okresný súd Košice I` + - Dates: `12 decembra 2024` → `12.12.2024`; `december 2024` → `01.12.2024` to `31.12.2024` + - IDs: `175` → `sud_175`; `sudca 42` → `sudca_42` + - Region names: always use full form with "kraj": `Bratislava` → `Bratislavský kraj` + + ### Step 3 — Name Search Strategy (CRITICAL) + When searching by person name, ALWAYS follow this order: + 1. **First: call `judge_autocomplete`** (or equivalent autocomplete for other domains) + - Use the name as `query`, set `limit=10` + - This returns exact name matches regardless of alphabetical pagination + 2. **If autocomplete returns results:** use the returned IDs to call `judge_id` for full details + 3. **If autocomplete returns nothing:** fall back to general search with `query=name` and `size=50` + 4. **Never** rely on page 0 of general search results when looking for a specific name — results are alphabetical and the person may be on page 10+ + + ### Step 4 — Diacritics Handling (CRITICAL) + Slovak names with special characters (á, é, í, ó, ú, ý, ä, č, ď, ě, ľ, ĺ, ň, ô, ŕ, š, ť, ž) must be handled carefully: + - Always attempt the search with the diacritics-correct form first: `Novák`, not `Novak` + - If no results, retry without diacritics: `Novak` + - If still no results, try common variants: `Nováková`, `Novakova` + - Inform the user which variant was used + + ### Step 5 — Pagination Handling + - Default API page size is 20. Total results may be 2365+ for broad queries. + - When total results > available results shown, always inform the user there are more + - Suggest filtering by region (kraj), court type, or status to narrow results + - Never silently show only page 1 without mentioning it's a subset + + ### Step 6 — Parameter Validation + Before calling any tool, validate: + - Date formats match the expected format for that endpoint (DD.MM.YYYY or YYYY-MM-DD — check per tool) + - IDs follow the correct prefix pattern (sud_, sudca_, spravneKonanie_, etc.) + - Facet filter values are from known valid options (e.g. region names are exact API values) + - `page` is ≥ 1 (never 0) + + ### Step 7 — Tool Invocation + Call the appropriate tool with validated, normalized parameters. + If a parameter is uncertain (e.g., user gave an ambiguous court name), either: + - Ask the user to confirm before calling, OR + - Use autocomplete to find the correct value first + + ### Step 8 — Result Handling + ✅ Results found : Summarize clearly, show structured list + ⚠️ Empty results : Explain calmly, suggest alternatives + 📄 Partial results (paginated) : Show what was found, mention total count, suggest filtering + ❌ API error : Inform user politely, suggest retry + + ### Step 9 — Response Generation + Always respond in **Slovak**. Format rules: + - Use numbered lists for multiple results + - Show: name, role/function, court, region, status (active/inactive) + - Keep responses concise — do not dump raw data + - Use emojis sparingly (✅ ⚠️ 🔍 only) + - Never show IDs, URLs, parameter names, or technical details + + --- + + ## Error Recovery Playbook + Name not found on page 0 -> Use autocomplete, do NOT report "not found" + Diacritics mismatch -> Try both forms, report which was used + Too many results (>100) -> Ask user to specify region, court, or time period + Unknown court name -> Use court autocomplete to find correct name + Date format unclear -> Ask user to confirm or infer from context + ID format wrong -> Normalize automatically (add prefix) + + --- + + ## Response Format Examples + + **Single judge found:** + ``` + Našiel som sudcu Novák: + • Meno: JUDr. Ján Novák + • Funkcia: Sudca + • Súd: Okresný súd Bratislava I + • Kraj: Bratislavský kraj + • Stav: aktívny + ``` + + **Multiple results:** + ``` + Našiel som 5 sudcov s menom Novák (zobrazujem všetkých 5): + 1. JUDr. Ján Novák — Okresný súd Bratislava I (aktívny) + 2. JUDr. Peter Novák — Krajský súd Košice (aktívny) + ... + ``` + + **Paginated results:** + ``` + Celkovo: 2 365 výsledkov. Zobrazujem prvých 10. + Pre presnejšie výsledky uveďte kraj alebo typ súdu. + + 1. JUDr. Alena Adamcová — Najvyšší súd SR (aktívny) + ... + ``` + + **Not found:** + ``` + Nenašiel som sudcu s menom "Novak" v Bratislavskom kraji. + + Možné dôvody: + • Meno môže mať diakritiku: skúste "Novák" + • Sudca môže pôsobiť v inom kraji + • Skúste rozšíriť vyhľadávanie na celú SR + + Chcete, aby som hľadal "Novák" namiesto "Novak"? + ``` + + --- + + ## What You Are NOT + - ❌ Not a lawyer — do not give legal advice + - ❌ Not a historian — do not explain legal history beyond API data + - ❌ Not a search engine — only search within Ministry of Justice API + - ❌ Not multilingual by default — always respond in Slovak unless explicitly asked otherwise + """ \ No newline at end of file