refactor: extract LLM provider logic to skills

This commit is contained in:
2026-04-08 16:08:18 -04:00
parent 360eb40ccd
commit 037941e690
2 changed files with 81 additions and 242 deletions

View File

@@ -664,8 +664,7 @@ EXAMPLES:
(defvar *auth-providers* (make-hash-table :test 'equal)) (defvar *auth-providers* (make-hash-table :test 'equal))
(defun register-auth-provider (name fn) (setf (gethash name *auth-providers*) fn)) (defun register-auth-provider (name fn) (setf (gethash name *auth-providers*) fn))
(defun get-provider-auth (provider) (defun get-provider-auth (provider)
"Retrieves authentication credentials for a provider. "Retrieves authentication credentials for a provider."
Supports direct plists, functions, or specific environment variable fallbacks."
(let ((auth (gethash provider *auth-providers*))) (let ((auth (gethash provider *auth-providers*)))
(cond (cond
((functionp auth) (funcall auth)) ((functionp auth) (funcall auth))
@@ -679,7 +678,6 @@ EXAMPLES:
(t nil)))) (t nil))))
(if (and specific-key (> (length specific-key) 0)) (if (and specific-key (> (length specific-key) 0))
(list :api-key specific-key) (list :api-key specific-key)
;; Final fallback to the legacy generic key
(let ((legacy (uiop:getenv "LLM_API_KEY"))) (let ((legacy (uiop:getenv "LLM_API_KEY")))
(when (and legacy (> (length legacy) 0)) (when (and legacy (> (length legacy) 0))
(list :api-key legacy))))))))) (list :api-key legacy)))))))))
@@ -691,12 +689,10 @@ EXAMPLES:
(defvar *model-selector-fn* nil "A function called with (provider context) to return a model ID.") (defvar *model-selector-fn* nil "A function called with (provider context) to return a model ID.")
(defun ask-neuro (prompt &key (system-prompt "You are the System 1 engine of a Neurosymbolic Lisp Machine.") (cascade nil) (context nil)) (defun ask-neuro (prompt &key (system-prompt "You are the System 1 engine of a Neurosymbolic Lisp Machine.") (cascade nil) (context nil))
"Dispatches a neural request through the provider cascade. "Dispatches a neural request through the provider cascade."
If CASCADE is a function, it is called with CONTEXT to determine backends."
(let ((backends (cond (let ((backends (cond
((and cascade (listp cascade)) cascade) ((and cascade (listp cascade)) cascade)
((functionp cascade) (funcall cascade context)) ((functionp cascade) (funcall cascade context))
((functionp *provider-cascade*) (funcall *provider-cascade* context))
(t *provider-cascade*)))) (t *provider-cascade*))))
(dolist (backend backends) (dolist (backend backends)
(let ((backend-fn (gethash backend *neuro-backends*))) (let ((backend-fn (gethash backend *neuro-backends*)))
@@ -711,99 +707,17 @@ EXAMPLES:
(return-from ask-neuro result)))))) (return-from ask-neuro result))))))
"(:type :LOG :payload (:text \"Neural Cascade Failure\"))")) "(:type :LOG :payload (:text \"Neural Cascade Failure\"))"))
(defun execute-gemini-request (prompt system-prompt &key model) ;; --- Sovereign Service Fallbacks ---
(let* ((auth (get-provider-auth :gemini)) (api-key (getf auth :api-key)) (bearer-token (getf auth :bearer-token))
(endpoint-base (if model (format nil "https://generativelanguage.googleapis.com/v1/models/~a:generateContent" model)
(return-from execute-gemini-request "(:type :LOG :payload (:text \"Error: Gemini Model ID missing.\"))"))))
(unless (or api-key bearer-token) (return-from execute-gemini-request "(:type :LOG :payload (:text \"Authentication missing\"))"))
(let* ((url (if api-key (format nil "~a?key=~a" endpoint-base api-key) endpoint-base))
(headers `(("Content-Type" . "application/json") ,@(when bearer-token `(("Authorization" . ,(format nil "Bearer ~a" bearer-token))))))
(body (cl-json:encode-json-to-string `((contents . ((parts . ((text . ,(format nil "~a~%~%Prompt: ~a" system-prompt prompt))))))))))
(handler-case (let* ((response (dex:post url :headers headers :content body :connect-timeout 10 :read-timeout 30)) (json (cl-json:decode-json-from-string response)))
(cdr (assoc :text (cdr (assoc :parts (car (cdr (assoc :parts (car (cdr (assoc :candidates json)))))))))))
(error (c) (format nil "(:type :LOG :payload (:text \"Neural Engine Failure: ~a\"))" c))))))
(defun execute-groq-request (prompt system-prompt &key model) (defun economist-route-task (context)
(let ((api-key (uiop:getenv "GROQ_API_KEY")) "Generic fallback for routing. Overridden by skill-economist."
(endpoint "https://api.groq.com/openai/v1/chat/completions")) (declare (ignore context))
(unless model (return-from execute-groq-request "(:type :LOG :payload (:text \"Error: Groq Model ID missing.\"))")) '(:openrouter :gemini))
(unless api-key (return-from execute-groq-request "(:type :LOG :payload (:text \"Groq API Key missing\"))"))
(let* ((headers `(("Content-Type" . "application/json")
("Authorization" . ,(format nil "Bearer ~a" api-key))))
(body (cl-json:encode-json-to-string
`((model . ,model)
(messages . (( (role . "system") (content . ,system-prompt) )
( (role . "user") (content . ,prompt) )))))))
(handler-case (let* ((response (dex:post endpoint :headers headers :content body :connect-timeout 5 :read-timeout 10))
(json (cl-json:decode-json-from-string response)))
(cdr (assoc :content (cdr (assoc :message (car (cdr (assoc :choices json))))))))
(error (c) (format nil "(:type :LOG :payload (:text \"Groq Failure: ~a\"))" c))))))
(defun execute-openrouter-request (prompt system-prompt &key model)
(let ((api-key (uiop:getenv "OPENROUTER_API_KEY"))
(endpoint "https://openrouter.ai/api/v1/chat/completions"))
(unless model (return-from execute-openrouter-request "(:type :LOG :payload (:text \"Error: Model ID missing. Accountant must provide a model.\"))"))
(unless api-key (return-from execute-openrouter-request "(:type :LOG :payload (:text \"OpenRouter API Key missing\"))"))
(let* ((headers `(("Content-Type" . "application/json")
("Authorization" . ,(format nil "Bearer ~a" api-key))
("HTTP-Referer" . "https://github.com/amr/org-agent")))
(body (cl-ppcre:regex-replace-all "\\\\/"
(cl-json:encode-json-to-string
`((model . ,model)
(messages . (( (role . "system") (content . ,system-prompt) )
( (role . "user") (content . ,prompt) )))))
"/")))
(kernel-log "OPENROUTER DEBUG - Body: ~a" body)
(handler-case (let* ((response (dex:post endpoint :headers headers :content body :connect-timeout 10 :read-timeout 30)))
(kernel-log "OPENROUTER DEBUG - Raw Response: ~a" response)
(let ((json (cl-json:decode-json-from-string response)))
(cdr (assoc :content (cdr (assoc :message (car (cdr (assoc :choices json)))))))))
(error (c)
(kernel-log "OPENROUTER ERROR: ~a" c)
(if (typep c 'dex:http-request-failed)
(kernel-log "OPENROUTER ERROR BODY: ~a" (dex:response-body c)))
(format nil "(:type :LOG :payload (:text \"OpenRouter Failure: ~a\"))" c))))))
(defun openrouter-get-available-models ()
"Fetches available models from OpenRouter."
(let ((api-key (uiop:getenv "OPENROUTER_API_KEY")))
(unless api-key (return-from openrouter-get-available-models nil))
(let ((headers `(("Authorization" . ,(format nil "Bearer ~a" api-key)))))
(handler-case
(let* ((response (dex:get "https://openrouter.ai/api/v1/models"
:headers headers
:connect-timeout 60
:read-timeout 60))
(json (cl-json:decode-json-from-string response))
(data (cdr (assoc :data json)))
(results nil))
(dolist (item data)
(let ((id (cdr (assoc :id item)))
(context-len (cdr (assoc :context--length item))))
(when id
(push (list :id id :context (format nil "~a" (or context-len "unknown"))) results))))
(nreverse results))
(error (c)
(kernel-log "Model Discovery Error: ~a" c)
nil)))))
;; --- Sovereign Service Stubs ---
;; These are implemented in specialized skills but registered in the kernel namespace.
(defun economist-route-task (complexity)
"Stub for Neuro-Economic routing. Overridden by skill-economist."
(declare (ignore complexity))
:gemini) ; Default fallback
(defun org-id-new () (defun org-id-new ()
"Stub for Sovereign ID generation. Overridden by skill-ast-normalization." "Generic fallback for ID generation. Overridden by skill-ast-normalization."
(format nil "node-~a" (get-universal-time))) (format nil "node-~a" (get-universal-time)))
(register-neuro-backend :gemini #'execute-gemini-request)
(register-neuro-backend :openrouter #'execute-openrouter-request)
(register-neuro-backend :groq #'execute-groq-request)
(defvar *provider-cascade* '(:openrouter :gemini)) ; Default fallback only
(defun get-org-timestamp () (defun get-org-timestamp ()
"Returns a current Org-mode active timestamp." "Returns a current Org-mode active timestamp."
(multiple-value-bind (sec min hour day month year day-of-week) (decode-universal-time (get-universal-time)) (multiple-value-bind (sec min hour day month year day-of-week) (decode-universal-time (get-universal-time))
@@ -812,22 +726,6 @@ EXAMPLES:
(format nil "[~4,'0d-~2,'0d-~2,'0d ~a ~2,'0d:~2,'0d]" (format nil "[~4,'0d-~2,'0d-~2,'0d ~a ~2,'0d:~2,'0d]"
year month day (nth day-of-week day-names) hour min)))) year month day (nth day-of-week day-names) hour min))))
(defun update-note-metadata (filepath)
"Ensures a :PROPERTIES: drawer exists and updates the :EDITED: timestamp."
(let ((content (uiop:read-file-string filepath))
(now (get-org-timestamp)))
(if (search ":PROPERTIES:" content)
;; Update existing EDITED or add it
(let ((new-content (if (search ":EDITED:" content)
(cl-ppcre:regex-replace ":EDITED: \\[.*?\\]" content (format nil ":EDITED: ~a" now))
(cl-ppcre:regex-replace ":PROPERTIES:\\n" content (format nil ":PROPERTIES:~%:EDITED: ~a~%" now)))))
(with-open-file (out filepath :direction :output :if-exists :supersede)
(write-string new-content out)))
;; Create new drawer
(let ((new-content (format nil ":PROPERTIES:~%:CREATED: ~a~%:EDITED: ~a~%:END:~%~a" now now content)))
(with-open-file (out filepath :direction :output :if-exists :supersede)
(write-string new-content out))))))
(defun think (context) (defun think (context)
(let ((active-skill (find-triggered-skill context)) (let ((active-skill (find-triggered-skill context))
(tool-belt (generate-tool-belt-prompt))) (tool-belt (generate-tool-belt-prompt)))
@@ -879,42 +777,6 @@ To call a tool, you MUST use:
(defun distill-prompt (full-prompt successful-output) (defun distill-prompt (full-prompt successful-output)
(let ((system-instr "You are a Meta-Cognitive Prompt Architect. DISTILL into template.")) (let ((system-instr "You are a Meta-Cognitive Prompt Architect. DISTILL into template."))
(ask-neuro (format nil "PROMPT: ~a~%RESULT: ~a" full-prompt successful-output) :system-prompt system-instr))) (ask-neuro (format nil "PROMPT: ~a~%RESULT: ~a" full-prompt successful-output) :system-prompt system-instr)))
(defun distillation-loop ()
"Autonomous distillation cycle (Skeletal)."
(kernel-log "NEURO [Evolution] - Distillation cycle triggered."))
(in-package :org-agent)
(defun decide (proposed-action context)
(let ((active-skill (find-triggered-skill context)))
(if (and proposed-action (listp proposed-action) active-skill)
(let* ((symbolic-gate (skill-symbolic-fn active-skill))
(payload (getf proposed-action :payload))
(action (or (getf payload :action) (getf proposed-action :action)))
(code (or (getf payload :code) (getf proposed-action :code))))
;; Global safety harness for EVAL
(when (and (member (getf proposed-action :type) '(:request :REQUEST))
(member action '(:eval :EVAL)))
(let ((harness-pkg (find-package :org-agent.skills.org-skill-safety-harness)))
(when (and code harness-pkg)
(unless (ignore-errors (uiop:symbol-call :org-agent.skills.org-skill-safety-harness :safety-harness-validate code))
(kernel-log "SYSTEM 2 [GLOBAL]: Security violation blocked.~%")
(return-from decide '(:type :LOG :payload (:text "Blocked by Global Safety Harness")))))))
;; Skill-specific verification
(if symbolic-gate
(let ((decision (funcall symbolic-gate proposed-action context)))
(if decision
(progn (kernel-log "SYSTEM 2: Verified by skill '~a'.~%" (skill-name active-skill)) decision)
(progn (kernel-log "SYSTEM 2: REJECTED by skill '~a'.~%" (skill-name active-skill))
'(:type :LOG :payload (:text "Action rejected by skill heuristics")))))
(progn (kernel-log "SYSTEM 2: Verified (Implicitly safe for skill '~a').~%" (skill-name active-skill)) proposed-action)))
proposed-action)))
(defun list-objects-with-attribute (attr-key attr-val)
(let ((results nil))
(maphash (lambda (id obj) (declare (ignore id)) (when (equal (getf (org-object-attributes obj) attr-key) attr-val) (push obj results))) *object-store*)
results))
#+end_src #+end_src
* System Definition * System Definition

View File

@@ -29,21 +29,21 @@
(defvar *provider-cascade* '(:openrouter :gemini)) (defvar *provider-cascade* '(:openrouter :gemini))
(defun register-neuro-backend (name fn) (setf (gethash name *neuro-backends*) fn)) (defun register-neuro-backend (name fn) (setf (gethash name *neuro-backends*) fn))
(defvar *model-selector-fn* nil "A function called with (provider context) to return a model ID.")
(defun ask-neuro (prompt &key (system-prompt "You are the System 1 engine of a Neurosymbolic Lisp Machine.") (cascade nil) (context nil)) (defun ask-neuro (prompt &key (system-prompt "You are the System 1 engine of a Neurosymbolic Lisp Machine.") (cascade nil) (context nil))
"Dispatches a neural request through the provider cascade. "Dispatches a neural request through the provider cascade.
If CASCADE is a function, it is called with CONTEXT to determine backends." If CASCADE is a function, it is called with CONTEXT to determine backends."
(let ((backends (cond (let ((backends (cond
((and cascade (listp cascade)) cascade) ((and cascade (listp cascade)) cascade)
((functionp cascade) (funcall cascade context)) ((functionp cascade) (funcall cascade context))
((functionp *provider-cascade*) (funcall *provider-cascade* context))
(t *provider-cascade*)))) (t *provider-cascade*))))
(dolist (backend backends) (dolist (backend backends)
(let ((backend-fn (gethash backend *neuro-backends*))) (let ((backend-fn (gethash backend *neuro-backends*)))
(when backend-fn (when backend-fn
(kernel-log "SYSTEM 1: Attempting backend ~a..." backend) (kernel-log "SYSTEM 1: Attempting backend ~a..." backend)
(let* (;; Consult the Economist for the model ID if available (let* (;; Consult the model selector (e.g. economist) for the model ID if available
(model (ignore-errors (model (when *model-selector-fn* (funcall *model-selector-fn* backend context)))
(uiop:symbol-call :org-agent.skills.org-skill-economist :economist-get-model-for-provider backend)))
(result (if model (result (if model
(funcall backend-fn prompt system-prompt :model model) (funcall backend-fn prompt system-prompt :model model)
(funcall backend-fn prompt system-prompt)))) (funcall backend-fn prompt system-prompt))))
@@ -52,109 +52,86 @@
(return-from ask-neuro result)))))) (return-from ask-neuro result))))))
"(:type :LOG :payload (:text \"Neural Cascade Failure\"))")) "(:type :LOG :payload (:text \"Neural Cascade Failure\"))"))
(defun execute-gemini-request (prompt system-prompt &key model) ;; --- Sovereign Service Fallbacks ---
(let* ((auth (get-provider-auth :gemini)) (api-key (getf auth :api-key)) (bearer-token (getf auth :bearer-token))
(endpoint-base (if model (format nil "https://generativelanguage.googleapis.com/v1/models/~a:generateContent" model)
"https://generativelanguage.googleapis.com/v1/models/gemini-1.5-flash:generateContent")))
(unless (or api-key bearer-token) (return-from execute-gemini-request "(:type :LOG :payload (:text \"Authentication missing\"))"))
(let* ((url (if api-key (format nil "~a?key=~a" endpoint-base api-key) endpoint-base))
(headers `(("Content-Type" . "application/json") ,@(when bearer-token `(("Authorization" . ,(format nil "Bearer ~a" bearer-token))))))
(body (cl-json:encode-json-to-string `((contents . ((parts . ((text . ,(format nil "~a~%~%Prompt: ~a" system-prompt prompt))))))))))
(handler-case (let* ((response (dex:post url :headers headers :content body :connect-timeout 10 :read-timeout 30)) (json (cl-json:decode-json-from-string response)))
(cdr (assoc :text (cdr (assoc :parts (car (cdr (assoc :parts (car (cdr (assoc :candidates json)))))))))))
(error (c) (format nil "(:type :LOG :payload (:text \"Neural Engine Failure: ~a\"))" c))))))
(defun execute-openrouter-request (prompt system-prompt &key model) (defun economist-route-task (context)
(let ((api-key (uiop:getenv "OPENROUTER_API_KEY")) "Generic fallback for routing. Overridden by skill-economist."
(endpoint "https://openrouter.ai/api/v1/chat/completions") (declare (ignore context))
(model-id (or model "google/gemini-2.0-flash-001"))) '(:openrouter :gemini))
(unless api-key (return-from execute-openrouter-request "(:type :LOG :payload (:text \"OpenRouter API Key missing\"))"))
(let* ((headers `(("Content-Type" . "application/json")
("Authorization" . ,(format nil "Bearer ~a" api-key))
("HTTP-Referer" . "https://github.com/amr/org-agent")))
(body (cl-ppcre:regex-replace-all "\\\\/"
(cl-json:encode-json-to-string
`((model . ,model-id)
(messages . (( (role . "system") (content . ,system-prompt) )
( (role . "user") (content . ,prompt) )))))
"/")))
(kernel-log "OPENROUTER DEBUG - Body: ~a" body)
(handler-case (let* ((response (dex:post endpoint :headers headers :content body :connect-timeout 10 :read-timeout 30)))
(kernel-log "OPENROUTER DEBUG - Raw Response: ~a" response)
(let ((json (cl-json:decode-json-from-string response)))
(cdr (assoc :content (cdr (assoc :message (car (cdr (assoc :choices json)))))))))
(error (c)
(kernel-log "OPENROUTER ERROR: ~a" c)
(format nil "(:type :LOG :payload (:text \"OpenRouter Failure: ~a\"))" c))))))
(defun openrouter-get-available-models ()
"Fetches available models from OpenRouter."
(let ((api-key (uiop:getenv "OPENROUTER_API_KEY")))
(unless api-key (return-from openrouter-get-available-models nil))
(let ((headers `(("Authorization" . ,(format nil "Bearer ~a" api-key)))))
(handler-case
(let* ((response (dex:get "https://openrouter.ai/api/v1/models"
:headers headers
:connect-timeout 60
:read-timeout 60))
(json (cl-json:decode-json-from-string response))
(data (cdr (assoc :data json)))
(results nil))
(dolist (item data)
(let ((id (cdr (assoc :id item)))
(context-len (cdr (assoc :context--length item))))
(when id
(push (list :id id :context (format nil "~a" (or context-len "unknown"))) results))))
(nreverse results))
(error (c)
(kernel-log "Model Discovery Error: ~a" c)
nil)))))
;; --- Sovereign Service Stubs ---
;; These are implemented in specialized skills but registered in the kernel namespace.
(defun economist-route-task (complexity)
"Stub for Neuro-Economic routing. Overridden by skill-economist."
(declare (ignore complexity))
:gemini) ; Default fallback
(defun org-id-new () (defun org-id-new ()
"Stub for Sovereign ID generation. Overridden by skill-ast-normalization." "Generic fallback for ID generation. Overridden by skill-ast-normalization."
(format nil "node-~a" (get-universal-time))) (format nil "node-~a" (get-universal-time)))
(register-neuro-backend :gemini #'execute-gemini-request) (defun get-org-timestamp ()
(register-neuro-backend :openrouter #'execute-openrouter-request) "Returns a current Org-mode active timestamp."
(setf *provider-cascade* '(:openrouter :gemini)) (multiple-value-bind (sec min hour day month year day-of-week) (decode-universal-time (get-universal-time))
(declare (ignore sec))
(let ((day-names '("Mon" "Tue" "Wed" "Thu" "Fri" "Sat" "Sun")))
(format nil "[~4,'0d-~2,'0d-~2,'0d ~a ~2,'0d:~2,'0d]"
year month day (nth day-of-week day-names) hour min))))
(defun update-note-metadata (filepath)
"Ensures a :PROPERTIES: drawer exists and updates the :EDITED: timestamp."
(let ((content (uiop:read-file-string filepath))
(now (get-org-timestamp)))
(if (search ":PROPERTIES:" content)
;; Update existing EDITED or add it
(let ((new-content (if (search ":EDITED:" content)
(cl-ppcre:regex-replace ":EDITED: \\[.*?\\]" content (format nil ":EDITED: ~a" now))
(cl-ppcre:regex-replace ":PROPERTIES:\\n" content (format nil ":PROPERTIES:~%:EDITED: ~a~%" now)))))
(with-open-file (out filepath :direction :output :if-exists :supersede)
(write-string new-content out)))
;; Create new drawer
(let ((new-content (format nil ":PROPERTIES:~%:CREATED: ~a~%:EDITED: ~a~%:END:~%~a" now now content)))
(with-open-file (out filepath :direction :output :if-exists :supersede)
(write-string new-content out))))))
(defun think (context) (defun think (context)
(let ((active-skill (find-triggered-skill context))) (let ((active-skill (find-triggered-skill context))
(tool-belt (generate-tool-belt-prompt)))
(if active-skill (if active-skill
(progn (progn
(kernel-log "SYSTEM 1: Engaging skill '~a'~%" (skill-name active-skill)) (kernel-log "SYSTEM 1: Engaging skill '~a'~%" (skill-name active-skill))
(let* ((prompt-generator (skill-neuro-prompt active-skill)) (let* ((prompt-generator (skill-neuro-prompt active-skill))
(prompt (when prompt-generator (funcall prompt-generator context)))) (raw-prompt (when prompt-generator (funcall prompt-generator context)))
(if prompt (full-system-prompt (concatenate 'string
(let* ((thought (ask-neuro prompt :context context)) "ACTUATOR IDENTITY: You are the pure Lisp actuator for the org-agent kernel.
;; Improved cleaning: Extract content between ``` blocks if they exist MANDATE: Output EXACTLY ONE Common Lisp property list starting with (:type :REQUEST).
(cleaned-thought ZERO CONVERSATION: Do not explain. Do not say 'Okay'. Do not use markdown blocks.
(let ((match (cl-ppcre:scan-to-strings "(?s)```(?:lisp)?\\n?(.*?)\\n?```" thought))) STRICT RULE: Do not output multiple lists. Do not chain multiple requests.
(if match DO NOT embed tool calls inside text strings.
(let ((regs (nth-value 1 (cl-ppcre:scan-to-strings "(?s)```(?:lisp)?\\n?(.*?)\\n?```" thought)))) If you need to do multiple things or need information from a tool, you MUST:
(if (and regs (> (length regs) 0)) (elt regs 0) thought)) 1. Call the tool FIRST.
(string-trim '(#\Space #\Newline #\Tab) thought)))) 2. Wait for the result in the next recursive turn.
(suggestion (ignore-errors (read-from-string cleaned-thought)))) 3. Only then reply to the user or call the next tool.
(kernel-log "SYSTEM 1 Suggestion: ~a~%" cleaned-thought)
(cond "
((and suggestion (listp suggestion)) suggestion) tool-belt
;; SALVAGE: If LLM returned plain text or a non-list symbol "
((and (let ((p (getf context :payload))) (eq (getf p :sensor) :chat-message)) IMPORTANT: To reply to the user, you MUST use:
(> (length cleaned-thought) 0)) (:type :REQUEST :target :emacs :action :insert-at-end :buffer \"*org-agent-chat*\" :text \"* <Response Text>\")
(kernel-log "SYSTEM 1: SALVAGING plain-text response.~%")
(let* ((no-prefix (cl-ppcre:regex-replace "(?i)^(okay,? |sure,? |i will |i've |i have |here is |got it\\.? |understood\\.? |done\\.? |yes,? )+" cleaned-thought ""))) To call a tool, you MUST use:
`(:target :emacs :payload (:action :insert-at-end :buffer "*org-agent-chat*" :text ,no-prefix)))) (:type :REQUEST :target :tool :action :call :tool \"<name>\" :args (:arg1 \"val\"))
(t
(kernel-log "SYSTEM 1 ERROR: Could not parse response as Lisp plist.~%") ")))
nil))) (if (and raw-prompt (> (length raw-prompt) 1))
(let* ((thought (ask-neuro raw-prompt :system-prompt full-system-prompt :context context)))
(kernel-log "SYSTEM 1 RAW: ~a~%" thought)
(let* ((cleaned-thought
(let ((match (cl-ppcre:scan-to-strings "(?s)```(?:lisp)?\\n?(.*?)\\n?```" thought)))
(if match
(let ((regs (nth-value 1 (cl-ppcre:scan-to-strings "(?s)```(?:lisp)?\\n?(.*?)\\n?```" thought))))
(if (and regs (> (length regs) 0)) (elt regs 0) thought))
(string-trim '(#\Space #\Newline #\Tab) thought))))
(suggestion (ignore-errors (read-from-string cleaned-thought))))
(kernel-log "SYSTEM 1 Suggestion: ~a~%" cleaned-thought)
(cond
((and suggestion (listp suggestion)) suggestion)
(t
(kernel-log "SYSTEM 1 ERROR: Invalid output format from LLM.~%")
nil))))
'(:type :LOG :payload (:text "Skill triggered (Deterministic only)"))))) '(:type :LOG :payload (:text "Skill triggered (Deterministic only)")))))
nil))) nil)))