Mirrors /opt/ai-apps/eh-search/ on the server, including the full FastAPI app (intent routing, FTS+fuzzy+substring hybrid, multi-source federation across vehicles + blog + brands + pages + static + tag bridge), SQL schema (Postgres materialized view with german_unaccent text search, pg_trgm for fuzzy), Dockerfile and compose. Sanitized the hardcoded password in sql/01_init.sql — replaced with REPLACE_ME_BEFORE_APPLYING placeholder since this repo is public. The eh-search service binds only on the private network (10.0.0.8:8200) and is reachable only via Pegasus nginx proxy at /api/search. Refs OP#1094 OP#1105 OP#1112 OP#1116 OP#1117
82 lines
1.7 KiB
Python
82 lines
1.7 KiB
Python
"""Pydantic response models."""
|
|
from pydantic import BaseModel, Field
|
|
from typing import Literal, Any
|
|
|
|
|
|
IntentType = Literal[
|
|
"komm_nr",
|
|
"dvn",
|
|
"vin",
|
|
"autocomplete_only",
|
|
"keyword_search",
|
|
"empty",
|
|
]
|
|
|
|
ResultType = Literal["vehicle", "page", "blog", "brand"]
|
|
|
|
|
|
class SearchResultItem(BaseModel):
|
|
"""Unified result item — fields are optional depending on type."""
|
|
type: ResultType = "vehicle"
|
|
title: str
|
|
slug: str | None = None
|
|
snippet: str | None = None
|
|
score: float = 0.0
|
|
matched_via: str = ""
|
|
|
|
# Vehicle-specific
|
|
vehicle_id: int | None = None
|
|
commission_number: str | None = None
|
|
vin: str | None = None
|
|
brand: str | None = None
|
|
model: str | None = None
|
|
price: float | None = None
|
|
primary_image_id: str | None = None
|
|
directus_product_id: int | None = None
|
|
|
|
# Blog-specific
|
|
tags: list[str] | None = None
|
|
category: str | None = None
|
|
published_at: str | None = None
|
|
image_id: str | None = None
|
|
|
|
# Brand-specific
|
|
logo_id: str | None = None
|
|
|
|
model_config = {"extra": "allow"}
|
|
|
|
|
|
class SearchResponse(BaseModel):
|
|
query: str
|
|
intent: IntentType
|
|
direct_redirect: bool = False
|
|
total: int
|
|
results: list[SearchResultItem]
|
|
took_ms: int
|
|
cache_hit: bool = False
|
|
|
|
|
|
class SuggestItem(BaseModel):
|
|
text: str
|
|
type: Literal["brand", "model", "category"]
|
|
count: int = 0
|
|
|
|
|
|
class SuggestResponse(BaseModel):
|
|
query: str
|
|
suggestions: list[SuggestItem]
|
|
took_ms: int
|
|
|
|
|
|
class HealthResponse(BaseModel):
|
|
status: str
|
|
environment: str
|
|
db: str
|
|
redis: str
|
|
vehicles_count: int
|
|
content: dict[str, Any] = Field(default_factory=dict)
|
|
|
|
|
|
class InvalidateResponse(BaseModel):
|
|
cleared: int
|
|
scope: str = "all"
|