File size: 5,185 Bytes
8871df9
 
 
 
 
 
 
cc2ed2f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8871df9
 
 
 
cc2ed2f
 
 
 
8871df9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cc2ed2f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8871df9
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
"""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