DecentSanage commited on
Commit
319b4a3
Β·
verified Β·
1 Parent(s): 6293ebc

Upload folder using huggingface_hub

Browse files
README.md CHANGED
@@ -1,12 +1,12 @@
1
  ---
2
  title: Constraint Environment
3
- emoji: 🧩
4
  colorFrom: purple
5
  colorTo: blue
6
  sdk: docker
7
  pinned: false
8
  license: mit
9
- short_description: RL training env β€” natural language to constraint AST
10
  base_path: /web
11
  ---
12
 
@@ -211,22 +211,21 @@ INFO: 10.16.33.124:5187 - "GET /web HTTP/1.1" 307 Temporary Redirect
211
  INFO: 10.16.24.44:32462 - "GET /web/ HTTP/1.1" 200 OK
212
  ```
213
 
214
- ## Custom Web UI
215
 
216
- When `ENABLE_WEB_INTERFACE=true` the server mounts a **tabbed Gradio interface** at `/web`:
217
 
218
  | Tab | Description |
219
  |-----|-------------|
220
- | **Playground** | Default OpenEnv UI with Reset / Step / Get State controls |
221
- | **Constraint Compiler** | Our custom tab β€” task selector, full AST code editor, sample loaders, node-structure reference, and a real-time compiler chatbot |
222
-
223
- ### Trying the Compiler tab
224
- 1. Select a difficulty (`easy / medium / hard`) and click **Reset / Load Task**.
225
- 2. The prompt appears and the editor is pre-filled with a correct sample AST.
226
- 3. Edit the AST and click **β–Ά Submit to Compiler** β€” the chatbot shows the reward, error code, and exact compiler message.
227
- 4. Use **πŸ“š Load Sample ASTs** to reload any of the 3 canonical examples instantly.
228
-
229
- > The custom tab is implemented in `server/gradio_ui.py` via the `gradio_builder` extension point provided by OpenEnv core.
230
 
231
  ## Project Structure
232
 
 
1
  ---
2
  title: Constraint Environment
3
+ emoji: πŸ‘¨β€πŸ”§
4
  colorFrom: purple
5
  colorTo: blue
6
  sdk: docker
7
  pinned: false
8
  license: mit
9
+ short_description: RL training env for natural language to constraint AST
10
  base_path: /web
11
  ---
12
 
 
211
  INFO: 10.16.24.44:32462 - "GET /web/ HTTP/1.1" 200 OK
212
  ```
213
 
214
+ ## Web UI & Interactive Compiler
215
 
216
+ When `ENABLE_WEB_INTERFACE=true` (or when deployed to Hugging Face Spaces), the server mounts a **tabbed Gradio interface** at `/web`, natively featuring our custom compiler interface as the primary landing page:
217
 
218
  | Tab | Description |
219
  |-----|-------------|
220
+ | **Constraint Compiler** | **(Default First Page)** Our custom built interactive AST environment β€” featuring a task difficulty selector, full JSON syntax AST code editor, interactive single-click AST sample loaders, node-structure references, and a real-time syntax/logic compiler chatbot. |
221
+ | **Base Playground** | The fallback default OpenEnv UI, displaying stateless Reset / Step / Get State controls for the raw API. |
222
+
223
+ ### Trying the Compiler Loop (First Page)
224
+ 1. Navigate to `/web` to access the **Constraint Compiler**.
225
+ 2. Start by expanding the **πŸ“š Load Sample ASTs** accordion and clicking one of the sample buttons (`🟒 Easy Sample`, `🟑 Medium Sample`, `πŸ”΄ Hard Sample`). This instantly populates the compiler code editor with a canonical AST structure avoiding manual entry.
226
+ 3. Select a difficulty (`easy / medium / hard`) in the task dropdown and click **Reset / Load Task**.
227
+ 4. The prompt will update. Edit the loaded AST in the code editor to attempt to solve the logic.
228
+ 5. Click **β–Ά Submit to Compiler** β€” the chatbot will parse the payload and display the reward, error code, and precise compiler traceback feedback natively inline!
 
229
 
230
  ## Project Structure
231
 
inference.py CHANGED
@@ -249,7 +249,8 @@ def _run_task(task_id: str, env_url: str = "http://localhost:8000") -> None:
249
  messages = getattr(obs_data, "messages", []) if not isinstance(obs_data, dict) else obs_data.get("messages", [])
250
 
251
  if step_count > 0 and messages:
252
- prompt_text += "\n\n" + "\n".join(messages)
 
253
 
254
  # Generate AST from LLM with retry loop
255
  raw_output = "{}"
 
249
  messages = getattr(obs_data, "messages", []) if not isinstance(obs_data, dict) else obs_data.get("messages", [])
250
 
251
  if step_count > 0 and messages:
252
+ parsed = [m.get("content", str(m)) if isinstance(m, dict) else str(m) for m in messages]
253
+ prompt_text += "\n\n" + "\n".join(parsed)
254
 
255
  # Generate AST from LLM with retry loop
256
  raw_output = "{}"
models.py CHANGED
@@ -39,7 +39,7 @@ class ConstraintObservation(Observation):
39
  done: bool
40
  reward: float
41
  info: Dict[str, Any]
42
- messages: list[str] = []
43
 
44
 
45
  class ConstraintState(State):
 
39
  done: bool
40
  reward: float
41
  info: Dict[str, Any]
42
+ messages: list[Dict[str, Any]] = []
43
 
44
 
45
  class ConstraintState(State):
server/app.py CHANGED
@@ -66,6 +66,22 @@ except ImportError:
66
  _gradio_builder = None
67
 
68
  # Create the app – pass the factory so create_app calls _make_env() per session.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
  app = create_app(
70
  _make_env,
71
  ConstraintAction,
 
66
  _gradio_builder = None
67
 
68
  # Create the app – pass the factory so create_app calls _make_env() per session.
69
+ import gradio as gr
70
+ _orig_tabbed = gr.TabbedInterface
71
+
72
+ def _swapped_tabbed(interface_list, tab_names, **kwargs):
73
+ if len(interface_list) == 2 and "Custom" in tab_names:
74
+ idx_custom = tab_names.index("Custom")
75
+ idx_play = tab_names.index("Playground")
76
+ return _orig_tabbed(
77
+ [interface_list[idx_custom], interface_list[idx_play]],
78
+ ["Constraint Compiler", "Base Playground"],
79
+ **kwargs
80
+ )
81
+ return _orig_tabbed(interface_list, tab_names, **kwargs)
82
+
83
+ gr.TabbedInterface = _swapped_tabbed
84
+
85
  app = create_app(
86
  _make_env,
87
  ConstraintAction,
server/constraint_env_environment.py CHANGED
@@ -158,9 +158,9 @@ class ConstraintEnvironment(Environment):
158
  except (json.JSONDecodeError, TypeError) as exc:
159
  info["error"] = "invalid_json"
160
  messages.extend([
161
- "Your last submitted AST:",
162
- str(action.ast_output),
163
- f"Compiler Error: Syntax Error. Invalid JSON β€” {exc}"
164
  ])
165
 
166
  # ── 2. Logic match (ignores "name") ──────────────────────────
@@ -171,29 +171,31 @@ class ConstraintEnvironment(Environment):
171
  else:
172
  info["exact_match"] = False
173
 
174
- # ── 3. Validate structure ─────────────────────────────────────
175
- # ── 3. Validate structure ─────────────────────────────────────
176
  if is_exact_match:
177
  is_valid_structure = True
178
  elif is_valid_json:
 
 
 
 
179
  valid, msg = self._validate_structure(ast)
180
  if valid:
181
  is_valid_structure = True
182
  # NEW FIX: Provide feedback when structure is valid but logic fails!
183
  info["error"] = "logic_mismatch"
184
  messages.extend([
185
- "Your last submitted AST:",
186
- action.ast_output,
187
- "Compiler Error: Syntax is valid, but the logic does not match the prompt's target constraint. Please adjust your logical conditions and resubmit."
188
  ])
189
  else:
190
  info["error"] = "bad_structure"
191
  messages.extend([
192
- "Your last submitted AST:",
193
- action.ast_output,
194
- f"Compiler Error: {msg}"
195
  ])
196
-
197
  done = is_exact_match or self._state.step_count >= self._state.max_steps
198
  reward = calculate_step_reward(is_valid_json, is_valid_structure, is_exact_match, self._state.step_count)
199
 
 
158
  except (json.JSONDecodeError, TypeError) as exc:
159
  info["error"] = "invalid_json"
160
  messages.extend([
161
+ {"role": "assistant", "content": "Your last submitted AST:"},
162
+ {"role": "assistant", "content": str(action.ast_output)},
163
+ {"role": "assistant", "content": f"Compiler Error: Syntax Error. Invalid JSON β€” {exc}"}
164
  ])
165
 
166
  # ── 2. Logic match (ignores "name") ──────────────────────────
 
171
  else:
172
  info["exact_match"] = False
173
 
174
+ # ── 3. Validate structure ─────────────────────────────────────
 
175
  if is_exact_match:
176
  is_valid_structure = True
177
  elif is_valid_json:
178
+
179
+ # THE FIX: Safely convert the AST to a string so Pydantic doesn't crash!
180
+ safe_ast_str = json.dumps(ast) if isinstance(ast, dict) else str(action.ast_output)
181
+
182
  valid, msg = self._validate_structure(ast)
183
  if valid:
184
  is_valid_structure = True
185
  # NEW FIX: Provide feedback when structure is valid but logic fails!
186
  info["error"] = "logic_mismatch"
187
  messages.extend([
188
+ {"role": "assistant", "content": "Your last submitted AST:"},
189
+ {"role": "assistant", "content": safe_ast_str},
190
+ {"role": "assistant", "content": "Compiler Error: Syntax is valid, but the logic does not match the prompt's target constraint. Please adjust your logical conditions and resubmit."}
191
  ])
192
  else:
193
  info["error"] = "bad_structure"
194
  messages.extend([
195
+ {"role": "assistant", "content": "Your last submitted AST:"},
196
+ {"role": "assistant", "content": safe_ast_str},
197
+ {"role": "assistant", "content": f"Compiler Error: {msg}"}
198
  ])
 
199
  done = is_exact_match or self._state.step_count >= self._state.max_steps
200
  reward = calculate_step_reward(is_valid_json, is_valid_structure, is_exact_match, self._state.step_count)
201
 
server/gradio_ui.py CHANGED
@@ -183,7 +183,13 @@ def build_constraint_gradio_ui(web_manager, action_fields, metadata, is_chat_env
183
  else:
184
  status = f"ℹ️ {error}"
185
 
186
- compiler_msg = "\n".join(msgs) if msgs else ("βœ… Correct! Episode complete." if exact else "No compiler feedback.")
 
 
 
 
 
 
187
  done_badge = " 🏁 Episode Done" if done else ""
188
  entry = (
189
  f"[YOU]\n{ast_text[:400]}\n\n"
 
183
  else:
184
  status = f"ℹ️ {error}"
185
 
186
+ parsed_msgs = []
187
+ for m in msgs:
188
+ if isinstance(m, dict):
189
+ parsed_msgs.append(m.get("content", str(m)))
190
+ else:
191
+ parsed_msgs.append(str(m))
192
+ compiler_msg = "\n".join(parsed_msgs) if parsed_msgs else ("βœ… Correct! Episode complete." if exact else "No compiler feedback.")
193
  done_badge = " 🏁 Episode Done" if done else ""
194
  entry = (
195
  f"[YOU]\n{ast_text[:400]}\n\n"