153 lines
4.6 KiB
Python
153 lines
4.6 KiB
Python
"""Pydantic request/response models for the FastAPI layer."""
|
|
from __future__ import annotations
|
|
|
|
from typing import List, Optional
|
|
|
|
from pydantic import BaseModel, Field
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Shared
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class ProductOut(BaseModel):
|
|
image_id: str
|
|
image_url: Optional[str] = None
|
|
image_urls: List[str] = Field(default_factory=list)
|
|
brand: str
|
|
product_name: str
|
|
title: Optional[str] = None
|
|
category: Optional[str] = None
|
|
description: Optional[str] = None
|
|
price_range: Optional[str] = None
|
|
size_variants: List[str] = Field(default_factory=list)
|
|
providers: List[str] = Field(default_factory=list)
|
|
highlights: List[str] = Field(default_factory=list)
|
|
nutrients: List[str] = Field(default_factory=list)
|
|
fssai_license: Optional[str] = None
|
|
product_sku: Optional[str] = None
|
|
sku_source: Optional[str] = None
|
|
hsn_code: Optional[str] = None
|
|
final_selling_price: Optional[float] = None
|
|
selling_price: Optional[float] = None
|
|
barcode: Optional[str] = None
|
|
barcode_type: Optional[str] = None
|
|
|
|
|
|
class SourceProductOut(BaseModel):
|
|
image_id: str
|
|
image_url: Optional[str] = None
|
|
image_urls: List[str] = Field(default_factory=list)
|
|
brand: str
|
|
product_name: str
|
|
title: Optional[str] = None
|
|
category: Optional[str] = None
|
|
description: Optional[str] = None
|
|
price_range: Optional[str] = None
|
|
size_variants: List[str] = Field(default_factory=list)
|
|
providers: List[str] = Field(default_factory=list)
|
|
highlights: List[str] = Field(default_factory=list)
|
|
nutrients: List[str] = Field(default_factory=list)
|
|
fssai_license: Optional[str] = None
|
|
product_sku: Optional[str] = None
|
|
sku_source: Optional[str] = None
|
|
hsn_code: Optional[str] = None
|
|
final_selling_price: Optional[float] = None
|
|
selling_price: Optional[float] = None
|
|
barcode: Optional[str] = None
|
|
barcode_type: Optional[str] = None
|
|
similarity: float
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Health
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class HealthOut(BaseModel):
|
|
status: str
|
|
database: bool
|
|
ollama: bool
|
|
ollama_model: str
|
|
embeddings_model: str
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Brands / catalog browsing
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class BrandsOut(BaseModel):
|
|
brands: List[str]
|
|
|
|
|
|
class CategoriesOut(BaseModel):
|
|
brand: str
|
|
categories: List[str]
|
|
|
|
|
|
class ProductListOut(BaseModel):
|
|
brand: str
|
|
total: int
|
|
limit: int
|
|
offset: int
|
|
products: List[ProductOut]
|
|
|
|
|
|
class AllProductsOut(BaseModel):
|
|
total: int
|
|
limit: int
|
|
offset: int
|
|
products: List[ProductOut]
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Semantic search (retrieval only, no LLM generation)
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class SearchOut(BaseModel):
|
|
query: str
|
|
brand: Optional[str] = None
|
|
results: List[SourceProductOut]
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# RAG chat
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class ChatTurn(BaseModel):
|
|
role: str = Field(..., description="'user' or 'assistant'")
|
|
content: str
|
|
|
|
|
|
class ChatRequest(BaseModel):
|
|
query: str = Field(..., min_length=1, max_length=2000)
|
|
brand: Optional[str] = Field(None, description="Restrict retrieval to a single brand")
|
|
category: Optional[str] = Field(None, description="Restrict retrieval to a category")
|
|
top_k: Optional[int] = Field(None, ge=1, le=15)
|
|
history: Optional[List[ChatTurn]] = Field(default=None, description="Prior turns for follow-up questions")
|
|
|
|
|
|
class ChatResponseOut(BaseModel):
|
|
answer: str
|
|
query: str
|
|
brand: Optional[str] = None
|
|
detected_category: Optional[str] = Field(
|
|
None, description="Product category auto-detected from the query and used to scope retrieval, if any"
|
|
)
|
|
sources: List[SourceProductOut]
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Catalog generation (admin/ingestion trigger)
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class CatalogGenerateRequest(BaseModel):
|
|
brand: str = Field(..., min_length=1, max_length=120)
|
|
max_products: int = Field(50, ge=1, le=300)
|
|
|
|
|
|
class CatalogJobOut(BaseModel):
|
|
job_id: str
|
|
brand: str
|
|
status: str
|
|
detail: Optional[str] = None
|