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