#+PROPERTY: header-args:lisp :tangle (expand-file-name "org-skill-llm-gateway.lisp" (concat (or (getenv "INSTALL_DIR") ".") "/skills")) :PROPERTIES: :ID: llm-gateway-spec :CREATED: [2026-04-10 Thu] :END: #+TITLE: Skill: LLM Gateway #+STARTUP: content #+FILETAGS: :skill:llm:gateway: * Overview The *LLM Gateway* skill provides a unified interface for interacting with multiple Large Language Model providers. * Test Suite #+begin_src lisp :tangle (expand-file-name "llm-gateway-tests.lisp" (concat (or (getenv "INSTALL_DIR") ".") "/tests")) (defpackage :opencortex-llm-gateway-tests (:use :cl :fiveam :opencortex) (:export #:llm-gateway-suite)) (in-package :opencortex-llm-gateway-tests) (def-suite llm-gateway-suite :description "Tests for the LLM Gateway skill") (in-suite llm-gateway-suite) (test test-llm-gateway-timeout "Tier 2 Chaos: Verify that LLM Gateway handles connection failures gracefully." ;; Point to a non-existent port to force a connection error (let ((uiop:*environment* (copy-list uiop:*environment*))) (setf (uiop:getenv "OLLAMA_HOST") "localhost:1") (let ((result (opencortex::execute-llm-request :prompt "hello" :provider :ollama))) (is (eq (getf result :status) :error)) (is (uiop:string-prefix-p "Ollama Failure" (getf result :message)))))) #+end_src * Implementation ** Package Context #+begin_src lisp :tangle (expand-file-name "org-skill-llm-gateway.lisp" (concat (or (getenv "INSTALL_DIR") ".") "/skills")) (in-package :opencortex) #+end_src ** Skill Metadata #+begin_src lisp :tangle (expand-file-name "org-skill-llm-gateway.lisp" (concat (or (getenv "INSTALL_DIR") ".") "/skills")) (defparameter *skill-llm-gateway* '(:name "llm-gateway" :description "Unified provider-agnostic LLM interface." :capabilities (:ask-llm :get-embedding) :type :probabilistic) "Skill metadata for the LLM Gateway.") #+end_src ** Request Execution #+begin_src lisp :tangle (expand-file-name "org-skill-llm-gateway.lisp" (concat (or (getenv "INSTALL_DIR") ".") "/skills")) (defun execute-llm-request (&key prompt system-prompt provider model) "Generic executor for all LLM providers." (let* ((active-provider (or provider :ollama)) (api-key (uiop:getenv (format nil "~:@(~a_API_KEY~)" active-provider))) (full-prompt (if system-prompt (format nil "~a~%~%~a" system-prompt prompt) prompt))) (case active-provider (:ollama (let* ((host (or (uiop:getenv "OLLAMA_HOST") "localhost:11434")) (url (format nil "http://~a/api/generate" host)) (body (cl-json:encode-json-to-string `((model . ,(or model "llama3")) (prompt . ,full-prompt) (stream . :false))))) (handler-case (let* ((response (dex:post url :headers '(("Content-Type" . "application/json")) :content body)) (json (cl-json:decode-json-from-string response))) (list :status :success :content (cdr (assoc :response json)))) (error (c) (list :status :error :message (format nil "Ollama Failure: ~a" c)))))) (t (list :status :error :message "Provider not implemented"))))) #+end_src ** Cognitive Tools #+begin_src lisp :tangle (expand-file-name "org-skill-llm-gateway.lisp" (concat (or (getenv "INSTALL_DIR") ".") "/skills")) (def-cognitive-tool :get-ollama-embedding "Generates vector embeddings via Ollama API." ((:text :type :string :description "Text to embed.")) :body (lambda (args) (let ((text (getf args :text))) (let* ((host (or (uiop:getenv "OLLAMA_HOST") "localhost:11434")) (url (format nil "http://~a/api/embeddings" host)) (body (cl-json:encode-json-to-string `((model . "nomic-embed-text") (prompt . ,text))))) (handler-case (let* ((response (dex:post url :headers '(("Content-Type" . "application/json")) :content body)) (json (cl-json:decode-json-from-string response))) (cdr (assoc :embedding json))) (error (c) (harness-log "OLLAMA EMBED ERROR: ~a" c) nil)))))) #+end_src #+begin_src lisp :tangle (expand-file-name "org-skill-llm-gateway.lisp" (concat (or (getenv "INSTALL_DIR") ".") "/skills")) (def-cognitive-tool :ask-llm "Unified interface for interacting with LLM providers." ((:prompt :type :string :description "The user prompt") (:system-prompt :type :string :description "The system prompt (optional)") (:provider :type :keyword :description "The provider (e.g., :ollama, :openai)") (:model :type :string :description "The model name")) :body (lambda (args) (execute-llm-request :prompt (getf args :prompt) :system-prompt (getf args :system-prompt) :provider (getf args :provider) :model (getf args :model)))) #+end_src ** Skill Registration #+begin_src lisp :tangle (expand-file-name "org-skill-llm-gateway.lisp" (concat (or (getenv "INSTALL_DIR") ".") "/skills")) (defskill :skill-llm-gateway :priority 50 :trigger (lambda (ctx) (declare (ignore ctx)) t) :probabilistic (lambda (ctx) (let ((input (getf ctx :user-input))) (when input (execute-llm-request :prompt input)))) :deterministic (lambda (action ctx) (declare (ignore ctx)) action)) #+end_src