From dd2f5c83ce49146f5927d77d6bf436e598b94c10 Mon Sep 17 00:00:00 2001 From: Amr Gharbeia Date: Sun, 19 Apr 2026 15:36:16 -0400 Subject: [PATCH] fix(tui): Add Keyword Cleaner to ensure reliable protocol matching --- literate/tui-client.org | 42 +++++++++++++++++++------------ src/tui-client.lisp | 42 +++++++++++++++++++------------ verify_response_v2.py | 56 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 108 insertions(+), 32 deletions(-) create mode 100644 verify_response_v2.py diff --git a/literate/tui-client.org b/literate/tui-client.org index 0d2c8a7..99483b6 100644 --- a/literate/tui-client.org +++ b/literate/tui-client.org @@ -38,26 +38,36 @@ The OpenCortex TUI Client is a standalone Common Lisp application built on **Cro (setf *incoming-msgs* nil) msgs))) +(defun clean-keywords (msg) + (if (listp msg) + (let ((clean nil)) + (loop for (k v) on msg by #'cddr + do (push (intern (string k) :keyword) clean) + (push v clean)) + (nreverse clean)) + msg)) + (defun listen-thread () (loop while *is-running* do (handler-case (when (and *stream* (open-stream-p *stream*)) - (let ((msg (opencortex:read-framed-message *stream*))) - (cond ((eq msg :eof) (setf *is-running* nil)) - ((eq msg :error) (setf *status-text* "Protocol Error")) - ((and (listp msg) (eq (getf msg :TYPE) :EVENT)) - (let ((payload (getf msg :PAYLOAD))) - (when (eq (getf payload :ACTION) :handshake) - (setf *status-text* "Ready")))) - ((and (listp msg) (eq (getf msg :TYPE) :STATUS)) - (setf *status-text* (format nil "[Scribe: ~a] [Gardener: ~a]" - (getf msg :SCRIBE) - (getf msg :GARDENER)))) - ((and (listp msg) (eq (getf msg :TYPE) :CHAT)) - (enqueue-msg (getf msg :TEXT))) - (t (enqueue-msg (format nil "~s" msg)))))) - (error (c) (setf *status-text* (format nil "Net Error: ~a" c)) (setf *is-running* nil))) - (sleep 0.05))) + (let ((raw-msg (opencortex:read-framed-message *stream*))) + (unless (member raw-msg '(:eof :error)) + (let ((msg (clean-keywords raw-msg))) + (cond ((and (listp msg) (eq (getf msg :TYPE) :EVENT)) + (let ((payload (getf msg :PAYLOAD))) + (when (eq (getf payload :ACTION) :handshake) + (setf *status-text* "Ready")))) + ((and (listp msg) (eq (getf msg :TYPE) :STATUS)) + (setf *status-text* (format nil "[Scribe: ~a] [Gardener: ~a]" + (getf msg :SCRIBE) + (getf msg :GARDENER)))) + ((and (listp msg) (eq (getf msg :TYPE) :CHAT)) + (enqueue-msg (getf msg :TEXT))) + (t (enqueue-msg (format nil "~s" msg)))))) + (when (eq raw-msg :eof) (setf *is-running* nil)) + (when (eq raw-msg :error) (setf *status-text* "Protocol Error")))) + (defun main () (handler-case diff --git a/src/tui-client.lisp b/src/tui-client.lisp index 37b2095..b67a0ee 100644 --- a/src/tui-client.lisp +++ b/src/tui-client.lisp @@ -25,26 +25,36 @@ (setf *incoming-msgs* nil) msgs))) +(defun clean-keywords (msg) + (if (listp msg) + (let ((clean nil)) + (loop for (k v) on msg by #'cddr + do (push (intern (string k) :keyword) clean) + (push v clean)) + (nreverse clean)) + msg)) + (defun listen-thread () (loop while *is-running* do (handler-case (when (and *stream* (open-stream-p *stream*)) - (let ((msg (opencortex:read-framed-message *stream*))) - (cond ((eq msg :eof) (setf *is-running* nil)) - ((eq msg :error) (setf *status-text* "Protocol Error")) - ((and (listp msg) (eq (getf msg :TYPE) :EVENT)) - (let ((payload (getf msg :PAYLOAD))) - (when (eq (getf payload :ACTION) :handshake) - (setf *status-text* "Ready")))) - ((and (listp msg) (eq (getf msg :TYPE) :STATUS)) - (setf *status-text* (format nil "[Scribe: ~a] [Gardener: ~a]" - (getf msg :SCRIBE) - (getf msg :GARDENER)))) - ((and (listp msg) (eq (getf msg :TYPE) :CHAT)) - (enqueue-msg (getf msg :TEXT))) - (t (enqueue-msg (format nil "~s" msg)))))) - (error (c) (setf *status-text* (format nil "Net Error: ~a" c)) (setf *is-running* nil))) - (sleep 0.05))) + (let ((raw-msg (opencortex:read-framed-message *stream*))) + (unless (member raw-msg '(:eof :error)) + (let ((msg (clean-keywords raw-msg))) + (cond ((and (listp msg) (eq (getf msg :TYPE) :EVENT)) + (let ((payload (getf msg :PAYLOAD))) + (when (eq (getf payload :ACTION) :handshake) + (setf *status-text* "Ready")))) + ((and (listp msg) (eq (getf msg :TYPE) :STATUS)) + (setf *status-text* (format nil "[Scribe: ~a] [Gardener: ~a]" + (getf msg :SCRIBE) + (getf msg :GARDENER)))) + ((and (listp msg) (eq (getf msg :TYPE) :CHAT)) + (enqueue-msg (getf msg :TEXT))) + (t (enqueue-msg (format nil "~s" msg)))))) + (when (eq raw-msg :eof) (setf *is-running* nil)) + (when (eq raw-msg :error) (setf *status-text* "Protocol Error")))) + (defun main () (handler-case diff --git a/verify_response_v2.py b/verify_response_v2.py new file mode 100644 index 0000000..14f2ae7 --- /dev/null +++ b/verify_response_v2.py @@ -0,0 +1,56 @@ +import socket, time, sys + +def verify(): + try: + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.settimeout(20) + s.connect(("localhost", 9105)) + + # 1. Read everything until initial status + full_data = b"" + while b":STATUS" not in full_data and b":status" not in full_data: + chunk = s.recv(4096) + if not chunk: break + full_data += chunk + + print(f"Initial stream: {full_data.decode()}") + + # 2. Send "Hi" + payload = '(:TYPE :EVENT :PAYLOAD (:SENSOR :CHAT-MESSAGE :TEXT "Hi"))' + msg = f"{len(payload):06x}{payload}".encode() + print(f"Sending: {msg.decode()}") + s.sendall(msg) + + # 3. Read response + responses = [] + start_time = time.time() + while time.time() - start_time < 15: + try: + chunk = s.recv(4096).decode() + if not chunk: break + print(f"Received chunk: {chunk}") + responses.append(chunk) + if ":CHAT" in chunk: + print("Found reasoning response!") + except socket.timeout: + break + + s.close() + + # Assertions + all_text = "".join(responses) + if ":status" in all_text or ":status" in full_data.decode(): + print("FAILURE: Found lowercase :status!") + else: + print("SUCCESS: Keywords are normalized to uppercase.") + + if ":CHAT" in all_text: + print("SUCCESS: Full response loop closed.") + else: + print("FAILURE: No chat response received.") + + except Exception as e: + print(f"Error: {e}") + sys.exit(1) + +verify()