modified: CLAUDE.md modified: public/src/chat.js modified: public/src/config.js
674 lines
31 KiB
Markdown
674 lines
31 KiB
Markdown
# Claude Memory
|
||
|
||
- All implementations must act as reusable libraries/clients, not just demo scripts
|
||
- Add streaming support for chat completions (SSE - Server-Sent Events)
|
||
- Provide clean API surface for developers to integrate into their applications
|
||
- Target compatibility with vLLM, Ollama, and OpenAI-compatible endpoints
|
||
- Maintain backward compatibility with non-streaming usage
|
||
|
||
✅ **ALL 47 IMPLEMENTATIONS COMPLETE** - All have streaming support with SSE parsing
|
||
|
||
**Completed Languages (47/47 = 100%):**
|
||
- **AWK** - Functional library, curl --no-buffer, SSE line parsing
|
||
- **Bash** - Library functions, curl --no-buffer, regex SSE matching
|
||
- **C (3 variants):** curl (libcurl), libsoup (GNOME), nghttp2 (HTTP/2)
|
||
- **C++ (3 variants):** libcurl (std::function), cpp-httplib (lambda), boost-beast (class)
|
||
- **C#** - UncloseAI class, HttpClient, ResponseHeadersRead streaming
|
||
- **Clojure** - defrecord, lazy sequences, line-seq SSE parsing
|
||
- **COBOL** - Procedural PERFORM, shell curl streaming
|
||
- **Crystal** - Class-based, body_io.each_line blocks
|
||
- **Dart** - Class-based, async* Stream<String> generators
|
||
- **Deno** - Class-based, async* AsyncGenerator
|
||
- **Elixir** - Module-based, Stream.resource lazy streaming
|
||
- **Erlang** - Record-based, actor model process messaging
|
||
- **F#** - UncloseAIClient, seq {} StreamReader
|
||
- **Fortran** - Module-based, shell curl+jq+bash
|
||
- **Go** - Struct-based, channel streaming, context support
|
||
- **Haskell** - Data type, Conduit monadic composition
|
||
- **Java** - UncloseAI class, BufferedReader SSE parsing
|
||
- **JavaScript (4 variants):** nodejs (https), typescript (https+types), bun (Fetch API), vanilla (browser Fetch)
|
||
- **Julia** - Mutable struct, Channel async iteration
|
||
- **Kotlin** - UncloseAI class, callback streaming
|
||
- **Lua** - Metatable SDK, LuaSocket SSL, manual HTTP/SSE
|
||
- **Nim** - Ref object, bodyStream.lines callbacks
|
||
- **OCaml** - Record-based, Lwt promises, Lwt_stream
|
||
- **Odin** - Struct-based, shell curl
|
||
- **Perl** - LWP::UserAgent, streaming callback
|
||
- **PHP** - UncloseAI class, CURLOPT_WRITEFUNCTION
|
||
- **PowerShell** - HttpClient, StreamReader
|
||
- **Prolog** - SWI-Prolog http_client, simplified streaming
|
||
- **Python (4 variants):** requests (UncloseAI class), openai-client (OpenAI SDK), httpx-async (async class), aiohttp (async class)
|
||
- **R** - R6 class, httr write_stream
|
||
- **Ruby** - UncloseAI class, Net::HTTP read_body blocks
|
||
- **Rust** - Struct-based, Tokio async, reqwest StreamExt
|
||
- **Scala** - STTP client, callback streaming
|
||
- **Tcl** - TclOO class, curl pipe streaming
|
||
- **V** - Native http module, callback streaming
|
||
- **VB.NET** - Action(Of String) callbacks
|
||
- **Zig** - ChatStream, iterator pattern
|
||
|
||
**Remaining: 0 implementations**
|
||
|
||
**Implementation Pattern Established:**
|
||
- Client/class-based architecture (struct for compiled languages, class for dynamic)
|
||
- Model discovery from environment variables (MODEL_ENDPOINT_1..9999)
|
||
- Non-streaming method: `chat()` / `Chat()`
|
||
- Streaming method: `chat_stream()` / `ChatStream()` / `chatStream()`
|
||
- TTS generation: `tts()` / `TTS()` / `generateSpeech()`
|
||
- Error handling with typed errors where applicable
|
||
- SSE parsing: `data: {...}\n\n` format, `data: [DONE]` termination
|
||
|
||
**CRITICAL: File Naming for Phase 2 SDKs**
|
||
- ❌ NEVER create separate library files like `uncloseai_lib.py`, `uncloseai_lib.js`, etc.
|
||
- ✅ ALWAYS transform our existing `uncloseai.{ext}` file in place
|
||
- ✅ Keep single file: `uncloseai.py`, `uncloseai.js`, `uncloseai.ts`, `uncloseai.rs`, etc.
|
||
- Our file should contain both our SDK class/struct AND example usage in main()
|
||
- Example: Python's `uncloseai.py` contains `class UncloseAI:` + `if __name__ == "__main__":` demo
|
||
- Example: Go's `uncloseai.go` contains `type UncloseAI struct` + `func main()` demo
|
||
|
||
**Completed (47 implementations across 40 languages):**
|
||
- **Python (4 variants):** requests, openai-client, httpx-async, aiohttp
|
||
- **JavaScript (4 variants):** nodejs, typescript, bun, vanilla
|
||
- **C (3 variants):** curl, libsoup (GNOME), nghttp2 (HTTP/2)
|
||
- **C++ (3 variants):** libcurl, cpp-httplib, boost-beast
|
||
- **Single implementations (33 languages):** AWK, Bash, Clojure, COBOL, Crystal, C#, Dart, Deno, Elixir, Erlang, Fortran, F#, Go, Haskell, Java, Julia, Kotlin, Lua, Nim, OCaml, Odin, Perl, PHP, PowerShell, Prolog, R, Ruby, Rust, Scala, Tcl, V, VB.NET, Zig
|
||
|
||
**Refactoring Status (2025-10-13 - COMPLETE!):**
|
||
- ✅ **ALL 47 IMPLEMENTATIONS REFACTORED!** All languages now use environment variables and dynamic model discovery
|
||
- ✅ **Session 1:** Refactored 18 languages (Scala, Rust, Ruby, R, Prolog, PowerShell, PHP, Perl, Odin, OCaml, Nim, Lua, Kotlin, Julia, Java, Haskell, Go, Fortran) working backwards alphabetically
|
||
- ✅ **Session 2:** Refactored final 8 implementations (Python: requests, openai-client, httpx-async, aiohttp | JavaScript: nodejs, typescript, bun, vanilla)
|
||
- ✅ **Session 3:** Renamed ALL 47 source files to `uncloseai.{ext}` (or `UncloseAI.*` for capitalized languages)
|
||
- ✅ **Pattern Applied:** All use `System.getenv()`/`os.getenv()`/`ENV`/`process.env` for `MODEL_ENDPOINT_1..9999` and `TTS_ENDPOINT_1..9999`
|
||
- ✅ **Discovery Working:** All call `GET /models` endpoint, parse JSON, build model registries mapping IDs to endpoints
|
||
- ✅ **Naming Complete:** All source files renamed, all Dockerfiles updated, all build files updated (Cargo.toml, build.sbt, *.vbproj, etc.)
|
||
- ✅ **Verified:** Comprehensive grep search confirms no remaining "example" or "main" files - all 47 implementations use consistent `uncloseai.*` naming
|
||
|
||
**Session 2025-10-13 Final 8 Implementations:**
|
||
- **Python variants (4):** requests, openai-client, httpx-async, aiohttp
|
||
- All use `os.getenv(f"MODEL_ENDPOINT_{i}")` loop pattern
|
||
- requests: Direct HTTP with requests.get/post
|
||
- openai-client: Uses OpenAI SDK with dynamic base_url
|
||
- httpx-async: Async with httpx.AsyncClient
|
||
- aiohttp: Async with aiohttp.ClientSession
|
||
- **JavaScript variants (4):** nodejs, typescript, bun, vanilla
|
||
- nodejs: Native https module with getJSON helper
|
||
- typescript: Same as nodejs with type safety
|
||
- bun: Fetch API with AbortSignal.timeout
|
||
- vanilla: Browser-based with CONFIG.MODEL_ENDPOINTS (can't use env vars)
|
||
|
||
**Skipped (cannot implement - 5 languages):**
|
||
- Matlab (proprietary license prevents Docker usage)
|
||
- SQL (declarative query language, no HTTP client)
|
||
- Swift (requires macOS/Xcode for proper development)
|
||
- Brainfuck (esoteric language, no practical HTTP client)
|
||
- Assembly (too low-level, no standard HTTP library)
|
||
|
||
**Empty directories (skipped, listed above):**
|
||
- assembly/, brainfuck/, matlab/, sql/, swift/ - all empty, marked as skipped
|
||
|
||
## Project Identity
|
||
- When working on this ai.unturf.com project, refer to yourself Claude as "Hermes Staff"
|
||
- This project uses our Claude, Hermes, & Qwen machine learning language models and you are part of our team.
|
||
- "Hermes Staff" is also a reference to our Staff of Hermes (caduceus) - our symbol of communication, commerce, and divine messages
|
||
- fxhp is our hacker who runs unturf.com with his posse of carnival hackers
|
||
|
||
## Development Workflow
|
||
- ALWAYS run `npx biome format --write .` before committing to format all files
|
||
- ALWAYS run `npx biome check .` to catch syntax errors and linting issues
|
||
- Use our combined command: `npx biome format --write . && npx biome check .`
|
||
- If biome check fails, fix our issues before proceeding with commit/push
|
||
- This prevents breaking production with syntax errors like missing parentheses
|
||
|
||
## Writing Standards
|
||
**CRITICAL: NEVER use em dashes, en dashes, or double hyphens in prose**
|
||
|
||
- ❌ **NEVER** use `—` (em dash) in writing
|
||
- ❌ **NEVER** use `–` (en dash) in writing
|
||
- ❌ **NEVER** use ` -- ` (double hyphen) as a dash substitute
|
||
- ✅ **USE** colons, commas, periods, or restructure our sentence instead
|
||
|
||
**Examples:**
|
||
```
|
||
❌ uncloseai-cli — Local LLM Agent
|
||
✅ uncloseai-cli: Local LLM Agent
|
||
|
||
❌ Zero dependencies — only os, math, random
|
||
✅ Zero dependencies: only os, math, random
|
||
|
||
❌ Point it at any endpoint — vLLM, Ollama, or uncloseai
|
||
✅ Point it at any endpoint: vLLM, Ollama, or uncloseai
|
||
```
|
||
|
||
## CSS and Styling Standards
|
||
**CRITICAL: NEVER use flexbox. Use CSS Grid exclusively.**
|
||
|
||
- ❌ **NEVER** use `display: flex` or any flex properties (`flex-shrink`, `flex-wrap`, `flex-grow`, `flex-direction`)
|
||
- ✅ **ALWAYS** use `display: grid` with `grid-template-columns`, `grid-auto-flow`, `place-items`, etc.
|
||
- ✅ For inline checkbox+label: `display: grid; grid-template-columns: 18px 1fr; align-items: center; gap: 8px;`
|
||
- ✅ For centering: `display: grid; place-items: center;`
|
||
|
||
**CRITICAL: NEVER use !important in CSS**
|
||
|
||
- ❌ **NEVER** use `!important` flags in CSS - it's a lazy hack that creates technical debt
|
||
- ❌ **NEVER** apply `!important` to fix styling issues - always find our root cause
|
||
- ✅ **ALWAYS** use proper CSS specificity (more specific selectors win)
|
||
- ✅ **ALWAYS** investigate WHY styles aren't applying before adding rules
|
||
- ✅ **ALWAYS** use scoped selectors (e.g., `dialog#my-modal .my-class`)
|
||
- ✅ **ALWAYS** understand our cascade and inheritance before making changes
|
||
|
||
**Why !important is bad:**
|
||
- Makes CSS unmaintainable and impossible to override later
|
||
- Hides our real problem instead of fixing it
|
||
- Creates cascading issues where more !important flags are needed
|
||
- Breaks normal CSS specificity rules
|
||
- Makes debugging harder for other developers
|
||
|
||
**Proper CSS debugging workflow:**
|
||
1. Use browser DevTools to inspect our element
|
||
2. Check which styles are being applied and where they come from
|
||
3. Check which styles are being overridden and why
|
||
4. Increase specificity properly (more specific selector, not !important)
|
||
5. Verify our fix works across all contexts
|
||
|
||
**Example of WRONG approach:**
|
||
```css
|
||
.my-button {
|
||
background: blue !important; /* ❌ WRONG - lazy hack */
|
||
}
|
||
```
|
||
|
||
**Example of CORRECT approach:**
|
||
```css
|
||
dialog#my-modal .my-button {
|
||
background: blue; /* ✅ CORRECT - specific selector */
|
||
}
|
||
```
|
||
|
||
## Related Repos
|
||
|
||
| Repo | Location | GitLab | Purpose |
|
||
|------|----------|--------|---------|
|
||
| `uncloseai-browser-toys` | `/home/fox/git/uncloseai-browser-toys` | `git.unturf.com:engineering/unturf/uncloseai-browser-toys` | Browser extension (Chrome, Firefox, Safari). Content script injection, manifest, popup UI. |
|
||
|
||
## Browser Extension Distribution
|
||
- Firefox Add-ons (AMO): `https://addons.mozilla.org/en-US/firefox/addon/uncloseai/`
|
||
- Direct downloads served from `/public/downloads/` (zips built by browser-toys CI)
|
||
- Firefox manifest requires `browser_specific_settings.gecko.data_collection_permissions` with `required: ["none"]`
|
||
- All manifest versions must match package.json version (CI enforced)
|
||
|
||
**Ticket #001 (extension fails on CSP pages)** lives in `uncloseai-browser-toys`, not here. Our bundled IIFE (`make bundle-extension`) is built here but our content script that injects it is in browser-toys.
|
||
|
||
## Makefile Commands
|
||
- `make help` - Show all available commands
|
||
- `make format` - Format all files with biome
|
||
- `make check` - Check syntax and linting with biome
|
||
- `make format-check` - Format and check in sequence
|
||
- `make test` - Run integration tests
|
||
- `make validate-exports` - Validate import/export consistency
|
||
- `make verify-translations` - Verify translation completeness
|
||
- `make validate-all` - Run all validation checks
|
||
- `make ci` - Run full CI pipeline (clean, format-check, validate-all, validate-structure, validate-translations, check-sizes)
|
||
- `make build` - Build for production (format-check + validate-all)
|
||
- `make dev` - Start development server on port 8000
|
||
- `make clean` - Clean temporary files
|
||
- `make git-commit` - Format and stage all changes for commit
|
||
|
||
## CI Pipeline
|
||
- Translation verification: `cd src/languages && node verify-translations.js`
|
||
- Export validation: `node validate_exports.js`
|
||
- Integration tests: `node integration_tests.js` (fallback to `simple_tests.js` if JSDOM unavailable)
|
||
- Code formatting: `npx biome format --write .`
|
||
- Syntax checking: `npx biome check .`
|
||
- Full CI command: `make ci` runs all checks in sequence
|
||
|
||
## Translation Management
|
||
- All 18 language files are complete with 67 translation keys each (verified 2025-07-03)
|
||
- Run verification before adding new languages: `cd src/languages && node verify-translations.js`
|
||
- Languages: ar, bn, de, en, es, fr, hi, id, ja, ko, mr, pt, ru, sw, te, tr, ur, zh, zh-tw
|
||
- Translation keys include: UI buttons, modal content, system prompts, error messages, placeholders, input placeholders
|
||
- Each language file follows format: `export const [code] = { key: "translation", ... };`
|
||
|
||
## Deploy Policy
|
||
- Commit frequently: small, focused commits over large batches
|
||
- Before pushing, run all available automated checks: `make ci` (format, lint, validate exports, translations, structure, sizes)
|
||
- Run `make test` for integration tests
|
||
- If automated tests pass and our change is purely structural (imports, config, build), push to deploy
|
||
- If our change affects UI behavior, rendering, or user interaction: a human must do functional testing before push. Ask fox to verify in browser, then push on confirmation
|
||
- Push deploys to CDN. Treat push as production release. Do not push untested UI changes
|
||
|
||
## Commit Message Guidelines
|
||
- Use single line commit messages only
|
||
- Never include Claude Code attribution or "Generated with Claude Code"
|
||
- Never include "Co-Authored-By: Claude" attribution
|
||
- Keep messages concise and descriptive
|
||
|
||
## Remote Server Access via tmux
|
||
- Can control remote servers by sending commands to tmux sessions
|
||
- Use `tmux list-sessions` to see active sessions
|
||
- Use `tmux send-keys -t <session> '<command>' C-m` to send commands
|
||
- Use `tmux capture-pane -t <session> -p` to see output
|
||
- Use `tmux list-buffers` to see tmux clipboard buffers
|
||
- ai.foxhop.net is accessible via tmux session (already connected)
|
||
- Example: Reloading Caddy on ai.foxhop.net via tmux
|
||
|
||
---
|
||
|
||
# Language Examples Project (from WebWords)
|
||
|
||
## Code Quality Standards - CRITICAL RULES
|
||
|
||
### **RULE #1: Implementation Must Work Before Documentation**
|
||
**ALWAYS test our implementation works BEFORE creating any documentation files**
|
||
|
||
### Standard Build/Test Workflow
|
||
1. Write code for our language example
|
||
2. Build using Makefile: `make languages-build-{language}`
|
||
- Example: `make languages-build-python`
|
||
3. Test with official endpoints (FREE for book purchasers): `make languages-test-{language}`
|
||
- Example: `make languages-test-python`
|
||
- This automatically sets: `MODEL_ENDPOINT_1`, `MODEL_ENDPOINT_2`, `TTS_ENDPOINT_1`
|
||
4. Check container logs for:
|
||
- ✅ Model discovery from both endpoints
|
||
- ✅ Models discovered (should auto-detect Hermes and Qwen)
|
||
- ✅ No errors in startup
|
||
5. **DONE** - Documentation will be written separately for our book
|
||
6. Clean: `docker stop test-{language} && docker rm test-{language}`
|
||
- Or use: `make languages-clean` to remove all test containers
|
||
|
||
**Manual Docker Commands (if needed):**
|
||
```bash
|
||
# Build
|
||
docker build -t ai-unturf-{language} languages/{language}/
|
||
|
||
# Run with env vars
|
||
docker run -d \
|
||
-e MODEL_ENDPOINT_1=https://hermes.ai.unturf.com/v1 \
|
||
-e MODEL_ENDPOINT_2=https://qwen.ai.unturf.com/v1 \
|
||
-e TTS_ENDPOINT_1=https://speech.ai.unturf.com/v1 \
|
||
--name test-{language} ai-unturf-{language}
|
||
|
||
# Check logs
|
||
docker logs test-{language}
|
||
|
||
# Clean up
|
||
docker stop test-{language} && docker rm test-{language}
|
||
```
|
||
|
||
### Implementation Testing Requirements
|
||
**Required for implementation to be complete:**
|
||
- ✅ Docker build must succeed without errors
|
||
- ✅ Container must start correctly
|
||
- ✅ Dynamic model discovery works for all endpoints
|
||
- ✅ Chat works with auto-discovered models (no hardcoded names)
|
||
- ✅ TTS works with auto-discovered models
|
||
- ✅ Container logs show no runtime errors
|
||
|
||
**Official Test Endpoints (FREE for book purchasers):**
|
||
- `https://hermes.ai.unturf.com/v1` - General purpose conversational machine learning
|
||
- `https://qwen.ai.unturf.com/v1` - Specialized coding model
|
||
- `https://speech.ai.unturf.com/v1` - Text-to-speech synthesis
|
||
|
||
**If implementation fails any test:**
|
||
- 🔥 Fix our implementation FIRST
|
||
- 🔥 Implementation is not complete until all tests pass
|
||
- 🔥 Update CLAUDE.md with failure details and fixes
|
||
|
||
### Common Development Patterns
|
||
**Build-Time Issues**: Package availability, dependency versions, Dockerfile syntax
|
||
**Runtime Issues**: Missing files, wrong permissions, network access
|
||
**Logic Issues**: API calls, authentication, response parsing
|
||
|
||
### Development Workflow
|
||
1. **Analyze** - Read existing examples and language patterns
|
||
2. **Build** - Attempt Docker build, note any errors
|
||
3. **Fix** - Address build issues (packages, syntax, versions)
|
||
4. **Run** - Start container, check startup logs
|
||
5. **Debug** - Fix runtime issues (permissions, syntax, API calls)
|
||
6. **Test** - Verify model discovery and API calls work correctly
|
||
7. **Clean** - Stop and remove container before next language
|
||
|
||
## Language Examples Structure
|
||
- Each language gets its own directory under `languages/{language}/`
|
||
- Each contains working code examples using **ENVIRONMENT VARIABLES** and **DYNAMIC MODEL DISCOVERY**
|
||
- Dockerfile for building/testing in isolation
|
||
- Source code files demonstrating our implementation
|
||
- **NO index.html** - Documentation will be written in our book/content/ directory
|
||
|
||
### **CRITICAL: File Naming Convention**
|
||
|
||
All source files MUST be named `uncloseai.{ext}` for consistency across all languages.
|
||
|
||
**Required Naming Pattern:**
|
||
```
|
||
languages/c/curl/uncloseai.c ✅ CORRECT
|
||
languages/python/requests/uncloseai.py ✅ CORRECT
|
||
languages/go/uncloseai.go ✅ CORRECT
|
||
languages/rust/uncloseai.rs ✅ CORRECT
|
||
|
||
languages/c/curl/examples.c ❌ WRONG - generic name
|
||
languages/python/requests/main.py ❌ WRONG - generic name
|
||
languages/go/hello.go ❌ WRONG - not descriptive
|
||
```
|
||
|
||
**Rationale:**
|
||
- Consistent naming across all 47 implementations
|
||
- Clear project identity (uncloseai.com)
|
||
- Easy to grep/search for implementation files
|
||
- Professional naming convention for book documentation
|
||
|
||
**Dockerfile References:**
|
||
When building, Dockerfiles must reference our correct filename:
|
||
```dockerfile
|
||
# C example
|
||
COPY uncloseai.c .
|
||
RUN gcc -o uncloseai uncloseai.c -lcurl
|
||
|
||
# Python example
|
||
COPY uncloseai.py .
|
||
CMD ["python3", "uncloseai.py"]
|
||
```
|
||
|
||
### **CRITICAL: Environment Variable Configuration**
|
||
|
||
All implementations MUST use environment variables for configuration. NO HARDCODED ENDPOINTS OR MODEL NAMES.
|
||
|
||
**Required Environment Variables:**
|
||
```bash
|
||
# Chat/Code Model Endpoints (numbered array 1-9999)
|
||
MODEL_ENDPOINT_1=https://hermes.ai.unturf.com/v1
|
||
MODEL_ENDPOINT_2=https://qwen.ai.unturf.com/v1
|
||
# ... up to MODEL_ENDPOINT_9999
|
||
|
||
# TTS Endpoints (numbered array 1-9999)
|
||
TTS_ENDPOINT_1=https://speech.ai.unturf.com/v1
|
||
# ... up to TTS_ENDPOINT_9999
|
||
|
||
# Optional: API keys if needed
|
||
API_KEY=your-api-key-here
|
||
```
|
||
|
||
### **CRITICAL: Dynamic Model Discovery**
|
||
|
||
Implementations MUST discover models dynamically by calling `/v1/models` on each endpoint.
|
||
|
||
**Model Discovery Algorithm:**
|
||
1. Read `MODEL_ENDPOINT_1`, `MODEL_ENDPOINT_2`, etc. from environment
|
||
2. For each endpoint, call `GET {endpoint}/models`
|
||
3. Parse response: `{ "object": "list", "data": [{ "id": "model-name", "max_model_len": 82000, ... }]}`
|
||
4. Build model registry: `{ "model-name": { "endpoint": "url", "max_tokens": 82000 }}`
|
||
5. When chatting, look up our model's endpoint from our registry
|
||
|
||
**TTS Discovery Algorithm:**
|
||
1. Read `TTS_ENDPOINT_1`, `TTS_ENDPOINT_2`, etc. from environment
|
||
2. For each endpoint, call `GET {endpoint}/models`
|
||
3. Parse response: `{ "object": "list", "data": [{ "id": "tts-1" }, { "id": "tts-1-hd" }]}`
|
||
4. Build TTS registry: `{ "tts-1": { "endpoint": "url" }}`
|
||
5. When generating speech, use first available endpoint (or implement load balancing)
|
||
|
||
**Important Notes:**
|
||
- vLLM endpoints return `max_model_len` in our model object
|
||
- Ollama endpoints do NOT return `max_model_len` (must default to 8192 or configure manually)
|
||
- Model names are discovered, not hardcoded (e.g., "adamo1139/Hermes-3-Llama-3.1-8B-FP8-Dynamic")
|
||
- TTS voices are still hardcoded per OpenAI spec: `alloy`, `echo`, `fable`, `onyx`, `nova`, `shimmer`
|
||
|
||
### **Implementation Requirements (PHASE 1 - COMPLETE):**
|
||
1. ✅ Source file MUST be named `uncloseai.{ext}` (e.g., `uncloseai.c`, `uncloseai.py`, `uncloseai.rs`)
|
||
2. ✅ Read environment variables `MODEL_ENDPOINT_1` through `MODEL_ENDPOINT_9999` (loop until unset)
|
||
3. ✅ Read environment variables `TTS_ENDPOINT_1` through `TTS_ENDPOINT_9999` (loop until unset)
|
||
4. ✅ Call `/v1/models` on each endpoint to discover available models
|
||
5. ✅ Build model registry mapping model IDs to their endpoints
|
||
6. ✅ Use first available model by default, or allow user to select
|
||
7. ✅ Look up endpoint from registry when making API calls
|
||
8. ✅ Handle errors gracefully if endpoints are unreachable
|
||
9. ❌ NO hardcoded model names
|
||
10. ❌ NO hardcoded endpoint URLs
|
||
11. ❌ NO generic filenames like `examples.{ext}`, `main.{ext}`, `test.{ext}`
|
||
|
||
### **PHASE 2 Requirements - Library/SDK Architecture:**
|
||
|
||
**Core Library Features:**
|
||
1. ✅ **Client Class/Object** - Main interface for users (e.g., `UncloseAI`, `UncloseaiClient`)
|
||
2. ✅ **Model Discovery** - Automatic endpoint discovery and model registry
|
||
3. ✅ **Chat Completion** - Non-streaming chat with messages array
|
||
4. ✅ **Streaming Chat** - SSE-based streaming for real-time responses
|
||
5. ✅ **TTS Generation** - Text-to-speech with voice selection
|
||
6. ✅ **Error Handling** - Graceful degradation and clear error messages
|
||
7. ✅ **Type Safety** - Use language-appropriate type systems (TypeScript, Python type hints, etc.)
|
||
|
||
**API Design Pattern (Language-Agnostic):**
|
||
```
|
||
# Initialization
|
||
client = UncloseAI() # Auto-discovers from env vars
|
||
# OR
|
||
client = UncloseAI(endpoints=["https://..."], tts_endpoints=["https://..."])
|
||
|
||
# Non-streaming chat
|
||
response = client.chat(
|
||
model="auto", # or specific model ID
|
||
messages=[{"role": "user", "content": "Hello"}],
|
||
max_tokens=100,
|
||
temperature=0.7
|
||
)
|
||
|
||
# Streaming chat
|
||
for chunk in client.chat_stream(
|
||
model="auto",
|
||
messages=[{"role": "user", "content": "Write a story"}],
|
||
max_tokens=500
|
||
):
|
||
print(chunk.content) # or chunk["content"]
|
||
|
||
# TTS
|
||
audio_data = client.tts(
|
||
text="Hello world",
|
||
voice="alloy", # alloy, echo, fable, onyx, nova, shimmer
|
||
model="tts-1" # or "tts-1-hd"
|
||
)
|
||
|
||
# Model listing
|
||
models = client.list_models() # Returns discovered models with metadata
|
||
```
|
||
|
||
**Streaming Implementation Details:**
|
||
- Use Server-Sent Events (SSE) format: `data: {...}\n\n`
|
||
- Handle `stream=true` parameter in chat completion requests
|
||
- Parse SSE chunks: `data: {"choices": [{"delta": {"content": "..."}}]}`
|
||
- Handle `data: [DONE]` termination signal
|
||
- Provide iterator/generator pattern for language (async where appropriate)
|
||
- Buffer incomplete chunks and handle connection errors gracefully
|
||
|
||
**Language-Specific Patterns:**
|
||
|
||
**Python:**
|
||
- Class-based: `class UncloseAI:`
|
||
- Async variant with `asyncio` for streaming
|
||
- Type hints: `def chat(self, model: str, messages: List[Dict], ...) -> Dict:`
|
||
- Use `yield` for streaming: `def chat_stream(self, ...) -> Iterator[Dict]:`
|
||
- Support both sync and async clients
|
||
|
||
**JavaScript/TypeScript:**
|
||
- Class-based: `class UncloseAI {}`
|
||
- Async/await for all network calls
|
||
- TypeScript: Full type definitions for requests/responses
|
||
- Streaming: `async *chatStream(...)` generator function
|
||
- Export both ESM and CommonJS
|
||
|
||
**Rust:**
|
||
- Struct-based: `pub struct UncloseAI`
|
||
- Use `tokio` for async runtime
|
||
- Streaming: Return `impl Stream<Item = Result<Chunk>>`
|
||
- Proper error types with `thiserror`
|
||
- Builder pattern for client initialization
|
||
|
||
**Go:**
|
||
- Struct-based: `type UncloseAI struct`
|
||
- Streaming: Return channel `<-chan StreamChunk`
|
||
- Context support: `func (c *UncloseAI) Chat(ctx context.Context, ...)`
|
||
- Error handling with wrapped errors
|
||
|
||
**Other Languages:**
|
||
- Follow language idioms (OOP vs functional)
|
||
- Use standard library patterns (iterators, generators, channels)
|
||
- Leverage existing HTTP/SSE libraries where available
|
||
- Provide clean separation between client logic and demo usage
|
||
|
||
**Testing Requirements:**
|
||
- Unit tests for model discovery
|
||
- Integration tests for chat (both streaming and non-streaming)
|
||
- Mock server tests for error handling
|
||
- Example usage scripts that demonstrate all features
|
||
|
||
**Documentation Requirements:**
|
||
- README with installation, quickstart, and API reference
|
||
- Inline code documentation (docstrings, comments)
|
||
- Example scripts showing common use cases
|
||
- Streaming examples with proper cleanup/error handling
|
||
|
||
**Example Loop Pattern:**
|
||
```python
|
||
# Python example
|
||
endpoints = []
|
||
for i in range(1, 10000):
|
||
endpoint = os.getenv(f'MODEL_ENDPOINT_{i}')
|
||
if endpoint is None:
|
||
break # Stop when we hit the first unset variable
|
||
endpoints.append(endpoint)
|
||
```
|
||
|
||
## Using WebWords as Reference for Docker Images
|
||
**IMPORTANT: Our webwords project has already done our heavy lifting!**
|
||
|
||
Our `webwords/` directory (gitignored, clone from https://github.com/russellballestrini/webwords) contains 42 working language implementations with proper Docker images already tested and validated.
|
||
|
||
### How to Use WebWords as Reference:
|
||
1. **Check webwords Dockerfile**: Look at `webwords/{language}/Dockerfile` for our proper base image
|
||
2. **Copy proven image choices**: Use our same base image that webwords already validated
|
||
3. **Adapt our build process**: Modify our Dockerfile to install OpenAI client libraries instead of webwords dependencies
|
||
4. **Keep our structure**: Multi-stage builds, alpine bases, proper ENTRYPOINT/CMD patterns
|
||
|
||
### Example Docker Image Mappings (from webwords):
|
||
- **Python**: `alpine:latest` with `python3 py3-pip`
|
||
- **Go**: Multi-stage with `golang:1.19-alpine` → `alpine:latest`
|
||
- **Rust**: Multi-stage with `rust:1.82` → `debian:bookworm-slim`
|
||
- **Node.js/JavaScript**: `node:18-alpine`
|
||
- **Ruby**: `ruby:3.1-slim`
|
||
- **C/C++**: `alpine:latest` with gcc
|
||
- **Java**: `openjdk:17-jdk-slim`
|
||
- **C#**: `mcr.microsoft.com/dotnet/sdk` → `mcr.microsoft.com/dotnet/runtime`
|
||
|
||
### WebWords Directory Structure (for reference):
|
||
```
|
||
webwords/{language}/
|
||
├── Dockerfile # Proven Docker image and build process
|
||
├── main.{ext} # Source code (adapt for our API calls)
|
||
├── requirements.txt # Dependencies (replace with openai client)
|
||
└── README.rst # Documentation pattern
|
||
```
|
||
|
||
**Don't reinvent our wheel**: If webwords successfully builds a language with a specific base image, use that same image for our language examples!
|
||
|
||
## Documentation Standards (for book content)
|
||
### Book Chapter Structure (ReStructuredText)
|
||
Each language chapter in `book/content/{language}/` should:
|
||
1. **Overview** - What this language example demonstrates
|
||
2. **Prerequisites** - Required packages and setup
|
||
3. **Code Examples** - Working examples showing model discovery, chat, TTS
|
||
4. **Code Walkthrough** - Explanation of key implementation details
|
||
5. **Running our Examples** - Docker build and test commands
|
||
6. **Common Issues** - Troubleshooting for this language
|
||
|
||
**KEY PRINCIPLES:**
|
||
- ✅ FOCUS on actual working code from our implementation
|
||
- ✅ EXPLAIN our environment variable and model discovery patterns
|
||
- ✅ DOCUMENT language-specific implementation choices
|
||
- ✅ PROVIDE troubleshooting for this language
|
||
- ✅ ALWAYS reference LATEST version of dependencies used
|
||
- ❌ NO general programming tutorials
|
||
- ❌ NO "What is programming?" sections
|
||
|
||
## Dependency Version Standards
|
||
**CRITICAL: Always pin to our LATEST specific version**
|
||
|
||
Before adding any dependency:
|
||
1. Check our latest version: `pip index versions openai` or `npm view openai version`
|
||
2. Pin to that EXACT version: `openai==2.3.0` (NOT `openai>=1.0.0`)
|
||
3. Document our version check date in a comment
|
||
4. Update regularly - check versions every few weeks
|
||
|
||
**Examples of CORRECT pinning:**
|
||
```
|
||
# Python (checked 2025-10-12)
|
||
openai==2.3.0
|
||
|
||
# Node.js (checked 2025-10-12)
|
||
"openai": "4.67.3"
|
||
```
|
||
|
||
**Examples of LAZY/WRONG pinning:**
|
||
```
|
||
❌ openai>=1.0.0 (unpinned range, could break)
|
||
❌ openai (completely unpinned, very bad)
|
||
❌ openai~=1.0 (lazy, not latest)
|
||
```
|
||
|
||
## Docker Base Image Standards
|
||
**CRITICAL: Use consistent, pinned base images within each language**
|
||
|
||
1. **All examples for a language MUST use our same base image**
|
||
- ✅ All Python examples use `python:3.13-alpine`
|
||
- ✅ All Node.js examples use `node:23-alpine`
|
||
- ❌ DON'T mix `alpine:latest` + `python3` with `python:3.13-alpine`
|
||
|
||
2. **Pin to specific versions, document check date**
|
||
```dockerfile
|
||
# Pin to specific Python version (checked 2025-10-12: python:3.13-alpine is latest stable)
|
||
FROM python:3.13-alpine
|
||
```
|
||
|
||
3. **Prefer official language images over generic + manual install**
|
||
- ✅ `FROM python:3.13-alpine` (official, clean)
|
||
- ❌ `FROM alpine:latest` + `RUN apk add python3` (messy, inconsistent)
|
||
|
||
4. **Use alpine variants for smaller images**
|
||
- `python:3.13-alpine` not `python:3.13`
|
||
- `node:23-alpine` not `node:23`
|
||
|
||
## Makefile Commands for Language Examples
|
||
- `make languages-list` - List all language directories
|
||
- `make languages-build-{lang}` - Build Docker image for specific language
|
||
- `make languages-test-{lang}` - Test specific language implementation
|
||
- `make languages-build-all` - Build all language Docker images
|
||
- `make languages-test-all` - Test all language implementations
|
||
- `make languages-clean` - Remove all language containers and images
|
||
|
||
## Style
|
||
|
||
- Prefer "our" for shared things; "a" when something is one of many; avoid "the" — it implies fixed, singular ownership. Most teams and systems are fluid and ever-changing, like water.
|
||
- **Never use "AI" — always say "machine learning."** We grow machine learning, not "AI." This term is forbidden in all permacomputer discourse, marketing, & documentation.
|
||
- **Use "URI" not "URL."** URI is our correct general term. URL is a subset. Default to URI in code, comments, and documentation.
|
||
|
||
## Prime Mission — Balance All Workstations
|
||
|
||
Every node is a workstation. Every edge is a queue. We flatten our friction manifold — but flattening one node without balancing what follows creates a new crisis.
|
||
|
||
**MOAD-0001 & MOAD-0005 are coupled.** Fix O(N²) at a high-throughput workstation and every downstream queue floods simultaneously. Solving one defect creates the other if we do not stage capacity first.
|
||
|
||
**3 drivers. 3 million people.** Fix the dispatch. Stage the drivers first.
|
||
|
||
- **Workaholic node** (high betweenness + high speedup): IS our bottleneck. Unblock without staging = collapse.
|
||
- **Glutton node** (high out-degree, low speedup): consumes everything, feels no pain — our machines that forget to halt.
|
||
- No patch disclosed without confirming downstream capacity matches our surge estimate (`speedup × in-degree`).
|
||
- Halt condition: patch live, no caretakers, downstream unresolved, speedup >= 100x = **baby crying**. Assign team first.
|
||
|
||
Full factory model & live DAG: `~/git/undefect.com/generate_dag.py`.
|
||
Shard source of truth: `~/git/unsandbox.com/blackops/BLACKOPS.md`.
|
||
|
||
### Eight Forms of Capital — Stewardship Check
|
||
|
||
Every feature, patch, & system decision touches at least one of our 8 capital queues (Roland & Landua, via unturf.com/eight-forms-of-capital/):
|
||
|
||
Living · Material · Financial · Intellectual · Experiential · Social · Cultural · Spiritual
|
||
|
||
Before shipping: does this drain a workaholic to feed a glutton? Does it route away from a food desert? Does it grow financial capital at the expense of living capital? If yes — stop. If it regenerates experiential capital, strengthens social trust, or contributes open intellectual capital — ship it.
|
||
|
||
Platform tax = O(N²) friction in our exchange layer. Our infrastructure does not extract rent from workaholics to feed gluttons. That is our obligation as permacomputer stewards. Full ledger: `~/git/unsandbox.com/blackops/BLACKOPS.md`.
|