Ru2SQL / src /api /schemas.py
Tyycha's picture
fix bugs
cc2ed2f
"""Pydantic-модСли для FastAPI endpoints."""
from __future__ import annotations
from pydantic import BaseModel, Field
class VocabularyPayload(BaseModel):
"""БизнСс-ΡΠ»ΠΎΠ²Π°Ρ€ΡŒ Π² Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅, согласованном с BusinessVocabulary.from_dict.
ΠŸΠ΅Ρ€Π΅Π΄Π°Ρ‘Ρ‚ΡΡ ΠΊΠ»ΠΈΠ΅Π½Ρ‚ΠΎΠΌ ΠΎΠΏΡ†ΠΈΠΎΠ½Π°Π»ΡŒΠ½ΠΎ Π² запросС Π½Π° Π³Π΅Π½Π΅Ρ€Π°Ρ†ΠΈΡŽ SQL. Если ΠΏΠΎΠ»Π΅
отсутствуСт β€” модСль Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚ Π±Π΅Π· ΡΠΏΠ΅Ρ†ΠΈΠ°Π»ΡŒΠ½Ρ‹Ρ… бизнСс-ΠΎΠΏΡ€Π΅Π΄Π΅Π»Π΅Π½ΠΈΠΉ.
"""
company: str = ""
terms: dict[str, str] = Field(default_factory=dict)
filters: dict[str, str] = Field(default_factory=dict)
notes: list[str] = Field(default_factory=list)
# ──────────────────────────────────────────────────────────────────────
# /generate-sql β€” старый эндпоинт для PAUQ-структуры (databases_dir + db_id)
# ──────────────────────────────────────────────────────────────────────
class GenerateRequest(BaseModel):
question: str = Field(..., min_length=1, max_length=2000, description="Вопрос Π½Π° русском")
db_id: str = Field(..., min_length=1, description="Π˜Π΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ Π‘Π” ΠΈΠ· PAUQ")
execute: bool = Field(default=False, description="ΠŸΡ€ΠΎΠ³Π½Π°Ρ‚ΡŒ сгСнСрированный SQL Π½Π° Π‘Π”")
vocabulary: VocabularyPayload | None = Field(
default=None,
description="ΠžΠΏΡ†ΠΈΠΎΠ½Π°Π»ΡŒΠ½Ρ‹ΠΉ бизнСс-ΡΠ»ΠΎΠ²Π°Ρ€ΡŒ ΠΊΠΎΠΌΠΏΠ°Π½ΠΈΠΈ (см. Ρ€Π°Π·Π΄Π΅Π» 3.6 Π’ΠšΠ )",
)
class ExecutionResult(BaseModel):
columns: list[str]
rows: list[list]
row_count: int
class GenerateResponse(BaseModel):
sql: str
raw_output: str
is_valid_sql: bool
execution: ExecutionResult | None = None
error: str | None = None
# ──────────────────────────────────────────────────────────────────────
# /query β€” Π½ΠΎΠ²Ρ‹ΠΉ эндпоинт для ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ»ΡŒΠ½ΠΎΠΉ Π‘Π” (connection string)
# ──────────────────────────────────────────────────────────────────────
class QueryRequest(BaseModel):
"""ΠŸΠΎΠ»Π½Ρ‹ΠΉ запрос «вопрос Π½Π° русском β†’ SQL β†’ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Β» для ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ»ΡŒΠ½ΠΎΠΉ Π‘Π”.
Π’ ΠΎΡ‚Π»ΠΈΡ‡ΠΈΠ΅ ΠΎΡ‚ GenerateRequest, Π½Π΅ привязан ΠΊ PAUQ-структурС: ΠΊΠ»ΠΈΠ΅Π½Ρ‚ сам
ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‘Ρ‚ connection string (SQLite/PostgreSQL/MySQL). Π˜ΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ
Streamlit-интСрфСйсом ΠΈ Π»ΡŽΠ±Ρ‹ΠΌΠΈ сторонними ΠΊΠ»ΠΈΠ΅Π½Ρ‚Π°ΠΌΠΈ.
"""
question: str = Field(..., min_length=1, max_length=2000)
connection_string: str = Field(
..., min_length=1,
description="Π‘Ρ‚Ρ€ΠΎΠΊΠ° ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΡ. ΠŸΡ€ΠΈΠΌΠ΅Ρ€: sqlite:///data/demo/sales.sqlite",
)
execute: bool = Field(default=True)
vocabulary: VocabularyPayload | None = None
class QueryResponse(BaseModel):
sql: str
raw_output: str
is_valid_sql: bool
gen_time_seconds: float
execution: ExecutionResult | None = None
error: str | None = None
# ──────────────────────────────────────────────────────────────────────
# /schema β€” ΠΎΡ‚Π΄Π°Ρ‚ΡŒ схСму ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ»ΡŒΠ½ΠΎΠΉ Π‘Π” для подстановки Π² UI
# ──────────────────────────────────────────────────────────────────────
class SchemaRequest(BaseModel):
connection_string: str = Field(..., min_length=1)
include_samples: bool = Field(default=True)
class ColumnPayload(BaseModel):
name: str
type: str
nullable: bool
primary_key: bool
class TablePayload(BaseModel):
name: str
columns: list[ColumnPayload]
sample_rows: list[list]
ddl: str
class SchemaResponse(BaseModel):
tables: list[TablePayload]
# ──────────────────────────────────────────────────────────────────────
# ΠŸΡ€ΠΎΡ‡Π΅Π΅
# ──────────────────────────────────────────────────────────────────────
class DatabaseInfo(BaseModel):
db_id: str
tables: list[str]
class HealthResponse(BaseModel):
status: str
model_loaded: bool
base_model: str