refactor: move LLM provider implementations to literate skill notes
This commit is contained in:
@@ -52,6 +52,9 @@ Dynamically route to OpenRouter's :free model list for Reflexive and Cognitive t
|
||||
* Registration
|
||||
#+begin_src lisp
|
||||
(progn
|
||||
;; Register the economist as the kernel's model selector
|
||||
(setf org-agent::*model-selector-fn* #'economist-get-model-for-provider)
|
||||
|
||||
(defskill :skill-economist
|
||||
:priority 100
|
||||
:trigger (lambda (context) (eq (getf (getf context :payload) :sensor) :cost-audit))
|
||||
|
||||
@@ -35,53 +35,45 @@ Define the interface for reliable communication with the Anthropic Messages API.
|
||||
:STATUS: SIGNED
|
||||
:END:
|
||||
|
||||
* Phase B: Blueprint (PROTOCOL)
|
||||
** 2. Semantic Interfaces
|
||||
|
||||
** 1. Architectural Intent
|
||||
*** `execute-anthropic-request`
|
||||
:signature `(execute-anthropic-request prompt system-prompt &key model) :string`
|
||||
:description "Direct call to the Anthropic Messages API."
|
||||
|
||||
The Anthropic Provider Agent will be designed as a stateless service responsible for managing communication with the Anthropic API. It will abstract away the complexities of API authentication, model selection, prompt construction, and response parsing, exposing a simple, functional interface to the broader PSF. The key architectural constraint is reliability and efficient context management. Error handling and API rate limiting must be gracefully managed to prevent cascading failures.
|
||||
* Phase D: Build (Implementation)
|
||||
|
||||
** 2. Semantic Interfaces (Lisp Signatures)
|
||||
#+begin_src lisp :tangle ../projects/org-skill-provider-anthropic/src/provider-logic.lisp
|
||||
(in-package :org-agent)
|
||||
|
||||
*** `anthropic-query`
|
||||
(defun execute-anthropic-request (prompt system-prompt &key model)
|
||||
(let ((api-key (uiop:getenv "ANTHROPIC_API_KEY"))
|
||||
(endpoint "https://api.anthropic.com/v1/messages")
|
||||
(model-id (or model "claude-3-5-sonnet-20240620")))
|
||||
(unless api-key (return-from execute-anthropic-request "(:type :LOG :payload (:text \"Anthropic API Key missing\"))"))
|
||||
(let* ((headers `(("Content-Type" . "application/json")
|
||||
("x-api-key" . ,api-key)
|
||||
("anthropic-version" . "2023-06-01")))
|
||||
(body (cl-json:encode-json-to-string
|
||||
`((model . ,model-id)
|
||||
(max_tokens . 4096)
|
||||
(system . ,system-prompt)
|
||||
(messages . (( (role . "user") (content . ,prompt) )))))))
|
||||
(handler-case (let* ((response (dex:post endpoint :headers headers :content body :connect-timeout 10 :read-timeout 30))
|
||||
(json (cl-json:decode-json-from-string response)))
|
||||
;; Anthropic response structure: content[0].text
|
||||
(cdr (assoc :text (car (cdr (assoc :content json))))))
|
||||
(error (c) (format nil "(:type :LOG :payload (:text \"Anthropic Failure: ~a\"))" c))))))
|
||||
#+end_src
|
||||
|
||||
This function is the primary entry point for interacting with the Anthropic models. It constructs the full prompt (system prompt + user query), sends it to the Anthropic API, and returns the model's response.
|
||||
|
||||
:lisp
|
||||
(defun anthropic-query (query &key model system-prompt max-tokens temperature top-p stream?)
|
||||
"Sends a query to the Anthropic API and returns the response.
|
||||
|
||||
QUERY: The user's question or instruction (string).
|
||||
MODEL: (Optional) The Claude model to use (string, default 'claude-v1.3').
|
||||
SYSTEM-PROMPT: (Optional) A system prompt to guide the model (string).
|
||||
MAX-TOKENS: (Optional) The maximum number of tokens in the response (integer).
|
||||
TEMPERATURE: (Optional) Sampling temperature (float, 0.0-1.0).
|
||||
TOP-P: (Optional) Top-p sampling (float, 0.0-1.0).
|
||||
STREAM?: (Optional) Whether to stream the response (boolean, default nil).
|
||||
|
||||
Returns: A string containing the model's generated response, or nil on error."
|
||||
...)
|
||||
|
||||
*** `anthropic-authenticate`
|
||||
|
||||
This function handles API key authentication. It retrieves the API key from environment variables or a configuration file.
|
||||
|
||||
:lisp
|
||||
(defun anthropic-authenticate ()
|
||||
"Authenticates with the Anthropic API, retrieving the API key from the environment or config.
|
||||
|
||||
Returns: The API key (string), or nil on failure."
|
||||
...)
|
||||
|
||||
*** `anthropic-model-resolve`
|
||||
|
||||
This function resolves a symbolic model name (e.g., 'most-powerful') to a specific Claude model identifier (e.g., 'claude-v1.3-100k').
|
||||
|
||||
:lisp
|
||||
(defun anthropic-model-resolve (model-symbol)
|
||||
"Resolves a symbolic model name to a specific Anthropic model identifier.
|
||||
|
||||
MODEL-SYMBOL: A symbolic name for the model (symbol).
|
||||
|
||||
Returns: The Anthropic model identifier (string), or nil if resolution fails."
|
||||
...)
|
||||
* Registration
|
||||
#+begin_src lisp
|
||||
(progn
|
||||
(org-agent:register-neuro-backend :anthropic #'execute-anthropic-request)
|
||||
|
||||
(defskill :skill-provider-anthropic
|
||||
:priority 110
|
||||
:trigger (lambda (context) nil)
|
||||
:neuro (lambda (context) nil)
|
||||
:symbolic (lambda (action context) action)))
|
||||
#+end_src
|
||||
|
||||
@@ -46,9 +46,21 @@ Expose two distinct backends to the kernel: =:gemini-api= and =:gemini-web=.
|
||||
#+begin_src lisp :tangle ../projects/org-skill-provider-gemini/src/provider-logic.lisp
|
||||
(in-package :org-agent)
|
||||
|
||||
(defun execute-gemini-request (prompt system-prompt &key model)
|
||||
(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-latest: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-gemini-api-request (prompt system-prompt &key model)
|
||||
"Implementation uses the standard kernel execute-gemini-request logic."
|
||||
(org-agent::execute-gemini-request prompt system-prompt :model model))
|
||||
(execute-gemini-request prompt system-prompt :model model))
|
||||
#+end_src
|
||||
|
||||
** Web Implementation
|
||||
@@ -63,6 +75,7 @@ Expose two distinct backends to the kernel: =:gemini-api= and =:gemini-web=.
|
||||
* Registration
|
||||
#+begin_src lisp
|
||||
(progn
|
||||
(org-agent:register-neuro-backend :gemini #'execute-gemini-request)
|
||||
(org-agent:register-neuro-backend :gemini-api #'execute-gemini-api-request)
|
||||
(org-agent:register-neuro-backend :gemini-web #'execute-gemini-web-request)
|
||||
|
||||
|
||||
72
notes/org-skill-provider-groq.org
Normal file
72
notes/org-skill-provider-groq.org
Normal file
@@ -0,0 +1,72 @@
|
||||
:PROPERTIES:
|
||||
:ID: f7db1884-49cc-4db6-9ca1-4c69ec3a631e
|
||||
:CREATED: [2026-04-08 Wed 11:30]
|
||||
:EDITED: [2026-04-08 Wed 11:30]
|
||||
:END:
|
||||
#+TITLE: SKILL: Groq Provider Agent (Universal Literate Note)
|
||||
#+STARTUP: content
|
||||
#+FILETAGS: :llm:provider:groq:lpu:psf:
|
||||
|
||||
* Overview
|
||||
The *Groq Provider Agent* leverages Groq's LPU (Language Processing Unit) for ultra-low latency inference. It is ideal for reflexive tasks and rapid interaction loops.
|
||||
|
||||
* Phase A: Demand (PRD)
|
||||
:PROPERTIES:
|
||||
:STATUS: FROZEN
|
||||
:END:
|
||||
|
||||
** 1. Purpose
|
||||
Enable high-speed neural completion via the Groq API.
|
||||
|
||||
** 2. User Needs
|
||||
- *Latency:* Sub-second response times for System 1 "hunches".
|
||||
- *Compatibility:* OpenAI-compatible endpoint.
|
||||
- *Reliability:* Secure management of `$GROQ_API_KEY`.
|
||||
|
||||
* Phase B: Blueprint (PROTOCOL)
|
||||
:PROPERTIES:
|
||||
:STATUS: SIGNED
|
||||
:END:
|
||||
|
||||
** 1. Architectural Intent
|
||||
Interface for executing ultra-fast neural requests.
|
||||
|
||||
** 2. Semantic Interfaces
|
||||
|
||||
*** `execute-groq-request`
|
||||
:signature `(execute-groq-request prompt system-prompt &key model) :string`
|
||||
:description "Direct call to the Groq Cloud API."
|
||||
|
||||
* Phase D: Build (Implementation)
|
||||
|
||||
#+begin_src lisp :tangle ../projects/org-skill-provider-groq/src/provider-logic.lisp
|
||||
(in-package :org-agent)
|
||||
|
||||
(defun execute-groq-request (prompt system-prompt &key model)
|
||||
(let ((api-key (uiop:getenv "GROQ_API_KEY"))
|
||||
(endpoint "https://api.groq.com/openai/v1/chat/completions")
|
||||
(model-id (or model "llama-3.3-70b-versatile")))
|
||||
(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-id)
|
||||
(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))))))
|
||||
#+end_src
|
||||
|
||||
* Registration
|
||||
#+begin_src lisp
|
||||
(progn
|
||||
(org-agent:register-neuro-backend :groq #'execute-groq-request)
|
||||
|
||||
(defskill :skill-provider-groq
|
||||
:priority 80
|
||||
:trigger (lambda (context) nil)
|
||||
:neuro (lambda (context) nil)
|
||||
:symbolic (lambda (action context) action)))
|
||||
#+end_src
|
||||
@@ -43,49 +43,42 @@ Define the interface for reliable communication with the OpenAI Chat Completions
|
||||
** 1. Architectural Intent
|
||||
The OpenAI Provider Agent will act as a translator and executor for requests targeting OpenAI's Chat Completions API. It will abstract away the complexities of API authentication, payload construction, and response parsing, providing a clean and consistent interface for System 1 agents within the Lisp Machine. It prioritizes secure API key management using environment variables and offers configurable parameters for inference. The architecture focuses on resilience, managing potential API errors (timeouts, rate limits) through robust error handling.
|
||||
|
||||
** 2. Semantic Interfaces (Lisp Signatures)
|
||||
** 2. Semantic Interfaces
|
||||
|
||||
*** `openai-chat-completion`
|
||||
*** `execute-openai-request`
|
||||
:signature `(execute-openai-request prompt system-prompt &key model) :string`
|
||||
:description "Direct call to the OpenAI Chat Completions API."
|
||||
|
||||
- *Purpose:* Primary function to interact with the OpenAI Chat Completions API.
|
||||
- *Signature:* `(openai-chat-completion messages &key model temperature api-key)`
|
||||
- *Arguments:*
|
||||
- `messages`: A list of message objects, conforming to the OpenAI Chat Completions API format (e.g., `((:role :system :content "You are a helpful assistant") (:role :user :content "Hello!"))`). Each message is a plist of roles and content keys.
|
||||
- `model`: (optional, keyword) The OpenAI model to use (e.g., `:gpt-4o`, `:gpt-3.5-turbo`). Defaults to a configurable default model.
|
||||
- `temperature`: (optional, keyword) The sampling temperature (0.0 - 2.0). Defaults to a configurable default temperature.
|
||||
- `api-key`: (optional, keyword) The OpenAI API key. If not provided, defaults to the value of the `$OPENAI_API_KEY` environment variable.
|
||||
- *Return Value:* A plist representing the API response. Crucially, will contain an ` :choices` key, whose value is a list of choices.
|
||||
* Phase D: Build (Implementation)
|
||||
|
||||
*** `construct-chat-payload`
|
||||
#+begin_src lisp :tangle ../projects/org-skill-provider-openai/src/provider-logic.lisp
|
||||
(in-package :org-agent)
|
||||
|
||||
- *Purpose:* Constructs the JSON payload for the Chat Completions API from a list of message objects.
|
||||
- *Signature:* `(construct-chat-payload messages model temperature)`
|
||||
- *Arguments:*
|
||||
- `messages`: A list of message objects as described above.
|
||||
- `model`: The OpenAI model string.
|
||||
- `temperature`: String representation of sampling temperature.
|
||||
- *Return Value:* A JSON string representing the complete payload.
|
||||
(defun execute-openai-request (prompt system-prompt &key model)
|
||||
(let ((api-key (uiop:getenv "OPENAI_API_KEY"))
|
||||
(endpoint "https://api.openai.com/v1/chat/completions")
|
||||
(model-id (or model "gpt-4o")))
|
||||
(unless api-key (return-from execute-openai-request "(:type :LOG :payload (:text \"OpenAI 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-id)
|
||||
(messages . (( (role . "system") (content . ,system-prompt) )
|
||||
( (role . "user") (content . ,prompt) )))))))
|
||||
(handler-case (let* ((response (dex:post endpoint :headers headers :content body :connect-timeout 10 :read-timeout 30))
|
||||
(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 \"OpenAI Failure: ~a\"))" c))))))
|
||||
#+end_src
|
||||
|
||||
*** `extract-choices`
|
||||
|
||||
- *Purpose:* Extracts the list of choices from the API response.
|
||||
- *Signature:* `(extract-choices response)`
|
||||
- *Arguments:*
|
||||
- `response`: The API response plist from `openai-chat-completion`.
|
||||
- *Return Value:* A list of choice objects (plists). Typically, the relevant choice contains a `:message` with `:role` and `:content`.
|
||||
|
||||
*** `get-openai-api-key`
|
||||
|
||||
- *Purpose:* Retrieves the OpenAI API key.
|
||||
- *Signature:* `(get-openai-api-key &optional key-override)`
|
||||
- *Arguments:*
|
||||
- `key-override:` (optional) The actual API key value, if provided, skip looking for the default $OPENAI_API_KEY environment variable.
|
||||
- *Return Value:* A string representing the API key. If the environment variable is not found or the `key-override` argument is missing raises an error. (Security consideration: API Key should be redacted from logs).
|
||||
|
||||
*** `handle-openai-error`
|
||||
|
||||
- *Purpose:* Handles errors returned by the OpenAI API (e.g., timeouts, rate limiting).
|
||||
- *Signature:* `(handle-openai-error error)`
|
||||
- *Arguments:*
|
||||
- `error`: The error object returned by the underlying HTTP client.
|
||||
- *Return Value:* Signals an appropriate error condition within the Lisp Machine, potentially including retry logic. (TBD)
|
||||
* Registration
|
||||
#+begin_src lisp
|
||||
(progn
|
||||
(org-agent:register-neuro-backend :openai #'execute-openai-request)
|
||||
|
||||
(defskill :skill-provider-openai
|
||||
:priority 100
|
||||
:trigger (lambda (context) nil)
|
||||
:neuro (lambda (context) nil)
|
||||
:symbolic (lambda (action context) action)))
|
||||
#+end_src
|
||||
|
||||
@@ -33,13 +33,21 @@ Define the interface for unified communication with the OpenRouter API.
|
||||
Interfaces for executing neural completion requests via the unified OpenRouter gateway.
|
||||
|
||||
** 2. Semantic Interfaces
|
||||
"Executes a completion request via the OpenRouter API."
|
||||
"Returns the preferred model ID for a given tier."
|
||||
|
||||
*** `execute-openrouter-request`
|
||||
:signature `(execute-openrouter-request prompt system-prompt &key model) :string`
|
||||
:description "Routes the request through the OpenRouter gateway."
|
||||
|
||||
*** `openrouter-get-available-models`
|
||||
:signature `(openrouter-get-available-models) :list`
|
||||
:description "Fetches available models from the OpenRouter API."
|
||||
|
||||
* Phase D: Build (Implementation)
|
||||
|
||||
** Tiered Model Resolution
|
||||
#+begin_src lisp :tangle ../projects/org-skill-provider-openrouter/src/provider-logic.lisp
|
||||
(in-package :org-agent)
|
||||
|
||||
(defun get-openrouter-tiered-model (tier)
|
||||
(case tier
|
||||
(:powerful "anthropic/claude-3.5-sonnet")
|
||||
@@ -50,23 +58,25 @@ Interfaces for executing neural completion requests via the unified OpenRouter g
|
||||
|
||||
** Request Execution
|
||||
#+begin_src lisp :tangle ../projects/org-skill-provider-openrouter/src/provider-logic.lisp
|
||||
(defun execute-openrouter-request (prompt system-prompt)
|
||||
(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"))
|
||||
(endpoint "https://openrouter.ai/api/v1/chat/completions")
|
||||
(model-id (or model (get-openrouter-tiered-model :fast))))
|
||||
|
||||
(unless api-key
|
||||
(return-from execute-openrouter-request
|
||||
"(:type :LOG :payload (:text \"OpenRouter API Key missing in environment\"))"))
|
||||
|
||||
(let* ((model (get-openrouter-tiered-model :fast))
|
||||
(headers `(("Content-Type" . "application/json")
|
||||
(let* ((headers `(("Content-Type" . "application/json")
|
||||
("Authorization" . ,(format nil "Bearer ~a" api-key))
|
||||
("HTTP-Referer" . "https://github.com/amr/org-agent")
|
||||
("X-Title" . "org-agent Sovereign Kernel")))
|
||||
(body (cl-json:encode-json-to-string
|
||||
`((model . ,model)
|
||||
(messages . (( (role . "system") (content . ,system-prompt) )
|
||||
( (role . "user") (content . ,prompt) )))))))
|
||||
(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) )))))
|
||||
"/")))
|
||||
|
||||
(handler-case
|
||||
(let* ((response (dex:post endpoint :headers headers :content body :connect-timeout 10 :read-timeout 30))
|
||||
@@ -75,6 +85,29 @@ Interfaces for executing neural completion requests via the unified OpenRouter g
|
||||
(cdr (assoc :content (cdr (assoc :message (car (cdr (assoc :choices json))))))))
|
||||
(error (c)
|
||||
(format nil "(:type :LOG :payload (:text \"OpenRouter Error: ~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)))))
|
||||
#+end_src
|
||||
|
||||
* Registration
|
||||
@@ -82,8 +115,8 @@ Interfaces for executing neural completion requests via the unified OpenRouter g
|
||||
;; Register the backend with the kernel at load-time
|
||||
(org-agent:register-neuro-backend :openrouter #'execute-openrouter-request)
|
||||
|
||||
;; Update the cascade to prefer OpenRouter
|
||||
(setf org-agent:*provider-cascade* '(:openrouter :gemini))
|
||||
;; Export discovery if needed
|
||||
(setf (fdefinition 'org-agent:openrouter-get-available-models) #'openrouter-get-available-models)
|
||||
|
||||
(defskill :skill-provider-openrouter
|
||||
:priority 100
|
||||
|
||||
Submodule projects/org-agent updated: 360eb40ccd...037941e690
Reference in New Issue
Block a user