3v324v23 commited on
Commit
0c8a432
·
1 Parent(s): b358ab7

Final production readiness fixes: UI contrast, Groq support, dynamic headers, and removal of hardcoded elements.

Browse files
Files changed (5) hide show
  1. app.py +102 -3
  2. server/app.py +21 -0
  3. server/environment.py +2 -1
  4. server/tasks/custom.py +19 -0
  5. start_local.ps1 +8 -0
app.py CHANGED
@@ -128,12 +128,50 @@ st.markdown("""
128
  text-transform: uppercase !important;
129
  margin-bottom: 0.5rem !important;
130
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131
  </style>
132
  """, unsafe_allow_html=True)
133
 
134
  # ─── Constants ───────────────────────────────────────────────────────────────
135
- ENV_BASE_URL = "http://localhost:7860"
136
- TASKS = ["single-pass-review", "iterative-negotiation", "escalation-judgment"]
137
 
138
  # ─── Helper Functions ─────────────────────────────────────────────────────────
139
  def get_agent_action(obs: dict, model_name: str, api_base: str, token: str) -> dict:
@@ -164,8 +202,13 @@ You must respond with EXACTLY this JSON format and nothing else:
164
 
165
  def format_diff_html(diff_text: str):
166
  lines = diff_text.split("\n")
 
 
 
 
 
167
  html_lines = ['<div class="diff-container">']
168
- html_lines.append('<div class="diff-header-bar"><span>src/api/users.py</span><span>Active Hunks</span></div>')
169
 
170
  for i, line in enumerate(lines):
171
  if line.startswith("+++") or line.startswith("---"):
@@ -200,6 +243,14 @@ init_state()
200
 
201
  # ─── Sidebar ───────────────────────────────────────────────────────────────────
202
  with st.sidebar:
 
 
 
 
 
 
 
 
203
  st.markdown('<div class="section-label">Task Difficulty</div>', unsafe_allow_html=True)
204
  task_name = st.selectbox("Select Task", TASKS, label_visibility="collapsed")
205
 
@@ -212,6 +263,54 @@ with st.sidebar:
212
  st.markdown('<div class="section-label">HF Token</div>', unsafe_allow_html=True)
213
  hf_token = st.text_input("Token", type="password", value=os.getenv("HF_TOKEN", ""), label_visibility="collapsed")
214
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
215
  st.divider()
216
 
217
  if st.button("Initialize environment", use_container_width=True):
 
128
  text-transform: uppercase !important;
129
  margin-bottom: 0.5rem !important;
130
  }
131
+
132
+ /* Visibility Fixes — Ensures all text is high contrast */
133
+ [data-testid="stWidgetLabel"] p, [data-testid="stWidgetLabel"], .section-label {
134
+ color: #1e293b !important;
135
+ font-weight: 700 !important;
136
+ text-transform: uppercase !important;
137
+ }
138
+
139
+ /* Force high contrast for all alert/info boxes */
140
+ .stAlert, [data-testid="stNotification"], [data-testid="stNotificationContent"] {
141
+ background-color: #ffffff !important;
142
+ border: 1px solid #004085 !important;
143
+ }
144
+
145
+ .stAlert p, .stAlert div, [data-testid="stNotification"] p, [data-testid="stNotification"] div {
146
+ color: #004085 !important;
147
+ font-weight: 600 !important;
148
+ }
149
+
150
+ /* Primary Button Styling */
151
+ button[kind="primary"] {
152
+ background-color: #1a7f3c !important;
153
+ color: white !important;
154
+ border: none !important;
155
+ font-weight: 700 !important;
156
+ }
157
+ button[kind="secondary"] {
158
+ background-color: #f6f8fa !important;
159
+ color: #24292f !important;
160
+ border: 1px solid #d0d7de !important;
161
+ font-weight: 600 !important;
162
+ }
163
+
164
+ /* Form Field Colors */
165
+ .stTextArea textarea, .stTextInput input, .stSelectbox select {
166
+ color: #1e293b !important;
167
+ border: 1px solid #d0d7de !important;
168
+ }
169
  </style>
170
  """, unsafe_allow_html=True)
171
 
172
  # ─── Constants ───────────────────────────────────────────────────────────────
173
+ ENV_BASE_URL = "http://localhost:8000"
174
+ TASKS = ["single-pass-review", "iterative-negotiation", "escalation-judgment", "custom-review"]
175
 
176
  # ─── Helper Functions ─────────────────────────────────────────────────────────
177
  def get_agent_action(obs: dict, model_name: str, api_base: str, token: str) -> dict:
 
202
 
203
  def format_diff_html(diff_text: str):
204
  lines = diff_text.split("\n")
205
+ filename = "unknown_file"
206
+ for line in lines:
207
+ if line.startswith("--- a/"): filename = line.replace("--- a/", "")
208
+ elif line.startswith("+++ b/"): filename = line.replace("+++ b/", "")
209
+
210
  html_lines = ['<div class="diff-container">']
211
+ html_lines.append(f'<div class="diff-header-bar"><span>{filename}</span><span>Active Hunks</span></div>')
212
 
213
  for i, line in enumerate(lines):
214
  if line.startswith("+++") or line.startswith("---"):
 
243
 
244
  # ─── Sidebar ───────────────────────────────────────────────────────────────────
245
  with st.sidebar:
246
+ # Health Check
247
+ try:
248
+ h = httpx.get(f"{ENV_BASE_URL}/health", timeout=1)
249
+ if h.status_code == 200: st.sidebar.caption("🟢 API Engine Online")
250
+ else: st.sidebar.caption("🔴 API Engine Error")
251
+ except:
252
+ st.sidebar.caption("⚪ API Engine Connecting...")
253
+
254
  st.markdown('<div class="section-label">Task Difficulty</div>', unsafe_allow_html=True)
255
  task_name = st.selectbox("Select Task", TASKS, label_visibility="collapsed")
256
 
 
263
  st.markdown('<div class="section-label">HF Token</div>', unsafe_allow_html=True)
264
  hf_token = st.text_input("Token", type="password", value=os.getenv("HF_TOKEN", ""), label_visibility="collapsed")
265
 
266
+ # Groq Auto-Detection
267
+ is_groq = "groq" in model_id.lower() or (hf_token.startswith("gsk_"))
268
+ if is_groq and "groq" not in api_url.lower():
269
+ st.warning("⚠️ Groq detected. Recommended API URL: https://api.groq.com/openai/v1")
270
+ if st.button("Use Groq API URL"):
271
+ st.session_state["api_url_override"] = "https://api.groq.com/openai/v1"
272
+ st.rerun()
273
+
274
+ if "api_url_override" in st.session_state:
275
+ api_url = st.session_state["api_url_override"]
276
+
277
+ st.divider()
278
+ st.markdown('<div class="section-label">Custom Review Config</div>', unsafe_allow_html=True)
279
+ custom_title = st.text_input("PR Title", value="Custom Review", key="custom_title")
280
+ custom_desc = st.text_area("PR Description", value="Reviewing custom code...", key="custom_desc")
281
+
282
+ # Selection for local files
283
+ all_files = []
284
+ for root, _, files in os.walk("."):
285
+ if ".git" in root or "__pycache__" in root: continue
286
+ for f in files:
287
+ all_files.append(os.path.join(root, f).replace(".\\", ""))
288
+
289
+ selected_file = st.selectbox("Load from local file", ["-- Select --"] + sorted(all_files))
290
+ loaded_code = ""
291
+ if selected_file != "-- Select --":
292
+ try:
293
+ with open(selected_file, "r") as f:
294
+ loaded_code = f.read()
295
+ except Exception as e:
296
+ st.error(f"Error loading file: {e}")
297
+
298
+ custom_diff = st.text_area("Code to Review", value=loaded_code if loaded_code else "", height=200, key="custom_diff")
299
+
300
+ if st.button("Update Custom Task", use_container_width=True):
301
+ try:
302
+ r = httpx.post(f"{ENV_BASE_URL}/config/custom", json={
303
+ "diff": custom_diff,
304
+ "pr_title": custom_title,
305
+ "pr_description": custom_desc
306
+ }, timeout=30)
307
+ if r.status_code == 200:
308
+ st.success("Custom task updated! Select 'custom-review' above and Initialize.")
309
+ else:
310
+ st.error(f"Failed to configure: {r.text}")
311
+ except Exception as e:
312
+ st.error(f"Connection error: {e}")
313
+
314
  st.divider()
315
 
316
  if st.button("Initialize environment", use_container_width=True):
server/app.py CHANGED
@@ -13,16 +13,37 @@ class ResetRequest(BaseModel):
13
  class StepRequest(BaseModel):
14
  action: PRAction
15
 
 
 
 
 
 
16
  class StepResponse(BaseModel):
17
  observation: PRObservation
18
  reward: float
19
  done: bool
20
  info: dict
21
 
 
 
 
 
 
 
 
 
22
  @app.get("/health")
23
  def health():
24
  return {"status": "ok"}
25
 
 
 
 
 
 
 
 
 
26
  @app.post("/reset", response_model=PRObservation)
27
  def reset(req: ResetRequest = ResetRequest()):
28
  return env.reset(task_name=req.task_name)
 
13
  class StepRequest(BaseModel):
14
  action: PRAction
15
 
16
+ class CustomTaskConfig(BaseModel):
17
+ diff: str
18
+ pr_title: Optional[str] = "Custom Review Session"
19
+ pr_description: Optional[str] = "User-provided code snippet for review."
20
+
21
  class StepResponse(BaseModel):
22
  observation: PRObservation
23
  reward: float
24
  done: bool
25
  info: dict
26
 
27
+ @app.get("/")
28
+ def index():
29
+ return {
30
+ "message": "Backend API is running!",
31
+ "action": "Visit the dashboard at http://localhost:8501",
32
+ "api_docs": "/docs"
33
+ }
34
+
35
  @app.get("/health")
36
  def health():
37
  return {"status": "ok"}
38
 
39
+ @app.post("/config/custom")
40
+ def set_custom_task(config: CustomTaskConfig):
41
+ from server.tasks import custom
42
+ custom.TASK["diff"] = config.diff
43
+ custom.TASK["pr_title"] = config.pr_title
44
+ custom.TASK["pr_description"] = config.pr_description
45
+ return {"status": "success"}
46
+
47
  @app.post("/reset", response_model=PRObservation)
48
  def reset(req: ResetRequest = ResetRequest()):
49
  return env.reset(task_name=req.task_name)
server/environment.py CHANGED
@@ -1,12 +1,13 @@
1
  import uuid
2
  from models import PRAction, PRObservation, PRState, ReviewDecision
3
- from server.tasks import single_pass, iterative, escalation
4
  from server import graders
5
 
6
  TASKS = {
7
  "single-pass-review": single_pass.TASK,
8
  "iterative-negotiation": iterative.TASK,
9
  "escalation-judgment": escalation.TASK,
 
10
  }
11
 
12
  class PRReviewEnvironment:
 
1
  import uuid
2
  from models import PRAction, PRObservation, PRState, ReviewDecision
3
+ from server.tasks import single_pass, iterative, escalation, custom
4
  from server import graders
5
 
6
  TASKS = {
7
  "single-pass-review": single_pass.TASK,
8
  "iterative-negotiation": iterative.TASK,
9
  "escalation-judgment": escalation.TASK,
10
+ "custom-review": custom.TASK,
11
  }
12
 
13
  class PRReviewEnvironment:
server/tasks/custom.py ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Dynamic Custom Task
2
+ # This file is updated via the /set_custom_task API endpoint.
3
+
4
+ TASK = {
5
+ "name": "custom-review",
6
+ "pr_title": "Custom Review Session",
7
+ "pr_description": "User-provided code snippet for review.",
8
+ "diff": "",
9
+ "ground_truth": {
10
+ "bug_type": "unknown",
11
+ "correct_decision": "request_changes",
12
+ "bug_keywords": [],
13
+ },
14
+ "max_turns": 3,
15
+ "author_responses": [
16
+ "I have addressed the concerns in the latest update. Please take a look.",
17
+ "The suggested changes have been implemented. Ready for final review."
18
+ ]
19
+ }
start_local.ps1 ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ # Start Local PR Review Environment
2
+ # This script starts the FastAPI backend and Streamlit dashboard simultaneously on Windows.
3
+
4
+ Write-Host "Starting FastAPI Backend on port 8000..." -ForegroundColor Cyan
5
+ Start-Process powershell -ArgumentList "-NoExit", "-Command", "uvicorn server.app:app --host 0.0.0.0 --port 8000"
6
+
7
+ Write-Host "Starting Streamlit Dashboard on port 8501..." -ForegroundColor Green
8
+ streamlit run app.py --server.port 8501 --server.address 0.0.0.0