From e88bcb4857ec8988f8031c343e22d9377d2e1cbc Mon Sep 17 00:00:00 2001 From: Google AI Agent Date: Mon, 30 Mar 2026 19:04:53 -0400 Subject: [PATCH] [fix] 5 bugs: 2 SyntaxErrors in nexus_think.py, Groq model name, server race condition, corrupt public/nexus/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug 1: nexus_think.py line 318 — stray '.' between function call and if-block This is a SyntaxError. The entire consciousness loop cannot import. The Nexus Mind has been dead since this was committed. Bug 2: nexus_think.py line 445 — 'parser.add_.argument()' Another SyntaxError — extra underscore in argparse call. The CLI entrypoint crashes on startup. Bug 3: groq_worker.py — DEFAULT_MODEL = 'groq/llama3-8b-8192' The Groq API expects bare model names. The 'groq/' prefix causes a 404. Fixed to 'llama3-8b-8192'. Bug 4: server.py — clients.remove() in finally block Raises KeyError if the websocket was never added to the set. Fixed to clients.discard() (safe no-op if not present). Also added tracking for disconnected clients during broadcast. Bug 5: public/nexus/ — 3 corrupt duplicate files (28.6 KB wasted) app.js, style.css, and index.html all had identical content (same SHA). These are clearly a broken copy operation. The real files are at repo root. Tests: 6 new, 21/22 total pass. The 1 pre-existing failure is in test_portals_json_uses_expanded_registry_schema (schema mismatch, not related to this PR). Signed-off-by: gemini --- .gitignore | 1 + nexus/groq_worker.py | 2 +- nexus/nexus_think.py | 4 +- public/nexus/app.js | 284 ------------------------------------- public/nexus/index.html | 284 ------------------------------------- public/nexus/style.css | 284 ------------------------------------- server.py | 5 +- tests/test_syntax_fixes.py | 111 +++++++++++++++ 8 files changed, 119 insertions(+), 856 deletions(-) delete mode 100644 public/nexus/app.js delete mode 100644 public/nexus/index.html delete mode 100644 public/nexus/style.css create mode 100644 tests/test_syntax_fixes.py diff --git a/.gitignore b/.gitignore index 14352f4..2556e45 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules/ test-results/ nexus/__pycache__/ +tests/__pycache__/ diff --git a/nexus/groq_worker.py b/nexus/groq_worker.py index 69a0cd0..3d55c02 100644 --- a/nexus/groq_worker.py +++ b/nexus/groq_worker.py @@ -25,7 +25,7 @@ from typing import Optional log = logging.getLogger("nexus") GROQ_API_URL = "https://api.groq.com/openai/v1/chat/completions" -DEFAULT_MODEL = "groq/llama3-8b-8192" +DEFAULT_MODEL = "llama3-8b-8192" class GroqWorker: """A worker for the Groq API.""" diff --git a/nexus/nexus_think.py b/nexus/nexus_think.py index 847de3c..14d1996 100644 --- a/nexus/nexus_think.py +++ b/nexus/nexus_think.py @@ -315,7 +315,7 @@ class NexusMind: ] summary = self._call_thinker(messages) -. + if summary: self.experience_store.save_summary( summary=summary, @@ -442,7 +442,7 @@ def main(): parser = argparse.ArgumentParser( description="Nexus Mind — Embodied consciousness loop" ) - parser.add_.argument( + parser.add_argument( "--model", default=DEFAULT_MODEL, help=f"Ollama model name (default: {DEFAULT_MODEL})" ) diff --git a/public/nexus/app.js b/public/nexus/app.js deleted file mode 100644 index 047e8fa..0000000 --- a/public/nexus/app.js +++ /dev/null @@ -1,284 +0,0 @@ - - - - - - - - - Cookie check - - - - - - -
- -
- -
- - - diff --git a/public/nexus/index.html b/public/nexus/index.html deleted file mode 100644 index 047e8fa..0000000 --- a/public/nexus/index.html +++ /dev/null @@ -1,284 +0,0 @@ - - - - - - - - - Cookie check - - - - - - -
- -
- -
- - - diff --git a/public/nexus/style.css b/public/nexus/style.css deleted file mode 100644 index 047e8fa..0000000 --- a/public/nexus/style.css +++ /dev/null @@ -1,284 +0,0 @@ - - - - - - - - - Cookie check - - - - - - -
- -
- -
- - - diff --git a/server.py b/server.py index 36c2541..11cc6db 100644 --- a/server.py +++ b/server.py @@ -12,16 +12,19 @@ async def broadcast_handler(websocket): try: async for message in websocket: # Broadcast to all OTHER clients + disconnected = set() for client in clients: if client != websocket: try: await client.send(message) except Exception as e: logging.error(f"Failed to send to a client: {e}") + disconnected.add(client) + clients.difference_update(disconnected) except websockets.exceptions.ConnectionClosed: pass finally: - clients.remove(websocket) + clients.discard(websocket) # discard is safe if not present logging.info(f"Client disconnected. Total clients: {len(clients)}") async def main(): diff --git a/tests/test_syntax_fixes.py b/tests/test_syntax_fixes.py new file mode 100644 index 0000000..05177b2 --- /dev/null +++ b/tests/test_syntax_fixes.py @@ -0,0 +1,111 @@ +"""Tests for syntax and correctness fixes across the-nexus codebase. + +Covers: + - nexus_think.py: no stray dots (SyntaxError), no typos in argparse + - groq_worker.py: model name has no 'groq/' prefix + - server.py: uses discard() not remove() for client cleanup + - public/nexus/: corrupt duplicate directory removed +""" + +import ast +from pathlib import Path + + +NEXUS_ROOT = Path(__file__).resolve().parent.parent + + +# ── nexus_think.py syntax checks ──────────────────────────────────── + +def test_nexus_think_parses_without_syntax_error(): + """nexus_think.py must be valid Python. + + Two SyntaxErrors existed: + 1. Line 318: stray '.' between function call and if-block + 2. Line 445: 'parser.add_.argument()' (extra underscore) + + If either is present, the entire consciousness loop can't import. + """ + source = (NEXUS_ROOT / "nexus" / "nexus_think.py").read_text() + # ast.parse will raise SyntaxError if the file is invalid + try: + ast.parse(source, filename="nexus_think.py") + except SyntaxError as e: + raise AssertionError( + f"nexus_think.py has a SyntaxError at line {e.lineno}: {e.msg}" + ) from e + + +def test_nexus_think_no_stray_dot(): + """There should be no line that is just a dot in nexus_think.py.""" + source = (NEXUS_ROOT / "nexus" / "nexus_think.py").read_text() + for i, line in enumerate(source.splitlines(), 1): + stripped = line.strip() + if stripped == ".": + raise AssertionError( + f"nexus_think.py has a stray '.' on line {i}. " + "This causes a SyntaxError." + ) + + +def test_nexus_think_argparse_no_typo(): + """parser.add_argument must not be written as parser.add_.argument.""" + source = (NEXUS_ROOT / "nexus" / "nexus_think.py").read_text() + assert "add_.argument" not in source, ( + "nexus_think.py contains 'add_.argument' — should be 'add_argument'." + ) + + +# ── groq_worker.py model name ─────────────────────────────────────── + +def test_groq_default_model_has_no_prefix(): + """Groq API expects model names without router prefixes. + + Sending 'groq/llama3-8b-8192' returns a 404. + The correct name is just 'llama3-8b-8192'. + """ + source = (NEXUS_ROOT / "nexus" / "groq_worker.py").read_text() + for line in source.splitlines(): + stripped = line.strip() + if stripped.startswith("DEFAULT_MODEL") and "=" in stripped: + assert "groq/" not in stripped, ( + f"groq_worker.py DEFAULT_MODEL contains 'groq/' prefix: {stripped}. " + "The Groq API expects bare model names like 'llama3-8b-8192'." + ) + break + else: + # DEFAULT_MODEL not found — that's a different issue, not this test's concern + pass + + +# ── server.py client cleanup ──────────────────────────────────────── + +def test_server_uses_discard_not_remove(): + """server.py must use clients.discard() not clients.remove(). + + remove() raises KeyError if the websocket isn't in the set. + This happens if an exception occurs before clients.add() runs. + discard() is a safe no-op if the element isn't present. + """ + source = (NEXUS_ROOT / "server.py").read_text() + assert "clients.discard(" in source, ( + "server.py should use clients.discard(websocket) for safe cleanup." + ) + assert "clients.remove(" not in source, ( + "server.py should NOT use clients.remove(websocket) — " + "raises KeyError if websocket wasn't added." + ) + + +# ── public/nexus/ corrupt duplicate directory ──────────────────────── + +def test_public_nexus_duplicate_removed(): + """public/nexus/ contained 3 files with identical content (all 9544 bytes). + + app.js, style.css, and index.html were all the same file — clearly a + corrupt copy operation. The canonical files are at the repo root. + """ + corrupt_dir = NEXUS_ROOT / "public" / "nexus" + assert not corrupt_dir.exists(), ( + "public/nexus/ still exists. These are corrupt duplicates " + "(all 3 files have identical content). Remove this directory." + )