Scheme LLM Toolkit: Functional LLM Integration with Contracts
Table of Contents
- 1. Scheme LLM Toolkit
1. Scheme LLM Toolkit
A composable, functional interface for LLM integration in Guile Scheme, emphasizing type safety through contracts and formal specification.
1.1. Repository
1.2. Philosophy
The toolkit follows several key principles:
- Composability - Functions compose naturally using standard Scheme idioms
- Provider Abstraction - Unified interface across LLM providers (Ollama, OpenAI, Anthropic)
- Contracts as Documentation - Runtime contracts serve as executable specifications
- Formal Methods Integration - Contracts bridge to TLA+, Lean4, and OpenAPI specifications
1.3. Architecture
src/llm/
├── core/
│ ├── provider.scm # Provider abstraction layer
│ ├── contracts.scm # Racket-inspired contract system
│ └── tools.scm # Function calling / tool use
├── providers/
│ ├── ollama.scm # Local Ollama API
│ ├── openai.scm # OpenAI API
│ └── anthropic.scm # Anthropic Claude API
└── utils/
├── http.scm # HTTP client (curl-based)
└── json.scm # JSON parsing/generation
1.4. Contract System
The contract system is inspired by Racket's approach to design-by-contract:
1.4.1. Core Concepts
- Flat Contracts - Simple predicates (
string?,number?) - Combinators - Compose contracts (
and/c,or/c,not/c) - Function Contracts - Specify domain/range (
->/c) - Structural Contracts - Validate data shapes (
alist/c,listof/c) - Blame Assignment - Identify who violated the contract
1.4.2. Example Usage
(use-modules (llm core contracts))
;; Define a flat contract
(define positive?
(flat-contract (lambda (x) (> x 0))
#:name "positive?"))
;; Contract combinators
(define non-empty-string?
(and/c (flat-contract string?)
(flat-contract (lambda (s) (> (string-length s) 0)))))
;; Function contract
(define string->number/c
(->/c (flat-contract string?)
(flat-contract number?)))
;; Structural contract for LLM messages
(define message/c
(alist/c
#:required `((role . ,role/c)
(content . ,string/c))
#:optional `((name . ,string/c))))
1.4.3. Formal Methods Mapping
| Racket Concept | TLA+ Equivalent | Lean4 Equivalent | OpenAPI |
|---|---|---|---|
| Flat contract | State predicate | Prop | type |
| Function contract | Action spec | Function type | operation |
| Blame | Error trace | Error monad | 4xx/5xx |
1.5. Provider Abstraction
All providers implement a common interface:
(use-modules (llm core provider)
(llm providers openai-provider))
;; Register a provider
(register-openai-provider!
#:api-key (getenv "OPENAI_API_KEY")
#:model "gpt-4o")
;; Use through abstraction
(let ((provider (get-provider 'openai)))
(provider-complete provider "Explain recursion briefly."))
;; Provider capabilities are declared
(provider-capabilities provider)
;; => (streaming chat embeddings function-calling ...)
1.6. Function Calling / Tool Use
(use-modules (llm core tools))
;; Define a tool
(define weather-tool
(make-tool
#:name "get_weather"
#:description "Get current weather for a location"
#:parameters '((type . "object")
(properties . ((city . ((type . "string"))))))
#:handler (lambda (city)
(format #f "Weather in ~a: sunny" city))))
;; Convert to provider format
(tools->openai-format (list weather-tool))
(tools->anthropic-format (list weather-tool))
1.7. FreeBSD Compatibility
The toolkit includes first-class FreeBSD support:
# FreeBSD setup
pkg install guile3 curl bash
# guile-json must be built from source
gmake install-guile-json
# Run tests
gmake test
Key adaptations:
- Auto-detects OS and sets
GUILE=guile3on FreeBSD - Uses
/usr/local/bin/bashinstead of/bin/bash - Provides
bin/guile-llmwrapper for portable execution - Build script for guile-json (not in FreeBSD packages)
1.8. Formal Specification
The project includes formal specifications mapping contracts to:
1.8.1. TLA+ Style
---------------------------- MODULE Provider ----------------------------
VARIABLES providerState, requestQueue
TypeInvariant ==
/\ providerState \in [Providers -> {"ready", "busy", "error"}]
CanAcceptRequest(p) ==
providerState[p] = "ready"
Safety ==
\A p \in Providers:
providerState[p] = "error" =>
~(\E r \in Responses: Complete(p, _, r))
=========================================================================
1.8.2. Lean4 Style
structure Provider where
name : String
complete : String → IO (Except Error Response)
-- Proof obligations
complete_deterministic : ∀ p, complete p = complete p
1.8.3. JSON Schema / OpenAPI
;; Convert JSON Schema to contract
(define user/c
(json-schema/c
'((type . "object")
(required . ("name" "email"))
(properties
. ((name . ((type . "string")))
(email . ((type . "string"))))))))
(contract-check user/c '((name . "Alice") (email . "a@b.com")))
;; => #t
1.9. Project Status
| Component | Status | Notes |
|---|---|---|
| Ollama Provider | Complete | Local LLM support |
| OpenAI Provider | Complete | Including function calling |
| Anthropic Provider | Complete | Claude API support |
| Contract System | Complete | Racket-inspired |
| Function Calling | Complete | Cross-provider |
| CLI Tool | Planned | Interactive chat |
| RAG Utilities | Planned | Embeddings + search |
1.10. References
- Racket Contracts: https://docs.racket-lang.org/reference/contracts.html
- TLA+ Specification Language: https://lamport.azurewebsites.net/tla/tla.html
- Lean4 Theorem Prover: https://lean-lang.org/
- OpenAPI Specification: https://spec.openapis.org/oas/latest.html
1.11. Installation
# Clone
git clone https://github.com/aygp-dr/scheme-llm-toolkit
cd scheme-llm-toolkit
# Check system
gmake check-system # FreeBSD
make check-system # Linux/macOS
# Install dependencies
gmake install-guile-deps
# Test
gmake test
# Use
bin/guile-llm -c "(use-modules (llm core provider)) (display 'ok)"
1.12. Why Scheme for LLMs?
- Homoiconicity - Code as data enables powerful prompt templating
- Macros - DSLs for prompt engineering compile to efficient code
- REPL-driven - Interactive development with live LLM connections
- Functional Composition - Pipeline LLM calls naturally
- Formal Methods Heritage - Natural fit for specification and verification
;; S-expression prompts compose naturally
(define code-review-prompt
`(system "You are a code reviewer")
(user ,(format #f "Review: ~a" code)))
;; Functional pipelines
(define analyze-and-fix
(compose
(curry provider-complete provider)
(curry format-prompt 'fix-bugs)
extract-issues
(curry provider-complete provider)
(curry format-prompt 'analyze)))
1.13. Conclusion
The Scheme LLM Toolkit demonstrates that functional programming principles–composition, immutability, and formal contracts–provide a robust foundation for LLM integration. By treating contracts as executable specifications that bridge to formal methods, we gain both runtime safety and documentation that can inform verification efforts.