Deadman922 commited on
Commit
ba19f6d
·
verified ·
1 Parent(s): 1d259db

Upload 2 files

Browse files
Files changed (2) hide show
  1. inference.py +10 -3
  2. server/main.py +125 -82
inference.py CHANGED
@@ -19,12 +19,19 @@ from typing import Any, Dict, List, Optional
19
 
20
  import requests
21
  from openai import OpenAI
 
 
 
 
 
 
 
22
 
23
  # Configuration from environment
24
  MAX_STEPS = int(os.getenv("MAX_STEPS", "10"))
25
- API_BASE_URL = os.getenv("API_BASE_URL", "https://api.groq.com/openai/v1")
26
- MODEL_NAME = os.getenv("MODEL_NAME", "llama-3.3-70b-versatile")
27
- HF_TOKEN = os.getenv("HF_TOKEN", "")
28
  API_KEY = os.getenv("GROQ_API_KEY", "") or HF_TOKEN or os.getenv("OPENAI_API_KEY", "")
29
 
30
 
 
19
 
20
  import requests
21
  from openai import OpenAI
22
+ try:
23
+ from dotenv import load_dotenv
24
+ except Exception: # pragma: no cover - optional dependency guard
25
+ load_dotenv = None
26
+
27
+ if load_dotenv:
28
+ load_dotenv()
29
 
30
  # Configuration from environment
31
  MAX_STEPS = int(os.getenv("MAX_STEPS", "10"))
32
+ API_BASE_URL = os.getenv("API_BASE_URL", "<your-active-endpoint>")
33
+ MODEL_NAME = os.getenv("MODEL_NAME", "<your-active-model>")
34
+ HF_TOKEN = os.getenv("HF_TOKEN")
35
  API_KEY = os.getenv("GROQ_API_KEY", "") or HF_TOKEN or os.getenv("OPENAI_API_KEY", "")
36
 
37
 
server/main.py CHANGED
@@ -12,6 +12,11 @@ from difflib import get_close_matches
12
  from fastapi import FastAPI, HTTPException, UploadFile, File, Form
13
  from fastapi.responses import HTMLResponse, JSONResponse
14
 
 
 
 
 
 
15
  from .environment import (
16
  SQLQualityEnvironment,
17
  analyze_uploaded_database,
@@ -31,6 +36,9 @@ from .models import (
31
  StepResponse,
32
  )
33
 
 
 
 
34
  app = FastAPI(
35
  title="SQL Quality Audit Agent Environment",
36
  description="OpenEnv-compatible environment for training AI agents on SQL data quality tasks",
@@ -79,6 +87,77 @@ def schema_table_map(schema: list[dict]) -> dict[str, dict]:
79
  return {table["table_name"]: table for table in schema}
80
 
81
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
  def load_static_html() -> str:
83
  """Load the static HTML interface for the App section."""
84
  # Look for static/index.html in various locations
@@ -122,34 +201,21 @@ async def health() -> dict:
122
  Returns:
123
  dict: Status response with server and LLM connection status
124
  """
125
- # Check LLM availability - MOCK MODE ENABLED by default for offline operation
126
- llm_available = False
127
- mock_mode = True # Default to mock mode so app works without HF_TOKEN
128
-
129
- conn = None
130
- try:
131
- import os
132
- hf_token = os.getenv("HF_TOKEN", "")
133
- api_base_url = os.getenv("API_BASE_URL", "")
134
- model_name = os.getenv("MODEL_NAME", "")
135
-
136
- # Only require LLM if explicitly configured
137
- if hf_token and api_base_url and model_name:
138
- llm_available = True
139
- mock_mode = False
140
- else:
141
- # Mock mode: app works without LLM for basic SQL operations
142
- llm_available = True # Report as available so UI doesn't hang
143
- mock_mode = True
144
- except Exception:
145
- pass
146
-
147
  return {
148
  "status": "ok",
149
  "llm": "mock_mode" if mock_mode else "connected",
150
  "llm_available": llm_available,
151
  "mock_mode": mock_mode,
152
- "message": "Mock Mode: LLM features disabled. Basic SQL query and task functionality available." if mock_mode else "LLM Connected"
 
153
  }
154
 
155
 
@@ -163,50 +229,41 @@ async def status() -> dict:
163
  Returns:
164
  dict: Status response with llm_connected boolean
165
  """
166
- llm_connected = False # Default to disconnected until proven otherwise
 
 
167
  llm_error = None
168
- mock_mode = False # Default to real mode, determine based on env vars
169
-
170
- try:
171
- import os
172
- hf_token = os.getenv("HF_TOKEN", "")
173
- api_base_url = os.getenv("API_BASE_URL", "")
174
- model_name = os.getenv("MODEL_NAME", "")
175
-
176
- if hf_token and api_base_url and model_name:
177
- # Real LLM mode - attempt connection test
178
- mock_mode = False
179
- try:
180
- import requests
181
- headers = {"Authorization": f"Bearer {hf_token}"}
182
- # Simple model info request (lightweight)
183
- response = requests.get(
184
- f"{api_base_url}/models",
185
- headers=headers,
186
- timeout=5
187
- )
188
- llm_connected = response.status_code == 200
189
- except Exception as e:
190
- llm_error = str(e)
191
- # Fallback to env check only
192
- llm_connected = True # Assume connected if env vars present
193
- else:
194
- # No LLM configured - report as disconnected
195
- mock_mode = True
196
- llm_connected = False
197
- llm_error = "HF_TOKEN, API_BASE_URL, and MODEL_NAME required for LLM"
198
- except Exception as e:
199
- llm_error = str(e)
200
- # In case of error, report as disconnected
201
- llm_connected = False
202
- mock_mode = True
203
-
204
  return {
205
  "status": "online",
206
  "llm": "mock_mode" if mock_mode else ("connected" if llm_connected else "disconnected"),
207
  "llm_connected": llm_connected,
208
  "llm_error": llm_error,
209
- "mock_mode": mock_mode
 
210
  }
211
 
212
 
@@ -1631,33 +1688,19 @@ async def custom_db_status() -> dict:
1631
 
1632
  def _get_ai_client():
1633
  """Get an OpenAI-compatible client for AI insights.
1634
-
1635
- Priority:
1636
- 1. GROQ_API_KEY → Groq API (fast, reliable)
1637
- 2. HF_TOKEN + API_BASE_URL → HuggingFace Inference
1638
- 3. None → fallback to local heuristics
1639
  """
1640
- groq_key = os.getenv("GROQ_API_KEY", "")
1641
- if groq_key:
1642
  try:
1643
  from openai import OpenAI
 
1644
  return OpenAI(
1645
- api_key=groq_key,
1646
- base_url="https://api.groq.com/openai/v1"
1647
- ), os.getenv("GROQ_MODEL", "llama-3.3-70b-versatile")
1648
  except Exception:
1649
  pass
1650
-
1651
- hf_token = os.getenv("HF_TOKEN", "")
1652
- api_base = os.getenv("API_BASE_URL", "")
1653
- model_name = os.getenv("MODEL_NAME", "")
1654
- if hf_token and api_base and model_name:
1655
- try:
1656
- from openai import OpenAI
1657
- return OpenAI(api_key=hf_token, base_url=api_base), model_name
1658
- except Exception:
1659
- pass
1660
-
1661
  return None, None
1662
 
1663
 
 
12
  from fastapi import FastAPI, HTTPException, UploadFile, File, Form
13
  from fastapi.responses import HTMLResponse, JSONResponse
14
 
15
+ try:
16
+ from dotenv import load_dotenv
17
+ except Exception: # pragma: no cover - optional dependency guard
18
+ load_dotenv = None
19
+
20
  from .environment import (
21
  SQLQualityEnvironment,
22
  analyze_uploaded_database,
 
36
  StepResponse,
37
  )
38
 
39
+ if load_dotenv:
40
+ load_dotenv()
41
+
42
  app = FastAPI(
43
  title="SQL Quality Audit Agent Environment",
44
  description="OpenEnv-compatible environment for training AI agents on SQL data quality tasks",
 
87
  return {table["table_name"]: table for table in schema}
88
 
89
 
90
+ def _first_non_empty(*values: str) -> str:
91
+ """Return the first non-empty stripped string."""
92
+ for value in values:
93
+ candidate = str(value or "").strip()
94
+ if candidate:
95
+ return candidate
96
+ return ""
97
+
98
+
99
+ def _resolve_llm_runtime() -> dict[str, Any]:
100
+ """Resolve OpenAI-compatible runtime config from environment variables."""
101
+ groq_key = _first_non_empty(os.getenv("GROQ_API_KEY", ""))
102
+ if groq_key:
103
+ return {
104
+ "configured": True,
105
+ "provider": "groq",
106
+ "api_key": groq_key,
107
+ "base_url": _first_non_empty(
108
+ os.getenv("GROQ_BASE_URL", ""),
109
+ os.getenv("API_BASE_URL", ""),
110
+ "https://api.groq.com/openai/v1",
111
+ ),
112
+ "model": _first_non_empty(
113
+ os.getenv("GROQ_MODEL", ""),
114
+ os.getenv("MODEL_NAME", ""),
115
+ "llama-3.3-70b-versatile",
116
+ ),
117
+ "missing": [],
118
+ }
119
+
120
+ hf_token = _first_non_empty(os.getenv("HF_TOKEN", ""))
121
+ openai_key = _first_non_empty(os.getenv("OPENAI_API_KEY", ""), os.getenv("API_KEY", ""))
122
+ provider = "huggingface" if hf_token else ("openai" if openai_key else "none")
123
+ api_key = hf_token or openai_key
124
+
125
+ if provider == "openai":
126
+ base_url = _first_non_empty(
127
+ os.getenv("OPENAI_BASE_URL", ""),
128
+ os.getenv("API_BASE_URL", ""),
129
+ "https://api.openai.com/v1",
130
+ )
131
+ else:
132
+ base_url = _first_non_empty(os.getenv("API_BASE_URL", ""), os.getenv("OPENAI_BASE_URL", ""))
133
+
134
+ model_name = _first_non_empty(os.getenv("MODEL_NAME", ""), os.getenv("OPENAI_MODEL", ""))
135
+
136
+ missing: list[str] = []
137
+ if not api_key:
138
+ missing.append("HF_TOKEN or OPENAI_API_KEY")
139
+ if not base_url:
140
+ missing.append("API_BASE_URL")
141
+ if not model_name:
142
+ missing.append("MODEL_NAME")
143
+
144
+ return {
145
+ "configured": bool(api_key and base_url and model_name),
146
+ "provider": provider,
147
+ "api_key": api_key,
148
+ "base_url": base_url,
149
+ "model": model_name,
150
+ "missing": missing,
151
+ }
152
+
153
+
154
+ def _format_llm_missing_message(missing: list[str]) -> str:
155
+ """Build a user-friendly missing-config message."""
156
+ if not missing:
157
+ return "LLM configuration incomplete."
158
+ return f"Missing LLM configuration: {', '.join(missing)}"
159
+
160
+
161
  def load_static_html() -> str:
162
  """Load the static HTML interface for the App section."""
163
  # Look for static/index.html in various locations
 
201
  Returns:
202
  dict: Status response with server and LLM connection status
203
  """
204
+ runtime = _resolve_llm_runtime()
205
+ llm_available = bool(runtime["configured"])
206
+ mock_mode = not llm_available
207
+ if llm_available:
208
+ message = "LLM Connected"
209
+ else:
210
+ message = _format_llm_missing_message(runtime["missing"])
211
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
212
  return {
213
  "status": "ok",
214
  "llm": "mock_mode" if mock_mode else "connected",
215
  "llm_available": llm_available,
216
  "mock_mode": mock_mode,
217
+ "provider": runtime["provider"],
218
+ "message": message,
219
  }
220
 
221
 
 
229
  Returns:
230
  dict: Status response with llm_connected boolean
231
  """
232
+ runtime = _resolve_llm_runtime()
233
+ mock_mode = not bool(runtime["configured"])
234
+ llm_connected = bool(runtime["configured"])
235
  llm_error = None
236
+
237
+ if llm_connected:
238
+ try:
239
+ import requests
240
+
241
+ headers = {"Authorization": f"Bearer {runtime['api_key']}"}
242
+ probe_url = f"{runtime['base_url'].rstrip('/')}/models"
243
+ response = requests.get(probe_url, headers=headers, timeout=5)
244
+ if response.status_code == 200:
245
+ llm_connected = True
246
+ elif response.status_code in {401, 403}:
247
+ llm_connected = False
248
+ llm_error = f"LLM auth failed (HTTP {response.status_code})"
249
+ else:
250
+ # Some providers do not expose /models; keep configured-as-connected.
251
+ llm_connected = True
252
+ llm_error = f"LLM heartbeat warning (HTTP {response.status_code})"
253
+ except Exception as e:
254
+ # Keep configured-as-connected to avoid false negatives from transient network errors.
255
+ llm_connected = True
256
+ llm_error = f"LLM heartbeat skipped: {e}"
257
+ else:
258
+ llm_error = _format_llm_missing_message(runtime["missing"])
259
+
 
 
 
 
 
 
 
 
 
 
 
 
260
  return {
261
  "status": "online",
262
  "llm": "mock_mode" if mock_mode else ("connected" if llm_connected else "disconnected"),
263
  "llm_connected": llm_connected,
264
  "llm_error": llm_error,
265
+ "mock_mode": mock_mode,
266
+ "provider": runtime["provider"],
267
  }
268
 
269
 
 
1688
 
1689
  def _get_ai_client():
1690
  """Get an OpenAI-compatible client for AI insights.
 
 
 
 
 
1691
  """
1692
+ runtime = _resolve_llm_runtime()
1693
+ if runtime["configured"]:
1694
  try:
1695
  from openai import OpenAI
1696
+
1697
  return OpenAI(
1698
+ api_key=runtime["api_key"],
1699
+ base_url=runtime["base_url"],
1700
+ ), runtime["model"]
1701
  except Exception:
1702
  pass
1703
+
 
 
 
 
 
 
 
 
 
 
1704
  return None, None
1705
 
1706