Deadman922 commited on
Commit
3473fba
Β·
verified Β·
1 Parent(s): 450f21e

Upload 2 files

Browse files
Files changed (2) hide show
  1. server/main.py +15 -2
  2. server/models.py +192 -0
server/main.py CHANGED
@@ -65,12 +65,25 @@ async def root():
65
 
66
  @app.get("/health", response_model=HealthResponse)
67
  async def health() -> HealthResponse:
68
- """Health check endpoint for HF Space ping validation.
69
 
70
  Returns:
71
  dict: Status response indicating the server is running
72
  """
73
- return HealthResponse(status="ok")
 
 
 
 
 
 
 
 
 
 
 
 
 
74
 
75
 
76
  @app.post("/ping", response_model=HealthResponse)
 
65
 
66
  @app.get("/health", response_model=HealthResponse)
67
  async def health() -> HealthResponse:
68
+ """Health check endpoint for monitoring and load balancers.
69
 
70
  Returns:
71
  dict: Status response indicating the server is running
72
  """
73
+ # Check LLM availability
74
+ llm_available = False
75
+ try:
76
+ import os
77
+ hf_token = os.getenv("HF_TOKEN", "")
78
+ api_base_url = os.getenv("API_BASE_URL", "")
79
+ model_name = os.getenv("MODEL_NAME", "")
80
+
81
+ if hf_token and api_base_url and model_name:
82
+ llm_available = True
83
+ except Exception:
84
+ pass
85
+
86
+ return HealthResponse(status="ok", llm_available=llm_available)
87
 
88
 
89
  @app.post("/ping", response_model=HealthResponse)
server/models.py ADDED
@@ -0,0 +1,192 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from enum import Enum
4
+ from typing import Optional
5
+
6
+ from pydantic import BaseModel, Field, field_validator
7
+
8
+
9
+ class ActionType(str, Enum):
10
+ """
11
+ The four action types available to an agent in sql_quality_env.
12
+
13
+ QUERY β€” Read-only SELECT statement. Inspect schema or data.
14
+ Does not modify the database. Can be called multiple times.
15
+ PROFILE β€” Run null-rate and type distribution stats on a named column.
16
+ Shortcut for common inspection patterns. Read-only.
17
+ FIX β€” Submit a write query intended to resolve the data issue.
18
+ Executes on an isolated _fresh_test_db() copy for scoring.
19
+ SUBMIT β€” Declare final diagnosis and close the episode.
20
+ Triggers composite score computation. Episode ends after this.
21
+ """
22
+
23
+ QUERY = "query"
24
+ PROFILE = "profile"
25
+ FIX = "fix"
26
+ SUBMIT = "submit"
27
+
28
+
29
+ class SchemaInfo(BaseModel):
30
+ """Schema information for a database table."""
31
+
32
+ table_name: str
33
+ columns: list[str] # Format: "column_name (TYPE)"
34
+ row_count: int
35
+
36
+
37
+ class SQLObservation(BaseModel):
38
+ """What the agent sees at each step."""
39
+
40
+ task_id: str = Field(
41
+ description=(
42
+ "One of: null_audit, type_repair, dedup_bug, fk_integrity, schema_drift"
43
+ )
44
+ )
45
+ task_description: str = Field(
46
+ description="Natural language description of the task"
47
+ )
48
+ schema_info: list[SchemaInfo] = Field(description="Schema of all relevant tables")
49
+ sample_rows: dict[str, list[dict]] = Field(
50
+ description="First 5 rows of each table as list of dicts"
51
+ )
52
+ episode_id: Optional[str] = Field(
53
+ default=None, description="UUID4 identifying this episode"
54
+ )
55
+ attempt_number: int = Field(ge=1, description="Current attempt (1-indexed)")
56
+ max_attempts: int = Field(
57
+ ge=1, description="Maximum attempts allowed for this task"
58
+ )
59
+ previous_feedback: Optional[str] = Field(
60
+ default=None, description="Feedback from previous attempt, if any"
61
+ )
62
+ # NEW FIELDS:
63
+ trajectory_history: list[dict] = Field(
64
+ default_factory=list,
65
+ description=(
66
+ "List of previous actions and their results in this episode. "
67
+ "Each entry: {action_type, sql_or_answer, result_preview, step_reward}. "
68
+ "Empty on first step after reset()."
69
+ ),
70
+ )
71
+ available_actions: list[str] = Field(
72
+ default_factory=lambda: ["query", "profile", "fix", "submit"],
73
+ description="Action types available at this point in the episode.",
74
+ )
75
+ investigation_hint: str | None = Field(
76
+ default=None,
77
+ description=(
78
+ "Hint provided after a failed attempt. None on first step. "
79
+ "Populated with actionable feedback after each scored action."
80
+ ),
81
+ )
82
+
83
+
84
+ class SQLAction(BaseModel):
85
+ """
86
+ Agent action for sql_quality_env.
87
+
88
+ Fields:
89
+ task_id: Which task this action targets.
90
+ action_type: One of QUERY / PROFILE / FIX / SUBMIT.
91
+ sql: For QUERY and FIX actions β€” the SQL statement to execute.
92
+ column_name: For PROFILE actions β€” the column to profile.
93
+ answer: For null_audit SUBMIT β€” the column name identified.
94
+ fix_query: Alias for sql in FIX context (kept for backward compat).
95
+ root_cause_table: For dedup_bug SUBMIT β€” the table with duplicates.
96
+ offending_key: For dedup_bug SUBMIT β€” the key column with duplicates.
97
+ """
98
+
99
+ task_id: str
100
+ action_type: ActionType = ActionType.SUBMIT
101
+ sql: str | None = None
102
+ column_name: str | None = None
103
+ answer: str | None = None
104
+ fix_query: str | None = None
105
+ root_cause_table: str | None = None
106
+ offending_key: str | None = None
107
+
108
+ @property
109
+ def effective_sql(self) -> str | None:
110
+ """Return sql or fix_query, whichever is set. fix_query takes precedence."""
111
+ return self.fix_query or self.sql
112
+
113
+
114
+ class SQLReward(BaseModel):
115
+ """
116
+ Reward returned after each step.
117
+
118
+ Fields:
119
+ score: Episode-level composite score 0.0–1.0.
120
+ step_reward: Immediate reward for this specific action (not cumulative).
121
+ Used for dense reward shaping in RL training.
122
+ feedback: Human-readable explanation of what was scored and why.
123
+ dimensions: Per-dimension breakdown. Present only after SUBMIT.
124
+ None for intermediate QUERY/PROFILE/FIX actions.
125
+ partial_scores: Legacy field kept for backward compatibility.
126
+ """
127
+
128
+ score: float
129
+ step_reward: float = 0.0
130
+ feedback: str = ""
131
+ dimensions: dict | None = None
132
+ partial_scores: dict | None = None
133
+
134
+ @field_validator("score", "step_reward")
135
+ @classmethod
136
+ def clamp_to_unit(cls, v: float) -> float:
137
+ """Ensure scores stay in [0.0, 1.0]."""
138
+ return round(max(0.0, min(1.0, v)), 4)
139
+
140
+
141
+ class TrajectoryStep(BaseModel):
142
+ """A single step in the agent's investigation trajectory."""
143
+
144
+ step_number: int
145
+ action_type: ActionType
146
+ sql_submitted: str | None = None
147
+ answer_submitted: str | None = None
148
+ result_preview: str = ""
149
+ step_reward: float = 0.0
150
+ timestamp_ms: int = 0
151
+
152
+
153
+ class SQLState(BaseModel):
154
+ """Current episode state."""
155
+
156
+ task_id: str
157
+ episode_id: str = Field(description="UUID4 identifying this episode")
158
+ attempt_number: int
159
+ cumulative_reward: float = Field(ge=0.0)
160
+ done: bool
161
+
162
+
163
+ class ResetRequest(BaseModel):
164
+ """Request body for /reset endpoint."""
165
+
166
+ task_id: str = Field(
167
+ description=(
168
+ "One of: null_audit, type_repair, dedup_bug, fk_integrity, schema_drift"
169
+ )
170
+ )
171
+ seed: int | None = Field(
172
+ default=None,
173
+ description=(
174
+ "Optional deterministic episode seed. If omitted, environment chooses a random seed."
175
+ ),
176
+ )
177
+
178
+
179
+ class StepResponse(BaseModel):
180
+ """Response body for /step endpoint."""
181
+
182
+ observation: SQLObservation
183
+ reward: SQLReward
184
+ done: bool
185
+ info: dict
186
+
187
+
188
+ class HealthResponse(BaseModel):
189
+ """Response body for health/ping endpoints."""
190
+
191
+ status: str = Field(description="Service health status")
192
+ llm_available: Optional[bool] = Field(default=None, description="LLM connection status")