initial pass
ober
76e2e5227aca1be8699113986fb73b259928157b
new file mode 100644 --- /dev/null +++ b/Makefile @@ -0,0 +1,28 @@ +SCHEME = scheme +JERBOA_HOME ?= $(HOME)/mine/jerboa +LIBDIRS = --libdirs $(JERBOA_HOME)/lib:./lib + +.PHONY: all build run test clean repl + +all: build + +build: + $(SCHEME) $(LIBDIRS) --compile-imported-libraries --program main.ss + +run: + $(SCHEME) $(LIBDIRS) --script main.ss + +repl: + $(SCHEME) $(LIBDIRS) + +test: + $(SCHEME) $(LIBDIRS) --script test/run.ss + +clean: + find . -name "*.so" -delete + find . -name "*.wpo" -delete + rm -f jcode + +# Static binary (requires Chez Scheme static libs) +static: build + @echo "Static build not yet implemented" new file mode 100644 --- /dev/null +++ b/PLAN.md @@ -0,0 +1,602 @@ +# Jerboa-Code Implementation Plan + +*A portable AI coding agent in Chez Scheme* + +## Project Goal + +Implement a fully-featured AI coding agent equivalent to OpenCode, but: +- **Portable**: Runs on FreeBSD, Linux, macOS, Windows, anywhere Chez Scheme runs +- **Simple**: Clean architecture without JavaScript ecosystem bloat +- **Fast**: Single static binary, no runtime dependencies +- **Maintainable**: ~5,000 lines of Scheme vs 294,000 lines of TypeScript + +## Available Jerboa Standard Library + +The following modules are already available in `~/mine/jerboa/lib/std/`: + +### Core Infrastructure ✓ +- `(std net request)` - HTTP client (GET, POST, PUT, DELETE) +- `(std db sqlite)` - SQLite database with prepared statements +- `(std misc process)` - Process execution, subprocess I/O +- `(std text json)` - JSON reader/writer +- `(std os shell)` - Shell utilities +- `(std os path)` - Path manipulation +- `(std os env)` - Environment variables +- `(std os temp)` - Temporary files + +### Additional Useful Modules ✓ +- `(std text glob)` - Glob pattern matching +- `(std config)` - Configuration file parsing +- `(std cli getopt)` - Command-line argument parsing +- `(std log)` - Structured logging +- `(std misc uuid)` - UUID generation +- `(std text base64)` - Base64 encoding +- `(std crypto digest)` - Cryptographic hashing +- `(std net uri)` - URI parsing +- `(std text diff)` - Text diff/patch +- `(std misc retry)` - Retry with backoff + +--- + +## Architecture Overview + +``` +jerboa-code/ +├── lib/ +│ └── jerboa-code/ +│ ├── core/ +│ │ ├── agent.sls # Main agent loop +│ │ ├── message.sls # Message types and handling +│ │ ├── session.sls # Session persistence +│ │ └── config.sls # Configuration management +│ │ +│ ├── provider/ +│ │ ├── provider.sls # Provider abstraction +│ │ ├── openai.sls # OpenAI/compatible APIs +│ │ ├── anthropic.sls # Anthropic Claude API +│ │ ├── google.sls # Google Gemini API +│ │ └── models.sls # Model definitions +│ │ +│ ├── tool/ +│ │ ├── registry.sls # Tool registration +│ │ ├── file.sls # read, write, edit tools +│ │ ├── bash.sls # Shell command execution +│ │ ├── search.sls # grep, glob, find +│ │ ├── web.sls # HTTP fetch, web search +│ │ └── batch.sls # Parallel tool execution +│ │ +│ └── ui/ +│ ├── cli.sls # Command-line interface +│ ├── repl.sls # Interactive REPL +│ └── format.sls # Output formatting +│ +├── main.ss # Entry point +├── Makefile # Build system +├── jerboa.pkg # Package definition +└── PLAN.md # This file +``` + +--- + +## Implementation Phases + +### Phase 1: Foundation (Week 1-2) + +#### 1.1 Project Skeleton +```scheme +;; jerboa.pkg +(package jerboa-code + :package "jerboa-code" + :version "0.1.0" + :depends + ((std net) + (std db) + (std text) + (std misc) + (std os) + (std cli))) +``` + +#### 1.2 Configuration System +- [ ] Read `~/.config/jerboa-code/config.json` or `jerboa-code.json` +- [ ] API key management (env vars + config file) +- [ ] Model selection and provider configuration +- [ ] Project-local configuration support + +```scheme +;; lib/jerboa-code/core/config.sls +(library (jerboa-code core config) + (export load-config + config-api-key + config-model + config-provider) + (import (chezscheme) + (std text json) + (std os path) + (std os env))) +``` + +#### 1.3 Session Storage +- [ ] SQLite database for sessions +- [ ] Message persistence +- [ ] Session listing/resumption + +```scheme +;; lib/jerboa-code/core/session.sls +(library (jerboa-code core session) + (export session-create + session-load + session-save + session-list + session-add-message + session-get-messages) + (import (chezscheme) + (std db sqlite) + (std misc uuid))) +``` + +#### 1.4 Message Types +- [ ] User messages +- [ ] Assistant messages +- [ ] Tool calls and results +- [ ] Streaming support + +```scheme +;; lib/jerboa-code/core/message.sls +(library (jerboa-code core message) + (export make-user-message + make-assistant-message + make-tool-call + make-tool-result + message->json + json->message) + (import (chezscheme) + (std text json))) +``` + +--- + +### Phase 2: Provider Integration (Week 2-3) + +#### 2.1 Provider Abstraction +- [ ] Generic provider interface +- [ ] API key resolution +- [ ] Model capabilities + +```scheme +;; lib/jerboa-code/provider/provider.sls +(library (jerboa-code provider provider) + (export make-provider + provider-chat + provider-stream + provider-available?) + (import (chezscheme) + (std net request) + (std text json))) +``` + +#### 2.2 OpenAI Provider +- [ ] Chat completions API +- [ ] Tool calling support +- [ ] Streaming responses +- [ ] o1/o3 reasoning models + +```scheme +;; lib/jerboa-code/provider/openai.sls +(library (jerboa-code provider openai) + (export openai-chat + openai-stream + openai-models) + (import (chezscheme) + (jerboa-code provider provider) + (std net request) + (std text json))) +``` + +#### 2.3 Anthropic Provider +- [ ] Messages API +- [ ] Extended thinking +- [ ] Tool use protocol + +```scheme +;; lib/jerboa-code/provider/anthropic.sls +(library (jerboa-code provider anthropic) + (export anthropic-chat + anthropic-stream) + (import (chezscheme) + (jerboa-code provider provider) + (std net request) + (std text json))) +``` + +#### 2.4 Additional Providers +- [ ] Google Gemini +- [ ] OpenRouter (for model aggregation) +- [ ] Local models (Ollama) + +--- + +### Phase 3: Tool System (Week 3-4) + +#### 3.1 Tool Registry +- [ ] Tool definition format +- [ ] Schema generation for AI +- [ ] Tool execution dispatch + +```scheme +;; lib/jerboa-code/tool/registry.sls +(library (jerboa-code tool registry) + (export define-tool + tool-schema + tool-execute + list-tools) + (import (chezscheme) + (std text json))) +``` + +#### 3.2 File Tools +- [ ] `read` - Read file contents +- [ ] `write` - Write file contents +- [ ] `edit` - String replacement editing +- [ ] `glob` - Find files by pattern +- [ ] `grep` - Search file contents + +```scheme +;; lib/jerboa-code/tool/file.sls +(library (jerboa-code tool file) + (export tool-read + tool-write + tool-edit + tool-glob + tool-grep) + (import (chezscheme) + (std os path) + (std text glob) + (std io))) +``` + +#### 3.3 Bash Tool +- [ ] Command execution +- [ ] Timeout handling +- [ ] Output capture +- [ ] Working directory support + +```scheme +;; lib/jerboa-code/tool/bash.sls +(library (jerboa-code tool bash) + (export tool-bash + tool-bash-with-timeout) + (import (chezscheme) + (std misc process) + (std os shell))) +``` + +#### 3.4 Web Tools +- [ ] `fetch` - HTTP GET/POST +- [ ] Basic HTML parsing + +```scheme +;; lib/jerboa-code/tool/web.sls +(library (jerboa-code tool web) + (export tool-fetch + tool-web-search) + (import (chezscheme) + (std net request) + (std markup html-parser))) +``` + +#### 3.5 Batch Execution +- [ ] Parallel tool execution +- [ ] Result aggregation + +--- + +### Phase 4: Agent Core (Week 4-5) + +#### 4.1 Main Agent Loop +```scheme +;; lib/jerboa-code/core/agent.sls +(library (jerboa-code core agent) + (export agent-run + agent-step + agent-process-response) + (import (chezscheme) + (jerboa-code core session) + (jerboa-code core message) + (jerboa-code provider provider) + (jerboa-code tool registry))) + +;; Core loop pseudocode: +;; 1. Get user input +;; 2. Add to session messages +;; 3. Send to AI provider with tool schemas +;; 4. If response has tool calls: +;; a. Execute each tool +;; b. Add results to messages +;; c. Loop back to step 3 +;; 5. Return final text response +``` + +#### 4.2 Streaming Support +- [ ] Server-sent events parsing +- [ ] Incremental output display +- [ ] Tool call streaming + +#### 4.3 Error Handling +- [ ] API error recovery +- [ ] Tool execution errors +- [ ] Network retry logic + +--- + +### Phase 5: CLI Interface (Week 5-6) + +#### 5.1 Command-Line Interface +```scheme +;; lib/jerboa-code/ui/cli.sls +(library (jerboa-code ui cli) + (export cli-main + cli-run + cli-chat + cli-session) + (import (chezscheme) + (std cli getopt) + (jerboa-code core agent))) +``` + +Commands: +- `jcode` - Start interactive session +- `jcode "prompt"` - One-shot query +- `jcode --model claude-sonnet-4` - Specify model +- `jcode --provider openai` - Specify provider +- `jcode session list` - List sessions +- `jcode session resume <id>` - Resume session +- `jcode config` - Show/edit configuration + +#### 5.2 Interactive REPL +- [ ] Readline support +- [ ] History +- [ ] Multi-line input +- [ ] Slash commands (/help, /model, /clear) + +#### 5.3 Output Formatting +- [ ] Markdown rendering (terminal) +- [ ] Code block highlighting +- [ ] Diff display +- [ ] Progress indicators + +--- + +### Phase 6: Advanced Features (Week 6-8) + +#### 6.1 MCP (Model Context Protocol) +- [ ] MCP server support +- [ ] Tool discovery via MCP +- [ ] External tool integration + +#### 6.2 Git Integration +- [ ] Workspace detection +- [ ] Commit/diff tools +- [ ] Branch management + +#### 6.3 LSP Integration (Optional) +- [ ] Use existing `(std lsp)` module +- [ ] Code intelligence tools + +#### 6.4 Plugin System +- [ ] Load external tool definitions +- [ ] Custom provider support + +--- + +## Tool Specifications + +### Core Tools (Must Have) + +| Tool | Description | OpenCode Equivalent | +|------|-------------|---------------------| +| `read` | Read file contents | `ReadTool` | +| `write` | Write file contents | `WriteTool` | +| `edit` | Replace string in file | `EditTool` | +| `bash` | Execute shell command | `BashTool` | +| `glob` | Find files by pattern | `GlobTool` | +| `grep` | Search file contents | `GrepTool` | +| `fetch` | HTTP request | `WebFetchTool` | +| `batch` | Parallel execution | `BatchTool` | + +### Extended Tools (Nice to Have) + +| Tool | Description | OpenCode Equivalent | +|------|-------------|---------------------| +| `ls` | List directory | `ListTool` | +| `patch` | Apply unified diff | `ApplyPatchTool` | +| `multi-edit` | Multiple edits | `MultiEditTool` | +| `lsp` | Language server | `LspTool` | +| `search` | Code search | `CodeSearchTool` | + +--- + +## API Specifications + +### Message Format + +```scheme +;; User message +(make-message + :role "user" + :content "Read the file main.ss") + +;; Assistant message with tool call +(make-message + :role "assistant" + :content nil + :tool-calls + [(:id "call_123" + :type "function" + :function (:name "read" :arguments "{\"path\": \"main.ss\"}"))]) + +;; Tool result +(make-message + :role "tool" + :tool-call-id "call_123" + :content "(library ...)") +``` + +### Provider Interface + +```scheme +(define-interface provider + ;; Send messages, get response + (chat [messages tools] -> response) + + ;; Stream response chunks + (stream [messages tools callback] -> void) + + ;; List available models + (models [] -> model-list)) +``` + +### Tool Interface + +```scheme +(define-interface tool + ;; Tool name for AI + (name [] -> string) + + ;; JSON schema for parameters + (schema [] -> json) + + ;; Execute tool + (execute [params] -> result)) +``` + +--- + +## File Count Estimate + +| Component | Files | Lines (est) | +|-----------|-------|-------------| +| Core (agent, session, config, message) | 4 | 800 | +| Providers (openai, anthropic, google, etc) | 5 | 1000 | +| Tools (file, bash, search, web, batch) | 6 | 1200 | +| CLI/UI | 3 | 600 | +| Utilities | 2 | 400 | +| **Total** | **20** | **4000** | + +--- + +## Build System + +```makefile +# Makefile +SCHEME = scheme +JERBOA_HOME = $(HOME)/mine/jerboa +LIBDIRS = --libdirs $(JERBOA_HOME)/lib:./lib + +.PHONY: all build run test clean + +all: build + +build: + $(SCHEME) $(LIBDIRS) --compile-imported-libraries --program main.ss + +run: + $(SCHEME) $(LIBDIRS) --script main.ss + +static: + # Build single static binary + $(SCHEME) $(LIBDIRS) --compile-whole-program main.ss jcode.wpo + cc -o jcode jcode.wpo $(CHEZ_LIBS) + +test: + $(SCHEME) $(LIBDIRS) --script test/run.ss + +clean: + rm -f *.so *.wpo jcode +``` + +--- + +## Testing Strategy + +1. **Unit Tests**: Each module has corresponding test file +2. **Integration Tests**: Full agent loop with mock provider +3. **E2E Tests**: Real API calls with test prompts + +```scheme +;; test/tool-file-test.ss +(import (std test) + (jerboa-code tool file)) + +(test-suite "file tools" + (test-case "read existing file" + (let ([result (tool-read "test/fixtures/sample.txt")]) + (assert-equal? result "hello world\n"))) + + (test-case "glob finds files" + (let ([files (tool-glob "test/**/*.ss")]) + (assert (> (length files) 0))))) +``` + +--- + +## Timeline Summary + +| Week | Phase | Deliverable | +|------|-------|-------------| +| 1-2 | Foundation | Config, session, message types | +| 2-3 | Providers | OpenAI, Anthropic working | +| 3-4 | Tools | All core tools implemented | +| 4-5 | Agent | Main loop, streaming | +| 5-6 | CLI | Interactive interface | +| 6-8 | Polish | MCP, git, testing, docs | + +**Total: 6-8 weeks for complete feature parity** + +--- + +## Success Criteria + +1. **Functional**: Can have productive coding sessions equivalent to OpenCode +2. **Portable**: Runs on FreeBSD, Linux, macOS without modification +3. **Fast**: Sub-100ms startup, single static binary +4. **Simple**: Easy to understand, modify, and extend +5. **Reliable**: No SQLite locking issues, proper error handling + +--- + +## Getting Started + +```bash +# Clone and setup +cd ~/mine +mkdir jerboa-code +cd jerboa-code + +# Create initial structure +mkdir -p lib/jerboa-code/{core,provider,tool,ui} +mkdir -p test + +# Start with main.ss +cat > main.ss << 'EOF' +#!/usr/bin/env scheme --script +(import (chezscheme) + (jerboa-code core agent) + (jerboa-code ui cli)) + +(cli-main (command-line-arguments)) +EOF + +# Build and run +make run +``` + +--- + +## References + +- OpenCode source: `~/mine/opencode/packages/opencode/src/` +- Jerboa stdlib: `~/mine/jerboa/lib/std/` +- Chez Scheme: `~/mine/ChezScheme/` +- OpenAI API: https://platform.openai.com/docs/api-reference +- Anthropic API: https://docs.anthropic.com/en/api new file mode 100644 --- /dev/null +++ b/jerboa.pkg @@ -0,0 +1,11 @@ +(package jerboa-code + :package "jerboa-code" + :version "0.1.0" + :description "Portable AI coding agent" + :depends + ((std net) + (std db) + (std text) + (std misc) + (std os) + (std cli))) new file mode 100644 --- /dev/null +++ b/lib/jerboa-code/core/agent.sls @@ -0,0 +1,116 @@ +#!chezscheme +;;; jerboa-code agent core - main AI interaction loop + +(library (jerboa-code core agent) + (export agent-run + agent-chat + agent-step) + + (import (chezscheme) + (jerboa-code core config) + (jerboa-code core message) + (jerboa-code core session) + (jerboa-code provider provider) + (jerboa-code tool registry) + (std text json) + (std log)) + + (define log (make-logger "agent")) + + ;; System prompt for the agent + (define (system-prompt) + "You are an expert AI coding assistant. You help users with software development tasks. + +You have access to tools that let you: +- Read and write files +- Execute shell commands +- Search code and files +- Make HTTP requests + +When the user asks you to do something: +1. Think about what tools you need +2. Use tools to gather information or make changes +3. Report back with results + +Be concise and helpful. When editing files, make minimal changes.") + + ;; Run a complete agent session + (define (agent-run session-id user-input) + (log-info log "agent-run" `((session . ,session-id) (input . ,user-input))) + + ;; Add user message to session + (let ([user-msg (make-user-message user-input)]) + (session-add-message session-id user-msg)) + + ;; Get all messages and run agent loop + (let ([messages (session-get-messages session-id)]) + (agent-loop session-id messages))) + + ;; Main agent loop - handles tool calls + (define (agent-loop session-id messages) + (let* ([provider (get-current-provider)] + [tools (get-tool-schemas)] + [response (provider-chat provider messages tools)]) + + (log-debug log "got-response" `((role . ,(message-role response)))) + + ;; Save assistant response + (session-add-message session-id response) + + ;; Check for tool calls + (if (message-tool-calls response) + ;; Execute tools and continue + (let ([results (execute-tool-calls (message-tool-calls response))]) + ;; Save tool results + (for-each + (lambda (result) (session-add-message session-id result)) + results) + ;; Continue the loop with updated messages + (agent-loop session-id (session-get-messages session-id))) + ;; No tool calls - return final response + response))) + + ;; Execute a list of tool calls + (define (execute-tool-calls tool-calls) + (log-info log "executing-tools" `((count . ,(length tool-calls)))) + (map execute-single-tool tool-calls)) + + ;; Execute a single tool call + (define (execute-single-tool tc) + (let* ([name (tool-call-name tc)] + [args (string->json-object (tool-call-arguments tc))] + [result (tool-execute name args)]) + (log-debug log "tool-result" `((tool . ,name) (result-length . ,(string-length result)))) + (make-tool-result (tool-call-id tc) result))) + + ;; Get the current provider based on config + (define (get-current-provider) + (let* ([provider-name (config-provider)] + [api-key (config-api-key)] + [model (config-model)]) + (make-provider provider-name api-key model))) + + ;; Simple one-shot chat (no session) + (define (agent-chat user-input) + (let* ([provider (get-current-provider)] + [tools (get-tool-schemas)] + [messages (list + (make-system-message (system-prompt)) + (make-user-message user-input))]) + (agent-chat-loop provider messages tools))) + + ;; Chat loop without session persistence + (define (agent-chat-loop provider messages tools) + (let ([response (provider-chat provider messages tools)]) + (if (message-tool-calls response) + (let* ([results (execute-tool-calls (message-tool-calls response))] + [new-messages (append messages (list response) results)]) + (agent-chat-loop provider new-messages tools)) + (message-content response)))) + + ;; Single step (for debugging) + (define (agent-step messages) + (let* ([provider (get-current-provider)] + [tools (get-tool-schemas)]) + (provider-chat provider messages tools))) +) new file mode 100644 --- /dev/null +++ b/lib/jerboa-code/core/config.sls @@ -0,0 +1,96 @@ +#!chezscheme +;;; jerboa-code configuration management + +(library (jerboa-code core config) + (export load-config + config-ref + config-api-key + config-model + config-provider + config-get-provider-key + *config*) + + (import (chezscheme) + (std text json) + (std os path) + (std os env)) + + ;; Global config state + (define *config* (make-parameter #f)) + + ;; Configuration file locations (in priority order) + (define (config-paths) + (list + (path-join (current-directory) "jerboa-code.json") + (path-join (or (getenv "XDG_CONFIG_HOME") + (path-join (getenv "HOME") ".config")) + "jerboa-code" "config.json") + (path-join (getenv "HOME") ".jerboa-code.json"))) + + ;; Find first existing config file + (define (find-config-file) + (let loop ([paths (config-paths)]) + (cond + [(null? paths) #f] + [(file-exists? (car paths)) (car paths)] + [else (loop (cdr paths))]))) + + ;; Load configuration from file and environment + (define (load-config) + (let* ([file (find-config-file)] + [file-config (if file + (call-with-input-file file read-json) + (make-hashtable string-hash string=?))] + [config (merge-env-config file-config)]) + (*config* config) + config)) + + ;; Merge environment variables into config + (define (merge-env-config config) + (let ([providers (or (hashtable-ref config "providers" #f) + (make-hashtable string-hash string=?))]) + ;; Check common API key env vars + (for-each + (lambda (pair) + (let ([env-var (car pair)] + [provider (cdr pair)]) + (let ([key (getenv env-var)]) + (when key + (let ([p (or (hashtable-ref providers provider #f) + (make-hashtable string-hash string=?))]) + (hashtable-set! p "api_key" key) + (hashtable-set! providers provider p)))))) + '(("OPENAI_API_KEY" . "openai") + ("ANTHROPIC_API_KEY" . "anthropic") + ("GOOGLE_API_KEY" . "google") + ("OPENROUTER_API_KEY" . "openrouter"))) + (hashtable-set! config "providers" providers) + config)) + + ;; Get config value by key path + (define (config-ref . keys) + (let loop ([obj (*config*)] [keys keys]) + (cond + [(null? keys) obj] + [(not (hashtable? obj)) #f] + [else + (loop (hashtable-ref obj (car keys) #f) (cdr keys))]))) + + ;; Get API key for a provider + (define (config-get-provider-key provider) + (config-ref "providers" provider "api_key")) + + ;; Get default model + (define (config-model) + (or (config-ref "model") + "claude-sonnet-4-20250514")) + + ;; Get default provider + (define (config-provider) + (or (config-ref "provider") + "anthropic")) + + ;; Get API key for default provider + (define (config-api-key) + (config-get-provider-key (config-provider))) +) new file mode 100644 --- /dev/null +++ b/lib/jerboa-code/core/message.sls @@ -0,0 +1,100 @@ +#!chezscheme +;;; jerboa-code message types + +(library (jerboa-code core message) + (export make-user-message + make-assistant-message + make-tool-call + make-tool-result + make-system-message + message-role + message-content + message-tool-calls + message-tool-call-id + message->json + json->message + tool-call-id + tool-call-name + tool-call-arguments) + + (import (chezscheme) + (std text json) + (std misc uuid)) + + ;; Message record type + (define-record-type message + (fields role content tool-calls tool-call-id)) + + ;; Tool call record type + (define-record-type tool-call + (fields id name arguments)) + + ;; Create a user message + (define (make-user-message content) + (make-message "user" content #f #f)) + + ;; Create an assistant message (optionally with tool calls) + (define (make-assistant-message content . tool-calls) + (make-message "assistant" content + (if (null? tool-calls) #f (car tool-calls)) + #f)) + + ;; Create a tool call + (define (make-tool-call name arguments) + (make-tool-call (uuid-generate) name arguments)) + + ;; Create a tool result message + (define (make-tool-result call-id content) + (make-message "tool" content #f call-id)) + + ;; Create a system message + (define (make-system-message content) + (make-message "system" content #f #f)) + + ;; Convert message to JSON for API + (define (message->json msg) + (let ([ht (make-hashtable string-hash string=?)]) + (hashtable-set! ht "role" (message-role msg)) + (when (message-content msg) + (hashtable-set! ht "content" (message-content msg))) + (when (message-tool-calls msg) + (hashtable-set! ht "tool_calls" + (map tool-call->json (message-tool-calls msg)))) + (when (message-tool-call-id msg) + (hashtable-set! ht "tool_call_id" (message-tool-call-id msg))) + ht)) + + ;; Convert tool call to JSON + (define (tool-call->json tc) + (let ([ht (make-hashtable string-hash string=?)] + [fn (make-hashtable string-hash string=?)]) + (hashtable-set! ht "id" (tool-call-id tc)) + (hashtable-set! ht "type" "function") + (hashtable-set! fn "name" (tool-call-name tc)) + (hashtable-set! fn "arguments" + (if (string? (tool-call-arguments tc)) + (tool-call-arguments tc) + (json-object->string (tool-call-arguments tc)))) + (hashtable-set! ht "function" fn) + ht)) + + ;; Parse message from JSON response + (define (json->message json) + (let ([role (hashtable-ref json "role" "assistant")] + [content (hashtable-ref json "content" #f)] + [tool-calls-json (hashtable-ref json "tool_calls" #f)] + [tool-call-id (hashtable-ref json "tool_call_id" #f)]) + (make-message + role + content + (and tool-calls-json (map json->tool-call tool-calls-json)) + tool-call-id))) + + ;; Parse tool call from JSON + (define (json->tool-call json) + (let ([fn (hashtable-ref json "function" #f)]) + (make-tool-call + (hashtable-ref json "id" "") + (hashtable-ref fn "name" "") + (hashtable-ref fn "arguments" "{}")))) +) new file mode 100644 --- /dev/null +++ b/lib/jerboa-code/core/session.sls @@ -0,0 +1,196 @@ +#!chezscheme +;;; jerboa-code session persistence + +(library (jerboa-code core session) + (export session-init-db + session-create + session-load + session-list + session-add-message + session-get-messages + session-update-title + session-delete + session-id + session-title + session-created + session-messages) + + (import (chezscheme) + (std db sqlite) + (std misc uuid) + (std os path) + (std os env) + (std text json) + (jerboa-code core message)) + + ;; Session record type + (define-record-type session + (fields id title created messages)) + + ;; Get database path + (define (db-path) + (let ([data-dir (or (getenv "XDG_DATA_HOME")