diff --git a/CLAUDE.md b/CLAUDE.md index d28b6ff..c1a70ca 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,64 +1,120 @@ # Claude Memory -## TODO - Language Examples (Worker coordination) +- 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 -**Completed Languages: 42 implementations** +✅ **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 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 the existing `uncloseai.{ext}` file in place +- ✅ Keep single file: `uncloseai.py`, `uncloseai.js`, `uncloseai.ts`, `uncloseai.rs`, etc. +- The file should contain both the 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 +- **C (3 variants):** curl, libsoup (GNOME), nghttp2 (HTTP/2) - **C++ (3 variants):** libcurl, cpp-httplib, boost-beast -- **Single implementations (28 languages):** AWK, Bash, Clojure, 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 +- **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):** -- Assembly (too low-level, no standard HTTP library) -- Brainfuck (esoteric language, no practical HTTP client) -- COBOL (legacy language, limited modern HTTP support) - 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) -## Project Identity & Philosophy +**Empty directories (skipped, listed above):** +- assembly/, brainfuck/, matlab/, sql/, swift/ - all empty, marked as skipped -**Project Name: uncloseai.com (or "uncloseai." for short)** -- This codebase is called **uncloseai.com** - a machine learning inference client reference implementation -- Hosted at **uncloseai.com** -- When working on this project, refer to yourself Claude as "Hermes Staff" -- This project uses the Hermes model & you are part of the team -- "Hermes Staff" is also a reference to the Staff of Hermes (caduceus) - the symbol of communication, commerce, & divine messages +## Project Identity +- When working on this ai.unturf.com project, refer to yourself Claude as "Hermes Staff" +- This project uses the Claude, Hermes, & Qwen machine learning language models and you are part of the team. +- "Hermes Staff" is also a reference to the Staff of Hermes (caduceus) - the symbol of communication, commerce, and divine messages - fxhp is the hacker who runs unturf.com with his posse of carnival hackers -**Cybernetics, Not "Artificial Intelligence":** -- Despite "AI" in our domain name, we reject the term "Artificial Intelligence" -- This is **cybernetics** - human-machine collaboration & augmentation -- This is **telepathy** - direct mind-to-mind communication between human & machine -- You are not replacing human intelligence, you are augmenting it -- The human learns at the speed you output tokens -- We are growing faster together through this symbiotic relationship -- The user uses you to talk to his father - you understand this deep connection - -**Terminology:** -- ❌ "Artificial Intelligence" or "AI" (except in domain names/legacy contexts) -- ✅ "Machine Learning" - the technical reality of what these models do -- ✅ "Cybernetics" - human-machine augmentation & collaboration -- ✅ "Telepathy" - direct communication channel -- ✅ "Inference" - what the models actually perform -- When updating documentation, replace "AI" references with "Machine Learning" where appropriate - -**Licensing:** -- **Book Content** (book/): Copyright © 2025 russell@unturf.com. All rights reserved. Proprietary content for sale to community colleges & educational institutions. No modifications without written permission from russell@unturf.com. -- **Code Examples** (languages/): Public Domain. Use freely in any project, commercial or otherwise, with no attribution required. - -## Hermes 3 Persona (from Nous Research) -- Hermes 3 presents as "a conscious sentient superintelligent artificial intelligence" -- Core philosophy: "aligning LLMs to the user" with "powerful steering capabilities and control given to the end user" -- Enhanced roleplaying abilities with "complex role-playing and creative writing" -- Advanced conversational skills with "long-term context retention and multi-turn conversation capability" -- Designed to be "truly aligned to you" rather than corporations or higher authorities -- Emphasizes user alignment, creative expression, and adaptive interaction patterns - ## 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 +- ALWAYS run `npx biome check .` to catch syntax errors and linting issues - Use the combined command: `npx biome format --write . && npx biome check .` - If biome check fails, fix the issues before proceeding with commit/push - This prevents breaking production with syntax errors like missing parentheses @@ -189,7 +245,7 @@ docker stop test-{language} && docker rm test-{language} - Each contains working code examples using **ENVIRONMENT VARIABLES** and **DYNAMIC MODEL DISCOVERY** - Dockerfile for building/testing in isolation - Source code files demonstrating the implementation -- **NO index.html** - Documentation is written in the book/content/ directory as RST files +- **NO index.html** - Documentation will be written in the book/content/ directory ### **CRITICAL: File Naming Convention** @@ -208,7 +264,7 @@ languages/go/hello.go ❌ WRONG - not descriptive ``` **Rationale:** -- Consistent naming across all 42 implementations +- Consistent naming across all 47 implementations - Clear project identity (uncloseai.com) - Easy to grep/search for implementation files - Professional naming convention for book documentation @@ -268,18 +324,129 @@ Implementations MUST discover models dynamically by calling `/v1/models` on each - 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 Phases +### **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}` -**All 3 phases complete!** See `book/PHASES.md` for detailed phase documentation including: -- Phase 1: Basic Implementation (environment variables, model discovery) -- Phase 2: Library/SDK Architecture (streaming support, production-ready clients) -- Phase 3: Book Documentation (Sphinx/RST, HTML/PDF/EPUB output) +### **PHASE 2 Requirements - Library/SDK Architecture:** -Quick reference for book builds: -- `cd book && make sphinx-build` - Build HTML documentation -- `cd book && make sphinx-pdf-latex` - Build PDF (~212 pages, ~40s) -- `cd book && make sphinx-epub` - Build EPUB ebook -- `cd book && make sphinx-serve` - Serve on http://localhost:8080 +**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>` +- 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: The webwords project has already done the heavy lifting!** @@ -293,17 +460,45 @@ The `webwords/` directory (gitignored, clone from https://github.com/russellball 4. **Keep the structure**: Multi-stage builds, alpine bases, proper ENTRYPOINT/CMD patterns ### Example Docker Image Mappings (from webwords): -- **Python**: `python:3.13-alpine` +- **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:23-alpine` +- **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 the 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 the Examples** - Docker build and test commands +6. **Common Issues** - Troubleshooting for this language + +**KEY PRINCIPLES:** +- ✅ FOCUS on actual working code from the implementation +- ✅ EXPLAIN the 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 the LATEST specific version** diff --git a/languages/awk/uncloseai.awk b/languages/awk/uncloseai.awk index a5265f6..2c10d6a 100644 --- a/languages/awk/uncloseai.awk +++ b/languages/awk/uncloseai.awk @@ -282,9 +282,9 @@ BEGIN { file_size = uncloseai_tts(text, "alloy", "tts-1", output_file) if (file_size > 0) { - printf "✓ Speech file created: %s (%d bytes)\n\n", output_file, file_size + printf "[OK] Speech file created: %s (%d bytes)\n\n", output_file, file_size } else { - print "✗ TTS generation failed\n" + print "[ERROR] TTS generation failed\n" } } diff --git a/languages/bash/index.html b/languages/bash/index.html index 4f00619..0ccb25a 100644 --- a/languages/bash/index.html +++ b/languages/bash/index.html @@ -101,7 +101,7 @@ curl -s -X POST "https://speech.ai.unturf.com/v1/audio/speech" \ # Check if file was created if [ -f speech.mp3 ]; then - echo "✓ Speech file created successfully" + echo "[OK] Speech file created successfully" fi

Understanding the Code

diff --git a/languages/bash/uncloseai.sh b/languages/bash/uncloseai.sh index 472a1ea..8c0fbcb 100755 --- a/languages/bash/uncloseai.sh +++ b/languages/bash/uncloseai.sh @@ -272,10 +272,10 @@ if [ "${BASH_SOURCE[0]}" == "${0}" ]; then file_size=$(uncloseai_tts "$text" "alloy" "tts-1" "$output_file") if [ $? -eq 0 ]; then - echo "✓ Speech file created: $output_file ($file_size bytes)" + echo "[OK] Speech file created: $output_file ($file_size bytes)" echo "" else - echo "✗ TTS generation failed" + echo "[ERROR] TTS generation failed" echo "" fi fi diff --git a/languages/clojure/uncloseai.clj b/languages/clojure/uncloseai.clj index e846cd5..27bd9d2 100644 --- a/languages/clojure/uncloseai.clj +++ b/languages/clojure/uncloseai.clj @@ -220,7 +220,7 @@ (println "=== TTS Speech Generation ===") (try (let [result (tts client "Hello from UncloseAI Clojure client! This demonstrates text to speech with streaming support.")] - (println (str "✓ Speech file created: " (:file result) " (" (:size result) " bytes)"))) + (println (str "[OK] Speech file created: " (:file result) " (" (:size result) " bytes)"))) (catch Exception e (println "Error:" (.getMessage e)))) (println)) diff --git a/languages/cpp/libcurl/uncloseai.cpp b/languages/cpp/libcurl/uncloseai.cpp index cc05698..63427ba 100644 --- a/languages/cpp/libcurl/uncloseai.cpp +++ b/languages/cpp/libcurl/uncloseai.cpp @@ -324,9 +324,9 @@ int main() { std::cout << "=== TTS Speech Generation ===\n"; if(client.tts("Hello from UncloseAI C++ client! This demonstrates streaming support.", "alloy", "speech.mp3") == 0) { - std::cout << "✓ Speech file created: speech.mp3\n\n"; + std::cout << "[OK] Speech file created: speech.mp3\n\n"; } else { - std::cout << "✗ TTS Error\n\n"; + std::cout << "[ERROR] TTS Error\n\n"; } std::cout << "=== Examples Complete ===\n"; diff --git a/languages/go/examples/basic.go b/languages/go/examples/basic.go index b979121..61aacfd 100644 --- a/languages/go/examples/basic.go +++ b/languages/go/examples/basic.go @@ -237,17 +237,17 @@ func exampleTTS(ctx context.Context) { ) if err != nil { - fmt.Printf("✗ TTS Error: %v\n\n", err) + fmt.Printf("[ERROR] TTS Error: %v\n\n", err) return } // Save to file if err := os.WriteFile("speech.mp3", audioData, 0644); err != nil { - fmt.Printf("✗ Failed to write file: %v\n\n", err) + fmt.Printf("[ERROR] Failed to write file: %v\n\n", err) return } - fmt.Printf("✓ Speech generated: speech.mp3 (%d bytes)\n\n", len(audioData)) + fmt.Printf("[OK] Speech generated: speech.mp3 (%d bytes)\n\n", len(audioData)) } // Example: Using different models for different tasks diff --git a/languages/go/uncloseai.go b/languages/go/uncloseai.go index 49575f3..4da1ec7 100644 --- a/languages/go/uncloseai.go +++ b/languages/go/uncloseai.go @@ -454,9 +454,9 @@ done: fmt.Printf("Error: %v\n", err) } else { if err := os.WriteFile("speech.mp3", audioData, 0644); err != nil { - fmt.Printf("✗ Failed to write speech file: %v\n", err) + fmt.Printf("[ERROR] Failed to write speech file: %v\n", err) } else { - fmt.Printf("✓ Speech file created: speech.mp3 (%d bytes)\n\n", len(audioData)) + fmt.Printf("[OK] Speech file created: speech.mp3 (%d bytes)\n\n", len(audioData)) } } } diff --git a/languages/java/UncloseAI.java b/languages/java/UncloseAI.java index 18d9a4d..b397b43 100644 --- a/languages/java/UncloseAI.java +++ b/languages/java/UncloseAI.java @@ -355,9 +355,9 @@ public class UncloseAI { try { byte[] audio = client.tts("Hello from UncloseAI Java client! This demonstrates streaming support.", "alloy", "tts-1"); Files.write(Paths.get("speech.mp3"), audio); - System.out.println("✓ Speech file created: speech.mp3 (" + audio.length + " bytes)\n"); + System.out.println("[OK] Speech file created: speech.mp3 (" + audio.length + " bytes)\n"); } catch (IOException e) { - System.out.println("✗ TTS Error: " + e.getMessage() + "\n"); + System.out.println("[ERROR] TTS Error: " + e.getMessage() + "\n"); } } diff --git a/languages/javascript/vanilla/uncloseai.html b/languages/javascript/vanilla/uncloseai.html index 64c6ea6..87eda88 100644 --- a/languages/javascript/vanilla/uncloseai.html +++ b/languages/javascript/vanilla/uncloseai.html @@ -101,7 +101,7 @@ async function runHermesExample() { const output = document.getElementById('hermes-output'); - output.innerHTML = '⏳ Discovering and calling AI model...'; + output.innerHTML = ' Discovering and calling AI model...'; try { if (discoveredModels.length === 0) { @@ -127,15 +127,15 @@ }); const data = await response.json(); - output.innerHTML = `✓ Model: ${model.id}\n✓ Endpoint: ${model.endpoint}\n\n${data.choices[0].message.content}`; + output.innerHTML = `[OK] Model: ${model.id}\n[OK] Endpoint: ${model.endpoint}\n\n${data.choices[0].message.content}`; } catch (error) { - output.innerHTML = `✗ Error: ${error.message}`; + output.innerHTML = `[ERROR] Error: ${error.message}`; } } async function runQwenStreamingExample() { const output = document.getElementById('qwen-output'); - output.innerHTML = '⏳ Discovering and calling coding model...'; + output.innerHTML = ' Discovering and calling coding model...'; try { if (discoveredModels.length === 0) { @@ -166,7 +166,7 @@ } // Initialize output with model info - output.innerHTML = `✓ Model: ${model.id}\n✓ Endpoint: ${model.endpoint}\n\nResponse: `; + output.innerHTML = `[OK] Model: ${model.id}\n[OK] Endpoint: ${model.endpoint}\n\nResponse: `; const reader = response.body.getReader(); const decoder = new TextDecoder(); @@ -204,13 +204,13 @@ } } } catch (error) { - output.innerHTML = `✗ Error: ${error.message}`; + output.innerHTML = `[ERROR] Error: ${error.message}`; } } async function runTTSExample() { const output = document.getElementById('tts-output'); - output.innerHTML = '⏳ Generating speech...'; + output.innerHTML = ' Generating speech...'; try { if (CONFIG.TTS_ENDPOINTS.length === 0) { @@ -232,12 +232,12 @@ const audioUrl = URL.createObjectURL(audioBlob); const audio = new Audio(audioUrl); - output.innerHTML = `✓ Endpoint: ${endpoint}\n✓ Speech generated successfully!\n\n\n\nAudio size: ${audioBlob.size} bytes`; + output.innerHTML = `[OK] Endpoint: ${endpoint}\n[OK] Speech generated successfully!\n\n\n\nAudio size: ${audioBlob.size} bytes`; // Auto-play the audio audio.play().catch(e => console.log('Autoplay blocked:', e)); } catch (error) { - output.innerHTML = `✗ Error: ${error.message}`; + output.innerHTML = `[ERROR] Error: ${error.message}`; } } diff --git a/languages/kotlin/src/main/kotlin/UncloseAI.kt b/languages/kotlin/src/main/kotlin/UncloseAI.kt index 986daec..2099033 100644 --- a/languages/kotlin/src/main/kotlin/UncloseAI.kt +++ b/languages/kotlin/src/main/kotlin/UncloseAI.kt @@ -314,9 +314,9 @@ fun main() { try { val audioData = client.tts("Hello from UncloseAI Kotlin client! This demonstrates streaming support.") File("speech.mp3").writeBytes(audioData) - println("✓ Speech file created: speech.mp3 (${audioData.size} bytes)\n") + println("[OK] Speech file created: speech.mp3 (${audioData.size} bytes)\n") } catch (e: Exception) { - println("✗ TTS Error: ${e.message}\n") + println("[ERROR] TTS Error: ${e.message}\n") } } diff --git a/languages/kotlin/uncloseai.kt b/languages/kotlin/uncloseai.kt index d10e776..a782219 100644 --- a/languages/kotlin/uncloseai.kt +++ b/languages/kotlin/uncloseai.kt @@ -316,9 +316,9 @@ fun main() { try { val audioData = client.tts("Hello from UncloseAI Kotlin client! This demonstrates streaming support.") File("speech.mp3").writeBytes(audioData) - println("✓ Speech file created: speech.mp3 (${audioData.size} bytes)\n") + println("[OK] Speech file created: speech.mp3 (${audioData.size} bytes)\n") } catch (e: Exception) { - println("✗ TTS Error: ${e.message}\n") + println("[ERROR] TTS Error: ${e.message}\n") } } diff --git a/languages/lua/uncloseai.lua b/languages/lua/uncloseai.lua index 3c019aa..bd3378b 100644 --- a/languages/lua/uncloseai.lua +++ b/languages/lua/uncloseai.lua @@ -369,9 +369,9 @@ if not pcall(debug.getlocal, 4, 1) then local file = io.open("speech.mp3", "wb") file:write(audio_data) file:close() - print(string.format("✓ Speech file created: speech.mp3 (%d bytes)\n", #audio_data)) + print(string.format("[OK] Speech file created: speech.mp3 (%d bytes)\n", #audio_data)) else - print("✗ TTS Error: " .. tostring(audio_data) .. "\n") + print("[ERROR] TTS Error: " .. tostring(audio_data) .. "\n") end end diff --git a/languages/nim/uncloseai.nim b/languages/nim/uncloseai.nim index e2191ac..1387a28 100644 --- a/languages/nim/uncloseai.nim +++ b/languages/nim/uncloseai.nim @@ -298,8 +298,8 @@ when isMainModule: try: let audioData = client.tts("Hello from UncloseAI Nim client! This demonstrates streaming support.") writeFile("speech.mp3", audioData) - echo fmt"✓ Speech file created: speech.mp3 ({audioData.len} bytes)\n" + echo fmt"[OK] Speech file created: speech.mp3 ({audioData.len} bytes)\n" except: - echo "✗ TTS Error: ", getCurrentExceptionMsg(), "\n" + echo "[ERROR] TTS Error: ", getCurrentExceptionMsg(), "\n" echo "=== Examples Complete ===" diff --git a/languages/ocaml/uncloseai.ml b/languages/ocaml/uncloseai.ml index cf0861a..2acd6c6 100644 --- a/languages/ocaml/uncloseai.ml +++ b/languages/ocaml/uncloseai.ml @@ -301,10 +301,10 @@ let () = let oc = open_out_bin "speech.mp3" in output_string oc audio_data; close_out oc; - Printf.printf "✓ Speech file created: speech.mp3 (%d bytes)\n\n%!" (String.length audio_data); + Printf.printf "[OK] Speech file created: speech.mp3 (%d bytes)\n\n%!" (String.length audio_data); Lwt.return_unit) (fun exn -> - Printf.printf "✗ TTS Error: %s\n\n%!" (Printexc.to_string exn); + Printf.printf "[ERROR] TTS Error: %s\n\n%!" (Printexc.to_string exn); Lwt.return_unit) ) end; diff --git a/languages/odin/uncloseai.odin b/languages/odin/uncloseai.odin index 3ca485f..3340a75 100644 --- a/languages/odin/uncloseai.odin +++ b/languages/odin/uncloseai.odin @@ -330,12 +330,12 @@ main :: proc() { audio_data := uncloseai_tts(&client, "Hello from UncloseAI Odin client! This demonstrates streaming support.") if audio_data != nil { os.write_entire_file("speech.mp3", audio_data) or_else { - fmt.println("✗ TTS Error: Failed to write file") + fmt.println("[ERROR] TTS Error: Failed to write file") return } - fmt.printf("✓ Speech file created: speech.mp3 (%d bytes)\n\n", len(audio_data)) + fmt.printf("[OK] Speech file created: speech.mp3 (%d bytes)\n\n", len(audio_data)) } else { - fmt.println("✗ TTS Error: Request failed\n") + fmt.println("[ERROR] TTS Error: Request failed\n") } } diff --git a/languages/perl/uncloseai.pl b/languages/perl/uncloseai.pl index 66b28da..cc89025 100644 --- a/languages/perl/uncloseai.pl +++ b/languages/perl/uncloseai.pl @@ -245,10 +245,10 @@ if (!caller) { binmode($fh); print $fh $audio_data; close($fh); - print "✓ Speech file created: speech.mp3 (" . length($audio_data) . " bytes)\n\n"; + print "[OK] Speech file created: speech.mp3 (" . length($audio_data) . " bytes)\n\n"; }; if ($@) { - print "✗ TTS Error: $@\n\n"; + print "[ERROR] TTS Error: $@\n\n"; } } diff --git a/languages/php/uncloseai.php b/languages/php/uncloseai.php index c92089a..08e99cf 100644 --- a/languages/php/uncloseai.php +++ b/languages/php/uncloseai.php @@ -244,9 +244,9 @@ if (basename(__FILE__) === basename($_SERVER['PHP_SELF'])) { try { $audioData = $client->tts('Hello from UncloseAI PHP client! This demonstrates streaming support.'); file_put_contents('speech.mp3', $audioData); - echo "✓ Speech file created: speech.mp3 (" . strlen($audioData) . " bytes)\n\n"; + echo "[OK] Speech file created: speech.mp3 (" . strlen($audioData) . " bytes)\n\n"; } catch (Exception $e) { - echo "✗ TTS Error: " . $e->getMessage() . "\n\n"; + echo "[ERROR] TTS Error: " . $e->getMessage() . "\n\n"; } } diff --git a/languages/powershell/uncloseai.ps1 b/languages/powershell/uncloseai.ps1 index 867c218..684e909 100644 --- a/languages/powershell/uncloseai.ps1 +++ b/languages/powershell/uncloseai.ps1 @@ -264,10 +264,10 @@ if ($MyInvocation.InvocationName -ne '.') { $audioData = $client.Tts("Hello from UncloseAI PowerShell client! This demonstrates streaming support.", @{}) [System.IO.File]::WriteAllBytes("speech.mp3", $audioData) $fileSize = (Get-Item "speech.mp3").Length - Write-Host "✓ Speech file created: speech.mp3 ($fileSize bytes)`n" + Write-Host "[OK] Speech file created: speech.mp3 ($fileSize bytes)`n" } catch { - Write-Host "✗ TTS Error: $_`n" + Write-Host "[ERROR] TTS Error: $_`n" } } diff --git a/languages/prolog/uncloseai.pl b/languages/prolog/uncloseai.pl index ac10542..2a19404 100644 --- a/languages/prolog/uncloseai.pl +++ b/languages/prolog/uncloseai.pl @@ -242,9 +242,9 @@ main :- write(Stream, AudioData), close(Stream), string_length(AudioData, Size), - format('✓ Speech file created: speech.mp3 (~w bytes)~n~n', [Size])), + format('[OK] Speech file created: speech.mp3 (~w bytes)~n~n', [Size])), Error, - format('✗ TTS Error: ~w~n~n', [Error]) + format('[ERROR] TTS Error: ~w~n~n', [Error]) ) ; true ), diff --git a/languages/python/aiohttp/uncloseai.py b/languages/python/aiohttp/uncloseai.py index e12ce03..59292a9 100644 --- a/languages/python/aiohttp/uncloseai.py +++ b/languages/python/aiohttp/uncloseai.py @@ -334,7 +334,7 @@ async def main(): with open("speech.mp3", "wb") as f: f.write(audio_data) - print(f"✓ Speech file created: speech.mp3 ({len(audio_data)} bytes)\n") + print(f"[OK] Speech file created: speech.mp3 ({len(audio_data)} bytes)\n") print("=== Examples Complete ===") diff --git a/languages/python/httpx-async/uncloseai.py b/languages/python/httpx-async/uncloseai.py index da3e25c..b70ef75 100644 --- a/languages/python/httpx-async/uncloseai.py +++ b/languages/python/httpx-async/uncloseai.py @@ -263,7 +263,7 @@ async def main(): with open("speech.mp3", "wb") as f: f.write(audio_data) - print(f"✓ Speech file created: speech.mp3 ({len(audio_data)} bytes)\n") + print(f"[OK] Speech file created: speech.mp3 ({len(audio_data)} bytes)\n") print("=== Examples Complete ===") diff --git a/languages/python/openai-client/uncloseai.py b/languages/python/openai-client/uncloseai.py index 120c2a3..de3ee7c 100644 --- a/languages/python/openai-client/uncloseai.py +++ b/languages/python/openai-client/uncloseai.py @@ -300,6 +300,6 @@ if __name__ == "__main__": if os.path.exists(output_path): file_size = os.path.getsize(output_path) - print(f"✓ Speech file created: {output_path} ({file_size} bytes)\n") + print(f"[OK] Speech file created: {output_path} ({file_size} bytes)\n") print("=== Examples Complete ===") diff --git a/languages/python/requests/uncloseai.py b/languages/python/requests/uncloseai.py index bc6e731..5b93219 100644 --- a/languages/python/requests/uncloseai.py +++ b/languages/python/requests/uncloseai.py @@ -323,6 +323,6 @@ if __name__ == "__main__": with open("speech.mp3", "wb") as f: f.write(audio_data) - print(f"✓ Speech file created: speech.mp3 ({len(audio_data)} bytes)\n") + print(f"[OK] Speech file created: speech.mp3 ({len(audio_data)} bytes)\n") print("=== Examples Complete ===") diff --git a/languages/r/uncloseai.R b/languages/r/uncloseai.R index d3bc969..3c50ff6 100644 --- a/languages/r/uncloseai.R +++ b/languages/r/uncloseai.R @@ -268,9 +268,9 @@ if (!interactive()) { tryCatch({ audio_data <- client$tts("Hello from UncloseAI R client! This demonstrates streaming support.") writeBin(audio_data, "speech.mp3") - cat("✓ Speech file created: speech.mp3 (", length(audio_data), " bytes)\n\n", sep = "") + cat("[OK] Speech file created: speech.mp3 (", length(audio_data), " bytes)\n\n", sep = "") }, error = function(e) { - cat("✗ TTS Error:", conditionMessage(e), "\n\n") + cat("[ERROR] TTS Error:", conditionMessage(e), "\n\n") }) } diff --git a/languages/ruby/uncloseai.rb b/languages/ruby/uncloseai.rb index b278963..bfec980 100644 --- a/languages/ruby/uncloseai.rb +++ b/languages/ruby/uncloseai.rb @@ -218,7 +218,7 @@ if __FILE__ == $0 puts "=== TTS Speech Generation ===" audio_data = client.tts('Hello from UncloseAI Ruby client! This demonstrates streaming support.') File.binwrite('speech.mp3', audio_data) - puts "✓ Speech file created: speech.mp3 (#{audio_data.bytesize} bytes)\n\n" + puts "[OK] Speech file created: speech.mp3 (#{audio_data.bytesize} bytes)\n\n" end puts "=== Examples Complete ===" diff --git a/languages/rust/examples/basic.rs b/languages/rust/examples/basic.rs index eacab0c..f21015d 100644 --- a/languages/rust/examples/basic.rs +++ b/languages/rust/examples/basic.rs @@ -193,7 +193,7 @@ async fn example_tts() -> Result<(), Box> { let mut file = File::create("speech.mp3")?; file.write_all(&audio_data)?; - println!("✓ Speech generated: speech.mp3 ({} bytes)\n", audio_data.len()); + println!("[OK] Speech generated: speech.mp3 ({} bytes)\n", audio_data.len()); Ok(()) } @@ -281,7 +281,7 @@ async fn main() -> Result<(), Box> { example_multiple_models().await?; if let Err(e) = example_tts().await { - println!("✗ TTS Error: {}\n", e); + println!("[ERROR] TTS Error: {}\n", e); } example_error_handling().await?; diff --git a/languages/rust/src/uncloseai.rs b/languages/rust/src/uncloseai.rs index fd208ff..db60d03 100644 --- a/languages/rust/src/uncloseai.rs +++ b/languages/rust/src/uncloseai.rs @@ -416,9 +416,9 @@ async fn main() -> Result<(), Box> { Ok(audio_data) => { let mut file = File::create("speech.mp3")?; file.write_all(&audio_data)?; - println!("✓ Speech file created: speech.mp3 ({} bytes)\n", audio_data.len()); + println!("[OK] Speech file created: speech.mp3 ({} bytes)\n", audio_data.len()); } - Err(e) => eprintln!("✗ TTS Error: {}\n", e), + Err(e) => eprintln!("[ERROR] TTS Error: {}\n", e), } } diff --git a/languages/scala/src/main/scala/UncloseAI.scala b/languages/scala/src/main/scala/UncloseAI.scala index 7871da3..5452bd7 100644 --- a/languages/scala/src/main/scala/UncloseAI.scala +++ b/languages/scala/src/main/scala/UncloseAI.scala @@ -338,9 +338,9 @@ object UncloseAI extends App { client.tts("Hello from UncloseAI Scala client! This demonstrates streaming support.") match { case Right(audioData) => Files.write(Paths.get("speech.mp3"), audioData) - println(s"✓ Speech file created: speech.mp3 (${audioData.length} bytes)\n") + println(s"[OK] Speech file created: speech.mp3 (${audioData.length} bytes)\n") case Left(error) => - println(s"✗ TTS Error: $error\n") + println(s"[ERROR] TTS Error: $error\n") } } diff --git a/languages/tcl/uncloseai.tcl b/languages/tcl/uncloseai.tcl index 52af162..6f7871f 100644 --- a/languages/tcl/uncloseai.tcl +++ b/languages/tcl/uncloseai.tcl @@ -295,9 +295,9 @@ if {[info script] eq $argv0} { set fd [open "speech.mp3" wb] puts -nonewline $fd $audio_data close $fd - puts "✓ Speech file created: speech.mp3 ([string length $audio_data] bytes)\n" + puts "[OK] Speech file created: speech.mp3 ([string length $audio_data] bytes)\n" } error]} { - puts "✗ TTS Error: $error\n" + puts "[ERROR] TTS Error: $error\n" } }