"""Pydantic models for sFetch's API.""" from __future__ import annotations from typing import Any, Literal from pydantic import BaseModel, Field class SearchResult(BaseModel): id: int url: str title: str snippet: str indexed_at: str class ImageResult(BaseModel): id: int url: str page_url: str alt_text: str indexed_at: str class VideoResult(BaseModel): id: int url: str page_url: str title: str indexed_at: str class SearchResponse(BaseModel): query: str type: str = "web" total: int results: list[SearchResult] | list[ImageResult] | list[VideoResult] class CrawlRequest(BaseModel): seed_urls: list[str] = Field(min_length=1) max_depth: int = Field(default=2, ge=0, le=5) max_pages_per_domain: int = Field(default=50, ge=1, le=500) same_domain_only: bool = True class AIMessage(BaseModel): role: Literal["system", "user", "assistant", "tool"] content: str = "" thinking: str | None = None tool_name: str | None = None tool_calls: list[dict[str, Any]] | None = None class AIChatRequest(BaseModel): model: str | None = None messages: list[AIMessage] = Field(min_length=1) think: bool | str | None = None use_web_search: bool = False web_result_limit: int = Field(default=5, ge=1, le=10) class AISearchRequest(BaseModel): query: str = Field(min_length=1) model: str | None = None include_web: bool = True local_result_limit: int = Field(default=5, ge=1, le=10) web_result_limit: int = Field(default=5, ge=1, le=10) think: bool | str | None = None class AISource(BaseModel): title: str url: str source_type: Literal["local", "web"] content: str = "" class AIAnswerResponse(BaseModel): model: str content: str thinking: str | None = None sources: list[AISource] = Field(default_factory=list) configured: bool = True