VeryOnly123 commited on
Commit
c45955d
·
1 Parent(s): 7d302c5
Files changed (2) hide show
  1. solver.py +27 -24
  2. static/solver.html +7 -11
solver.py CHANGED
@@ -8,14 +8,12 @@ import time
8
  import shutil
9
  import subprocess
10
  import threading
11
- import zipfile
12
  import urllib.request
13
  from datetime import datetime, timezone
14
 
15
  # Solver source location
16
  SOLVER_SRC = "/app/solver"
17
  WORKSPACE_BASE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "workspaces")
18
- DEFAULT_TIMEOUT = int(os.environ.get("SOLVER_TIMEOUT", "600"))
19
  DEFAULT_MODEL = os.environ.get("SOLVER_MODEL", "qwen3.6-plus")
20
  DEFAULT_SOLVER_URL = os.environ.get(
21
  "SOLVER_REPO_URL",
@@ -48,7 +46,6 @@ def _now():
48
  def fetch_solver_files(repo_url):
49
  """Download solver files from GitHub raw URL into /app/solver/."""
50
  os.makedirs(SOLVER_SRC, exist_ok=True)
51
- # Clean existing files
52
  for name in ("run.py", "spawn.py", "outline.md", "config.json"):
53
  p = os.path.join(SOLVER_SRC, name)
54
  if os.path.exists(p):
@@ -133,7 +130,7 @@ def submit_task(problem_text, name=None):
133
 
134
  print(f"[SUBMIT] Created task {task_id} -> {workspace}", flush=True)
135
 
136
- # Spawn background thread (no capacity limit)
137
  t = threading.Thread(target=_run_solver, args=(task_id, workspace), daemon=True)
138
  t.start()
139
  print(f"[SUBMIT] Thread started", flush=True)
@@ -152,7 +149,7 @@ def _run_solver(task_id, workspace):
152
  tasks[task_id].update({"status": "running", "started_at": _now()})
153
 
154
  _log(task_id, f"Starting solver in {workspace}")
155
- _log(task_id, f"Model: {DEFAULT_MODEL}, Timeout: {DEFAULT_TIMEOUT}s")
156
  _log(task_id, f"ANTHROPIC_API_KEY: {'set' if os.environ.get('ANTHROPIC_API_KEY') else 'NOT SET'}")
157
  _log(task_id, f"ANTHROPIC_BASE_URL: {os.environ.get('ANTHROPIC_BASE_URL', '(default)')}")
158
 
@@ -184,21 +181,9 @@ def _run_solver(task_id, workspace):
184
  reader = threading.Thread(target=_read_output, daemon=True)
185
  reader.start()
186
 
187
- # Monitor workspace for progress
188
  last_phase = "starting"
189
  while proc.poll() is None:
190
- elapsed = time.time() - start
191
- if elapsed > DEFAULT_TIMEOUT:
192
- proc.kill()
193
- _log(task_id, f"TIMEOUT after {elapsed:.0f}s")
194
- with tasks_lock:
195
- tasks[task_id].update({
196
- "status": "error",
197
- "error": f"Timeout after {elapsed:.0f}s",
198
- "finished_at": _now(),
199
- })
200
- return
201
-
202
  phase = _infer_phase(workspace)
203
  if phase != last_phase:
204
  _log(task_id, f"Phase: {phase}")
@@ -206,7 +191,7 @@ def _run_solver(task_id, workspace):
206
 
207
  with tasks_lock:
208
  tasks[task_id]["phase"] = phase
209
- tasks[task_id]["elapsed_s"] = round(elapsed)
210
 
211
  time.sleep(2)
212
 
@@ -264,12 +249,13 @@ def _run_solver(task_id, workspace):
264
 
265
 
266
  def _check_files(workspace):
267
- """Return list of completed steps based on workspace files."""
268
- steps = ["plan", "solution", "review", "final_summary"]
269
  done = []
270
- for s in steps:
271
- if os.path.exists(os.path.join(workspace, f"{s}.md")):
272
- done.append(s)
 
 
273
  return done
274
 
275
 
@@ -322,6 +308,23 @@ def get_task_logs(task_id, lines=100):
322
  return [f"Error reading log: {e}"]
323
 
324
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
325
  def list_tasks():
326
  with tasks_lock:
327
  return [
 
8
  import shutil
9
  import subprocess
10
  import threading
 
11
  import urllib.request
12
  from datetime import datetime, timezone
13
 
14
  # Solver source location
15
  SOLVER_SRC = "/app/solver"
16
  WORKSPACE_BASE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "workspaces")
 
17
  DEFAULT_MODEL = os.environ.get("SOLVER_MODEL", "qwen3.6-plus")
18
  DEFAULT_SOLVER_URL = os.environ.get(
19
  "SOLVER_REPO_URL",
 
46
  def fetch_solver_files(repo_url):
47
  """Download solver files from GitHub raw URL into /app/solver/."""
48
  os.makedirs(SOLVER_SRC, exist_ok=True)
 
49
  for name in ("run.py", "spawn.py", "outline.md", "config.json"):
50
  p = os.path.join(SOLVER_SRC, name)
51
  if os.path.exists(p):
 
130
 
131
  print(f"[SUBMIT] Created task {task_id} -> {workspace}", flush=True)
132
 
133
+ # Spawn background thread — run.py manages its own timeout & concurrency
134
  t = threading.Thread(target=_run_solver, args=(task_id, workspace), daemon=True)
135
  t.start()
136
  print(f"[SUBMIT] Thread started", flush=True)
 
149
  tasks[task_id].update({"status": "running", "started_at": _now()})
150
 
151
  _log(task_id, f"Starting solver in {workspace}")
152
+ _log(task_id, f"Model: {DEFAULT_MODEL}")
153
  _log(task_id, f"ANTHROPIC_API_KEY: {'set' if os.environ.get('ANTHROPIC_API_KEY') else 'NOT SET'}")
154
  _log(task_id, f"ANTHROPIC_BASE_URL: {os.environ.get('ANTHROPIC_BASE_URL', '(default)')}")
155
 
 
181
  reader = threading.Thread(target=_read_output, daemon=True)
182
  reader.start()
183
 
184
+ # Monitor workspace for progress (no timeout — run.py handles its own)
185
  last_phase = "starting"
186
  while proc.poll() is None:
 
 
 
 
 
 
 
 
 
 
 
 
187
  phase = _infer_phase(workspace)
188
  if phase != last_phase:
189
  _log(task_id, f"Phase: {phase}")
 
191
 
192
  with tasks_lock:
193
  tasks[task_id]["phase"] = phase
194
+ tasks[task_id]["elapsed_s"] = round(time.time() - start)
195
 
196
  time.sleep(2)
197
 
 
249
 
250
 
251
  def _check_files(workspace):
252
+ """Return list of completed steps based on workspace .md files (excluding problem.md)."""
 
253
  done = []
254
+ if not os.path.isdir(workspace):
255
+ return done
256
+ for fname in sorted(os.listdir(workspace)):
257
+ if fname.endswith(".md") and fname != "problem.md":
258
+ done.append(fname[:-3]) # strip .md
259
  return done
260
 
261
 
 
308
  return [f"Error reading log: {e}"]
309
 
310
 
311
+ def get_running_tasks():
312
+ """Return list of currently running tasks (for frontend recovery after page refresh)."""
313
+ with tasks_lock:
314
+ return [
315
+ {
316
+ "task_id": t["task_id"],
317
+ "status": t["status"],
318
+ "phase": t["phase"],
319
+ "problem": t["problem"],
320
+ "created_at": t["created_at"],
321
+ "files_done": _check_files(t["workspace"]),
322
+ }
323
+ for t in tasks.values()
324
+ if t["status"] in ("pending", "running")
325
+ ]
326
+
327
+
328
  def list_tasks():
329
  with tasks_lock:
330
  return [
static/solver.html CHANGED
@@ -275,18 +275,14 @@
275
  }
276
 
277
  // Task list
278
- const FILE_STEPS = ['plan', 'solution', 'review', 'final_summary'];
279
-
280
  function renderTaskProgress(filesDone, status) {
281
- return FILE_STEPS.map(f => {
282
- let cls = '';
283
- if (filesDone.includes(f)) {
284
- cls = 'done';
285
- } else if (status === 'running') {
286
- cls = 'current';
287
- }
288
- return `<span class="task-file-step ${cls}">${f}</span>`;
289
- }).join('');
290
  }
291
 
292
  async function loadTasks() {
 
275
  }
276
 
277
  // Task list
 
 
278
  function renderTaskProgress(filesDone, status) {
279
+ const steps = filesDone.map(f =>
280
+ `<span class="task-file-step done">${f}</span>`
281
+ );
282
+ if (status === 'running' || status === 'pending') {
283
+ steps.push(`<span class="task-file-step current">running…</span>`);
284
+ }
285
+ return steps.join('');
 
 
286
  }
287
 
288
  async function loadTasks() {