Bug fixes
This commit is contained in:
@@ -7,7 +7,7 @@ LLM backend settings and application constants.
|
||||
# LLM BACKEND CONFIGURATION — Edit these to match your setup
|
||||
# ============================================================
|
||||
LLM_BACKEND = "ollama" # "ollama" or "vllm"
|
||||
OLLAMA_URL = "http://turd.hem.holck.se:11434"
|
||||
OLLAMA_URL = "http://localhost:11434"
|
||||
VLLM_URL = "http://localhost:8000"
|
||||
MODEL_NAME = "gemma4:e4b"
|
||||
|
||||
|
||||
@@ -106,9 +106,10 @@ def run(args: argparse.Namespace):
|
||||
llm = LLM(backend=args.backend, model=args.model)
|
||||
|
||||
# Start web dashboard if requested
|
||||
_web_server = None
|
||||
if args.web:
|
||||
from .web import start_web_server
|
||||
start_web_server(args.web, workdir)
|
||||
_web_server = start_web_server(args.web, workdir)
|
||||
print(f" \033[36m🌐 Web dashboard: http://localhost:{args.web}\033[0m")
|
||||
|
||||
logger.log("startup", f"AutoDev started in {workdir}")
|
||||
@@ -352,6 +353,15 @@ def run(args: argparse.Namespace):
|
||||
print(f" Plan: see {config.PLAN_FILE}")
|
||||
logger.log("complete", "All steps executed successfully")
|
||||
|
||||
# Shut down web server
|
||||
if _web_server:
|
||||
try:
|
||||
from .web import stop_web_server
|
||||
stop_web_server()
|
||||
_web_server.server_close()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def main():
|
||||
args = parse_args()
|
||||
@@ -359,18 +369,14 @@ def main():
|
||||
run(args)
|
||||
except KeyboardInterrupt:
|
||||
print("\n\n Interrupted by user. State saved — restart to resume.")
|
||||
sys.exit(130)
|
||||
except SandboxViolation as e:
|
||||
print(f"\n \033[31m✗ SANDBOX VIOLATION: {e}\033[0m")
|
||||
sys.exit(1)
|
||||
except LLMError as e:
|
||||
print(f"\n \033[31m✗ LLM ERROR: {e}\033[0m")
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(f"\n \033[31m✗ UNEXPECTED ERROR: {e}\033[0m")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -12,17 +12,25 @@ import threading
|
||||
import urllib.parse
|
||||
from . import config
|
||||
|
||||
# Global event queue — logger pushes events, SSE endpoint streams them
|
||||
_event_queue: queue.Queue = queue.Queue(maxsize=5000)
|
||||
# Global event system — broadcast to all connected SSE clients
|
||||
_clients: list = [] # list of queue.Queue, one per connected client
|
||||
_clients_lock = threading.Lock()
|
||||
_event_history: list = [] # buffer of all past events for new clients
|
||||
_workdir: str = ""
|
||||
_shutdown: bool = False
|
||||
_server = None
|
||||
|
||||
|
||||
def push_event(event_type: str, data: dict):
|
||||
"""Push an event to all connected web clients."""
|
||||
try:
|
||||
_event_queue.put_nowait({"type": event_type, "data": data})
|
||||
except queue.Full:
|
||||
pass
|
||||
msg = {"type": event_type, "data": data}
|
||||
_event_history.append(msg)
|
||||
with _clients_lock:
|
||||
for q in _clients:
|
||||
try:
|
||||
q.put_nowait(msg)
|
||||
except queue.Full:
|
||||
pass
|
||||
|
||||
|
||||
class WebHandler(http.server.BaseHTTPRequestHandler):
|
||||
@@ -62,19 +70,28 @@ class WebHandler(http.server.BaseHTTPRequestHandler):
|
||||
self.send_header("Connection", "keep-alive")
|
||||
self.send_header("Access-Control-Allow-Origin", "*")
|
||||
self.end_headers()
|
||||
client_queue = queue.Queue(maxsize=5000)
|
||||
# Replay event history for late-connecting clients
|
||||
for past_event in _event_history:
|
||||
client_queue.put_nowait(past_event)
|
||||
with _clients_lock:
|
||||
_clients.append(client_queue)
|
||||
try:
|
||||
while True:
|
||||
while not _shutdown:
|
||||
try:
|
||||
event = _event_queue.get(timeout=1)
|
||||
event = client_queue.get(timeout=1)
|
||||
line = f"data: {json.dumps(event)}\n\n"
|
||||
self.wfile.write(line.encode("utf-8"))
|
||||
self.wfile.flush()
|
||||
except queue.Empty:
|
||||
# Send keepalive
|
||||
self.wfile.write(b": keepalive\n\n")
|
||||
self.wfile.flush()
|
||||
except (BrokenPipeError, ConnectionResetError, OSError):
|
||||
pass
|
||||
finally:
|
||||
with _clients_lock:
|
||||
if client_queue in _clients:
|
||||
_clients.remove(client_queue)
|
||||
|
||||
def _serve_file_tree(self):
|
||||
files = []
|
||||
@@ -115,14 +132,42 @@ class WebHandler(http.server.BaseHTTPRequestHandler):
|
||||
self.send_error(404, "File not found or not readable")
|
||||
|
||||
|
||||
def stop_web_server():
|
||||
global _shutdown
|
||||
_shutdown = True
|
||||
if _server:
|
||||
threading.Thread(target=_server.shutdown, daemon=True).start()
|
||||
|
||||
|
||||
def start_web_server(port: int, workdir: str):
|
||||
"""Start the web UI server in a background thread."""
|
||||
"""Start the web UI server in a background daemon thread."""
|
||||
global _workdir
|
||||
_workdir = workdir
|
||||
server = http.server.HTTPServer(("0.0.0.0", port), WebHandler)
|
||||
server.daemon_threads = True
|
||||
thread = threading.Thread(target=server.serve_forever, daemon=True)
|
||||
thread.start()
|
||||
|
||||
class ThreadedHTTPServer(http.server.HTTPServer):
|
||||
allow_reuse_address = True
|
||||
daemon_threads = True
|
||||
|
||||
def process_request(self, request, client_address):
|
||||
t = threading.Thread(target=self._handle, args=(request, client_address),
|
||||
daemon=True)
|
||||
t.start()
|
||||
|
||||
def _handle(self, request, client_address):
|
||||
try:
|
||||
self.finish_request(request, client_address)
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
self.shutdown_request(request)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
server = ThreadedHTTPServer(("0.0.0.0", port), WebHandler)
|
||||
global _server
|
||||
_server = server
|
||||
t = threading.Thread(target=server.serve_forever, daemon=True)
|
||||
t.start()
|
||||
return server
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user