modified: .gitignore

modified:   CLAUDE.md
	modified:   Makefile
	new file:   languages/awk/Dockerfile
	new file:   languages/awk/uncloseai.awk
	modified:   languages/bash/Dockerfile
	deleted:    languages/bash/examples.sh
	new file:   languages/bash/uncloseai.sh
	new file:   languages/c/curl/Dockerfile
	new file:   languages/c/curl/Makefile
	new file:   languages/c/curl/index.html
	new file:   languages/c/curl/uncloseai.c
	new file:   languages/c/libh2o/Dockerfile
	new file:   languages/c/libh2o/Makefile
	new file:   languages/c/libh2o/uncloseai.c
	new file:   languages/c/nghttp2/Dockerfile
	new file:   languages/c/nghttp2/Makefile
	new file:   languages/c/nghttp2/uncloseai.c
	new file:   languages/clojure/Dockerfile
	new file:   languages/clojure/deps.edn
	new file:   languages/clojure/uncloseai.clj
	new file:   languages/cobol/Dockerfile
	new file:   languages/cobol/discover.sh
	new file:   languages/cobol/hermes.sh
	new file:   languages/cobol/qwen.sh
	new file:   languages/cobol/tts.sh
	new file:   languages/cobol/uncloseai.cob
	new file:   languages/cpp/boost-beast/Dockerfile
	new file:   languages/cpp/boost-beast/Makefile
	new file:   languages/cpp/boost-beast/uncloseai.cpp
	new file:   languages/cpp/cpp-httplib/Dockerfile
	new file:   languages/cpp/cpp-httplib/Makefile
	new file:   languages/cpp/cpp-httplib/uncloseai.cpp
	new file:   languages/cpp/libcurl/Dockerfile
	new file:   languages/cpp/libcurl/Makefile
	new file:   languages/cpp/libcurl/uncloseai.cpp
	new file:   languages/crystal/Dockerfile
	new file:   languages/crystal/uncloseai.cr
	new file:   languages/csharp/Dockerfile
	new file:   languages/csharp/Uncloseai.cs
	new file:   languages/csharp/csharp.csproj
	new file:   languages/dart/Dockerfile
	new file:   languages/dart/bin/uncloseai.dart
	new file:   languages/dart/pubspec.yaml
	new file:   languages/deno/Dockerfile
	new file:   languages/deno/uncloseai.ts
	new file:   languages/elixir/Dockerfile
	new file:   languages/elixir/lib/uncloseai.ex
	new file:   languages/elixir/mix.exs
	new file:   languages/elixir/run.exs
	new file:   languages/erlang/Dockerfile
	new file:   languages/erlang/rebar.config
	new file:   languages/erlang/src/uncloseai.app.src
	new file:   languages/erlang/src/uncloseai.erl
	new file:   languages/fortran/Dockerfile
	new file:   languages/fortran/uncloseai.f90
	new file:   languages/fsharp/Dockerfile
	new file:   languages/fsharp/Uncloseai.fs
	new file:   languages/fsharp/fsharp.fsproj
	new file:   languages/go/Dockerfile
	new file:   languages/go/README.md
	new file:   languages/go/examples/basic.go
	new file:   languages/go/go.mod
	new file:   languages/go/uncloseai.go
	new file:   languages/go/uncloseai/uncloseai.go
	new file:   languages/haskell/Dockerfile
	new file:   languages/haskell/UncloseAI.hs
	new file:   languages/haskell/uncloseai.cabal
	new file:   languages/java/Dockerfile
	new file:   languages/java/UncloseAI.java
	new file:   languages/javascript/bun/Dockerfile
	new file:   languages/javascript/bun/uncloseai.ts
	new file:   languages/javascript/nodejs/Dockerfile
	new file:   languages/javascript/nodejs/README.md
	new file:   languages/javascript/nodejs/package.json
	new file:   languages/javascript/nodejs/uncloseai.js
	new file:   languages/javascript/typescript/Dockerfile
	new file:   languages/javascript/typescript/package.json
	new file:   languages/javascript/typescript/tsconfig.json
	new file:   languages/javascript/typescript/uncloseai.ts
	new file:   languages/javascript/vanilla/Dockerfile
	new file:   languages/javascript/vanilla/uncloseai.html
	new file:   languages/julia/Dockerfile
	new file:   languages/julia/Project.toml
	new file:   languages/julia/src/uncloseai.jl
	new file:   languages/kotlin/Dockerfile
	new file:   languages/kotlin/build.gradle.kts
	new file:   languages/kotlin/src/main/kotlin/UncloseAI.kt
	new file:   languages/kotlin/uncloseai.kt
	new file:   languages/lua/Dockerfile
	new file:   languages/lua/uncloseai.lua
	new file:   languages/nim/Dockerfile
	new file:   languages/nim/uncloseai.nim
	new file:   languages/ocaml/Dockerfile
	new file:   languages/ocaml/dune
	new file:   languages/ocaml/dune-project
	new file:   languages/ocaml/uncloseai.ml
	new file:   languages/odin/Dockerfile
	new file:   languages/odin/uncloseai.odin
	new file:   languages/perl/Dockerfile
	new file:   languages/perl/uncloseai.pl
	new file:   languages/php/Dockerfile
	new file:   languages/php/uncloseai.php
	new file:   languages/powershell/Dockerfile
	new file:   languages/powershell/uncloseai.ps1
	new file:   languages/prolog/Dockerfile
	new file:   languages/prolog/uncloseai.pl
	new file:   languages/python/aiohttp/Dockerfile
	new file:   languages/python/aiohttp/requirements.txt
	new file:   languages/python/aiohttp/uncloseai.py
	modified:   languages/python/httpx-async/Dockerfile
	deleted:    languages/python/httpx-async/examples.py
	new file:   languages/python/httpx-async/uncloseai.py
	modified:   languages/python/openai-client/Dockerfile
	deleted:    languages/python/openai-client/examples.py
	new file:   languages/python/openai-client/uncloseai.py
	modified:   languages/python/requests/Dockerfile
	deleted:    languages/python/requests/examples.py
	new file:   languages/python/requests/uncloseai.py
	new file:   languages/r/Dockerfile
	new file:   languages/r/uncloseai.R
	new file:   languages/ruby/Dockerfile
	new file:   languages/ruby/README.md
	new file:   languages/ruby/uncloseai.rb
	new file:   languages/rust/Cargo.toml
	new file:   languages/rust/Dockerfile
	new file:   languages/rust/README.md
	new file:   languages/rust/examples/basic.rs
	new file:   languages/rust/src/lib.rs
	new file:   languages/rust/src/uncloseai.rs
	new file:   languages/scala/Dockerfile
	new file:   languages/scala/build.sbt
	new file:   languages/scala/project/build.properties
	new file:   languages/scala/project/plugins.sbt
	new file:   languages/scala/src/main/scala/UncloseAI.scala
	new file:   languages/tcl/Dockerfile
	new file:   languages/tcl/uncloseai.tcl
	new file:   languages/v/Dockerfile
	new file:   languages/v/uncloseai.v
	new file:   languages/v/v.mod
	new file:   languages/vbnet/Dockerfile
	new file:   languages/vbnet/UncloseAI.vb
	new file:   languages/vbnet/UncloseAI.vbproj
	modified:   languages/zig/Dockerfile
	modified:   languages/zig/build.zig
	deleted:    languages/zig/src/main.zig
	new file:   languages/zig/src/uncloseai.zig
This commit is contained in:
Russell Ballestrini 2025-10-14 10:24:16 -04:00
parent bf3f8cfac6
commit 9e3eccb1b9
147 changed files with 20225 additions and 566 deletions

View file

@ -0,0 +1,13 @@
# Pin to specific Python version (checked 2025-10-13: python:3.13-alpine is latest stable)
FROM python:3.13-alpine
WORKDIR /app
COPY requirements.txt /app/requirements.txt
COPY uncloseai.py /app/uncloseai.py
RUN chmod +x /app/uncloseai.py
RUN pip3 install --no-cache-dir -r /app/requirements.txt
CMD ["python3", "/app/uncloseai.py"]

View file

@ -0,0 +1,2 @@
# aiohttp (checked 2025-10-13: aiohttp==3.13.0 is latest)
aiohttp==3.13.0

View file

@ -0,0 +1,343 @@
#!/usr/bin/env python3
"""
UncloseAI - Async Python Client (aiohttp)
A Python async client library for OpenAI-compatible APIs with streaming support
Compatible with vLLM, Ollama, and OpenAI-compatible endpoints
"""
import asyncio
import aiohttp
import json
import os
from typing import List, Dict, Optional, AsyncIterator
class UncloseAI:
"""Async client for OpenAI-compatible API endpoints with streaming support"""
def __init__(
self,
model_endpoints: Optional[List[str]] = None,
tts_endpoints: Optional[List[str]] = None,
api_key: Optional[str] = None,
timeout: float = 30.0
):
"""
Initialize UncloseAI async client
Args:
model_endpoints: List of model endpoint URLs (defaults to MODEL_ENDPOINT_* env vars)
tts_endpoints: List of TTS endpoint URLs (defaults to TTS_ENDPOINT_* env vars)
api_key: Optional API key for authentication
timeout: Request timeout in seconds
"""
self.timeout = timeout
self.api_key = api_key
self.models: List[Dict] = []
self.tts_endpoints: List[str] = []
self._initialized = False
self._model_endpoints = model_endpoints or self._discover_env_endpoints("MODEL_ENDPOINT")
self._tts_endpoints = tts_endpoints or self._discover_env_endpoints("TTS_ENDPOINT")
def _discover_env_endpoints(self, prefix: str) -> List[str]:
"""Discover endpoints from environment variables like PREFIX_1, PREFIX_2, ..."""
endpoints = []
for i in range(1, 10000):
endpoint = os.getenv(f"{prefix}_{i}")
if not endpoint:
break
endpoints.append(endpoint)
return endpoints
async def _ensure_initialized(self):
"""Ensure client is initialized with model discovery"""
if self._initialized:
return
async with aiohttp.ClientSession() as session:
for endpoint in self._model_endpoints:
await self._discover_models_from_endpoint(session, endpoint)
self.tts_endpoints = self._tts_endpoints
self._initialized = True
async def _discover_models_from_endpoint(self, session: aiohttp.ClientSession, endpoint: str) -> None:
"""Discover available models from an endpoint"""
try:
headers = {}
if self.api_key:
headers["Authorization"] = f"Bearer {self.api_key}"
async with session.get(
f"{endpoint}/models",
headers=headers,
timeout=aiohttp.ClientTimeout(total=10)
) as response:
if response.status == 200:
data = await response.json()
for model in data.get("data", []):
model_id = model["id"]
# Filter out modelperm-* and chatcmpl-* entries
if model_id.startswith("modelperm-") or model_id.startswith("chatcmpl-"):
continue
self.models.append({
"id": model_id,
"endpoint": endpoint,
"max_tokens": model.get("max_model_len", 8192)
})
except Exception:
# Silently skip failed endpoints
pass
async def list_models(self) -> List[Dict]:
"""Return list of discovered models with their metadata"""
await self._ensure_initialized()
return self.models.copy()
async def chat(
self,
messages: List[Dict[str, str]],
model: Optional[str] = None,
max_tokens: int = 100,
temperature: float = 0.7,
**kwargs
) -> Dict:
"""
Non-streaming chat completion
Args:
messages: List of message dicts with 'role' and 'content'
model: Model ID (defaults to first available model)
max_tokens: Maximum tokens in response
temperature: Sampling temperature
**kwargs: Additional parameters to pass to the API
Returns:
Response dict with 'choices' containing the completion
"""
await self._ensure_initialized()
model_info = self._get_model_info(model)
headers = {"Content-Type": "application/json"}
if self.api_key:
headers["Authorization"] = f"Bearer {self.api_key}"
payload = {
"model": model_info["id"],
"messages": messages,
"max_tokens": max_tokens,
"temperature": temperature,
"stream": False,
**kwargs
}
async with aiohttp.ClientSession() as session:
async with session.post(
f"{model_info['endpoint']}/chat/completions",
headers=headers,
json=payload,
timeout=aiohttp.ClientTimeout(total=self.timeout)
) as response:
response.raise_for_status()
return await response.json()
async def chat_stream(
self,
messages: List[Dict[str, str]],
model: Optional[str] = None,
max_tokens: int = 500,
temperature: float = 0.7,
**kwargs
) -> AsyncIterator[Dict]:
"""
Streaming chat completion using Server-Sent Events
Args:
messages: List of message dicts with 'role' and 'content'
model: Model ID (defaults to first available model)
max_tokens: Maximum tokens in response
temperature: Sampling temperature
**kwargs: Additional parameters to pass to the API
Yields:
Chunk dicts with 'choices' containing delta content
"""
await self._ensure_initialized()
model_info = self._get_model_info(model)
headers = {"Content-Type": "application/json"}
if self.api_key:
headers["Authorization"] = f"Bearer {self.api_key}"
payload = {
"model": model_info["id"],
"messages": messages,
"max_tokens": max_tokens,
"temperature": temperature,
"stream": True,
**kwargs
}
async with aiohttp.ClientSession() as session:
async with session.post(
f"{model_info['endpoint']}/chat/completions",
headers=headers,
json=payload,
timeout=aiohttp.ClientTimeout(total=self.timeout)
) as response:
response.raise_for_status()
async for line in response.content:
line_str = line.decode('utf-8').strip()
if not line_str:
continue
# SSE format: "data: {...}"
if line_str.startswith('data: '):
data = line_str[6:]
# Check for stream termination
if data.strip() == '[DONE]':
break
try:
chunk = json.loads(data)
yield chunk
except json.JSONDecodeError:
continue
async def tts(
self,
text: str,
voice: str = "alloy",
model: str = "tts-1",
response_format: str = "mp3"
) -> bytes:
"""
Generate speech from text
Args:
text: Input text to convert to speech
voice: Voice name (alloy, echo, fable, onyx, nova, shimmer)
model: TTS model (tts-1 or tts-1-hd)
response_format: Audio format (mp3, opus, aac, flac)
Returns:
Audio data as bytes
"""
await self._ensure_initialized()
if not self.tts_endpoints:
raise ValueError("No TTS endpoints available")
endpoint = self.tts_endpoints[0]
headers = {"Content-Type": "application/json"}
if self.api_key:
headers["Authorization"] = f"Bearer {self.api_key}"
payload = {
"model": model,
"voice": voice,
"input": text,
"response_format": response_format
}
async with aiohttp.ClientSession() as session:
async with session.post(
f"{endpoint}/audio/speech",
headers=headers,
json=payload,
timeout=aiohttp.ClientTimeout(total=self.timeout)
) as response:
response.raise_for_status()
return await response.read()
def _get_model_info(self, model: Optional[str] = None) -> Dict:
"""Get model info by ID or return first available model"""
if not self.models:
raise ValueError("No models available. Check endpoint configuration.")
if model is None:
return self.models[0]
for m in self.models:
if m["id"] == model:
return m
raise ValueError(f"Model '{model}' not found in discovered models")
# Demo usage when run as script
async def main():
print("=== UncloseAI Python Async Client (aiohttp) ===\n")
# Initialize client (auto-discovers from environment)
client = UncloseAI()
models = await client.list_models()
if not models:
print("ERROR: No models discovered. Set environment variables:")
print(" MODEL_ENDPOINT_1, MODEL_ENDPOINT_2, etc.")
return
print(f"Discovered {len(models)} model(s)")
for model in models:
print(f" - {model['id']} (max_tokens: {model['max_tokens']})")
print()
# Non-streaming chat example
print("=== Non-Streaming Chat ===")
response = await client.chat(
messages=[
{"role": "system", "content": "You are a helpful AI assistant."},
{"role": "user", "content": "Explain quantum computing in one sentence."}
],
max_tokens=100
)
print(f"Model: {response['model']}")
print(f"Response: {response['choices'][0]['message']['content']}\n")
# Streaming chat example
print("=== Streaming Chat ===")
model_id = models[1]["id"] if len(models) > 1 else None
print(f"Model: {model_id or models[0]['id']}")
print("Response: ", end="", flush=True)
async for chunk in client.chat_stream(
messages=[
{"role": "system", "content": "You are a coding assistant."},
{"role": "user", "content": "Write an async Python function to fetch multiple URLs"}
],
model=model_id,
max_tokens=200
):
if chunk.get("choices") and len(chunk["choices"]) > 0:
delta = chunk["choices"][0].get("delta", {})
content = delta.get("content", "")
if content:
print(content, end="", flush=True)
print("\n")
# TTS example
if client.tts_endpoints:
print("=== TTS Speech Generation ===")
audio_data = await client.tts(
text="Hello from UncloseAI Python async client with aiohttp! This demonstrates text to speech with streaming support.",
voice="alloy"
)
with open("speech.mp3", "wb") as f:
f.write(audio_data)
print(f"✓ Speech file created: speech.mp3 ({len(audio_data)} bytes)\n")
print("=== Examples Complete ===")
if __name__ == "__main__":
asyncio.run(main())

View file

@ -4,10 +4,10 @@ FROM python:3.13-alpine
WORKDIR /app
COPY requirements.txt /app/requirements.txt
COPY examples.py /app/examples.py
COPY uncloseai.py /app/uncloseai.py
RUN chmod +x /app/examples.py
RUN chmod +x /app/uncloseai.py
RUN pip3 install --no-cache-dir -r /app/requirements.txt
CMD ["python3", "/app/examples.py"]
CMD ["python3", "/app/uncloseai.py"]

View file

@ -1,108 +0,0 @@
#!/usr/bin/env python3
"""
uncloseai.com API Examples in Python using httpx (async)
Demonstrates async HTTP calls to Hermes AI, Qwen Coder, and TTS endpoints
"""
import asyncio
import httpx
async def main():
print("=== uncloseai.com Python (httpx async) Examples ===")
print()
async with httpx.AsyncClient() as client:
# Example 1: Hermes AI Chat
print("1. Hermes AI - General Purpose Chat (async httpx)")
print(" Asking: 'Give a Python Fizzbuzz solution in one line of code?'")
print()
hermes_response = await client.post(
"https://hermes.ai.unturf.com/v1/chat/completions",
headers={
"Content-Type": "application/json",
"Authorization": "Bearer dummy-key"
},
json={
"model": "adamo1139/Hermes-3-Llama-3.1-8B-FP8-Dynamic",
"messages": [{"role": "user", "content": "Give a Python Fizzbuzz solution in one line of code?"}],
"temperature": 0.5,
"max_tokens": 150
},
timeout=30.0
)
if hermes_response.status_code == 200:
print("Response:")
print(hermes_response.json()["choices"][0]["message"]["content"])
else:
print(f"Error: {hermes_response.status_code} - {hermes_response.text}")
print()
print("---")
print()
# Example 2: Qwen 3 Coder
print("2. Qwen 3 Coder - Specialized for Code (async httpx)")
print(" Asking: 'Write an async Python function to fetch multiple URLs'")
print()
qwen_response = await client.post(
"https://qwen.ai.unturf.com/v1/chat/completions",
headers={
"Content-Type": "application/json",
"Authorization": "Bearer dummy-key"
},
json={
"model": "hf.co/unsloth/Qwen3-Coder-30B-A3B-Instruct-GGUF:Q4_K_M",
"messages": [{"role": "user", "content": "Write an async Python function to fetch multiple URLs"}],
"temperature": 0.5,
"max_tokens": 200
},
timeout=30.0
)
if qwen_response.status_code == 200:
print("Response:")
print(qwen_response.json()["choices"][0]["message"]["content"])
else:
print(f"Error: {qwen_response.status_code} - {qwen_response.text}")
print()
print("---")
print()
# Example 3: Text-to-Speech
print("3. Text-to-Speech Generation (async httpx)")
print(" Converting text to speech and saving to speech.mp3")
print()
tts_response = await client.post(
"https://speech.ai.unturf.com/v1/audio/speech",
headers={
"Content-Type": "application/json",
"Authorization": "Bearer YOLO"
},
json={
"model": "tts-1",
"voice": "alloy",
"input": "Hello from async Python with httpx! Today is a wonderful day to build something people love!"
},
timeout=30.0
)
if tts_response.status_code == 200:
with open("speech.mp3", "wb") as f:
f.write(tts_response.content)
import os
file_size = os.path.getsize("speech.mp3")
print(f"✓ Speech file created: speech.mp3 ({file_size} bytes)")
else:
print(f"✗ Failed to create speech file: {tts_response.status_code}")
print()
print("=== Examples Complete ===")
if __name__ == "__main__":
asyncio.run(main())

View file

@ -0,0 +1,272 @@
#!/usr/bin/env python3
"""
UncloseAI - Async Python Client (httpx)
A Python async client for OpenAI-compatible APIs with streaming support
"""
import httpx
import json
import os
import asyncio
from typing import List, Dict, Optional, AsyncIterator
class UncloseAI:
"""Async client for OpenAI-compatible API endpoints with streaming support"""
def __init__(
self,
model_endpoints: Optional[List[str]] = None,
tts_endpoints: Optional[List[str]] = None,
api_key: Optional[str] = None,
timeout: float = 30.0
):
self.timeout = timeout
self.api_key = api_key
self.models: List[Dict] = []
self.tts_endpoints: List[str] = []
self._initialized = False
self._model_endpoints = model_endpoints or self._discover_env_endpoints("MODEL_ENDPOINT")
self._tts_endpoints = tts_endpoints or self._discover_env_endpoints("TTS_ENDPOINT")
def _discover_env_endpoints(self, prefix: str) -> List[str]:
endpoints = []
for i in range(1, 10000):
endpoint = os.getenv(f"{prefix}_{i}")
if not endpoint:
break
endpoints.append(endpoint)
return endpoints
async def _ensure_initialized(self):
if self._initialized:
return
async with httpx.AsyncClient(timeout=self.timeout) as client:
for endpoint in self._model_endpoints:
await self._discover_models_from_endpoint(client, endpoint)
self.tts_endpoints = self._tts_endpoints
self._initialized = True
async def _discover_models_from_endpoint(self, client: httpx.AsyncClient, endpoint: str) -> None:
try:
headers = {}
if self.api_key:
headers["Authorization"] = f"Bearer {self.api_key}"
response = await client.get(f"{endpoint}/models", headers=headers)
if response.status_code == 200:
data = response.json()
for model in data.get("data", []):
self.models.append({
"id": model["id"],
"endpoint": endpoint,
"max_tokens": model.get("max_model_len", 8192)
})
except Exception as e:
print(f"Warning: Failed to discover models from {endpoint}: {e}")
async def list_models(self) -> List[Dict]:
await self._ensure_initialized()
return self.models.copy()
async def chat(
self,
messages: List[Dict[str, str]],
model: Optional[str] = None,
max_tokens: int = 100,
temperature: float = 0.7,
**kwargs
) -> Dict:
await self._ensure_initialized()
model_info = self._get_model_info(model)
headers = {"Content-Type": "application/json"}
if self.api_key:
headers["Authorization"] = f"Bearer {self.api_key}"
payload = {
"model": model_info["id"],
"messages": messages,
"max_tokens": max_tokens,
"temperature": temperature,
"stream": False,
**kwargs
}
async with httpx.AsyncClient(timeout=self.timeout) as client:
response = await client.post(
f"{model_info['endpoint']}/chat/completions",
headers=headers,
json=payload
)
response.raise_for_status()
return response.json()
async def chat_stream(
self,
messages: List[Dict[str, str]],
model: Optional[str] = None,
max_tokens: int = 500,
temperature: float = 0.7,
**kwargs
) -> AsyncIterator[Dict]:
await self._ensure_initialized()
model_info = self._get_model_info(model)
headers = {"Content-Type": "application/json"}
if self.api_key:
headers["Authorization"] = f"Bearer {self.api_key}"
payload = {
"model": model_info["id"],
"messages": messages,
"max_tokens": max_tokens,
"temperature": temperature,
"stream": True,
**kwargs
}
async with httpx.AsyncClient(timeout=self.timeout) as client:
async with client.stream(
"POST",
f"{model_info['endpoint']}/chat/completions",
headers=headers,
json=payload
) as response:
response.raise_for_status()
async for line in response.aiter_lines():
if not line:
continue
if line.startswith('data: '):
data = line[6:]
if data.strip() == '[DONE]':
break
try:
chunk = json.loads(data)
yield chunk
except json.JSONDecodeError:
continue
async def tts(
self,
text: str,
voice: str = "alloy",
model: str = "tts-1",
response_format: str = "mp3"
) -> bytes:
await self._ensure_initialized()
if not self.tts_endpoints:
raise ValueError("No TTS endpoints available")
endpoint = self.tts_endpoints[0]
headers = {"Content-Type": "application/json"}
if self.api_key:
headers["Authorization"] = f"Bearer {self.api_key}"
payload = {
"model": model,
"voice": voice,
"input": text,
"response_format": response_format
}
async with httpx.AsyncClient(timeout=self.timeout) as client:
response = await client.post(
f"{endpoint}/audio/speech",
headers=headers,
json=payload
)
response.raise_for_status()
return response.content
def _get_model_info(self, model: Optional[str] = None) -> Dict:
if not self.models:
raise ValueError("No models available. Check endpoint configuration.")
if model is None:
return self.models[0]
for m in self.models:
if m["id"] == model:
return m
raise ValueError(f"Model '{model}' not found in discovered models")
async def main():
print("=== UncloseAI Python Async Client (httpx) ===\n")
client = UncloseAI()
models = await client.list_models()
if not models:
print("ERROR: No models discovered. Set environment variables:")
print(" MODEL_ENDPOINT_1, MODEL_ENDPOINT_2, etc.")
return
print(f"Discovered {len(models)} model(s)")
for model in models:
print(f" - {model['id']} (max_tokens: {model['max_tokens']})")
print()
# Non-streaming chat
print("=== Non-Streaming Chat ===")
response = await client.chat(
messages=[
{"role": "system", "content": "You are a helpful AI assistant."},
{"role": "user", "content": "Explain quantum computing in one sentence."}
],
max_tokens=100
)
print(f"Model: {response['model']}")
print(f"Response: {response['choices'][0]['message']['content']}\n")
# Streaming chat
print("=== Streaming Chat ===")
model_id = models[1]["id"] if len(models) > 1 else None
print(f"Model: {model_id or models[0]['id']}")
print("Response: ", end="", flush=True)
async for chunk in client.chat_stream(
messages=[
{"role": "system", "content": "You are a coding assistant."},
{"role": "user", "content": "Write a Python async function to fetch multiple URLs"}
],
model=model_id,
max_tokens=200
):
if chunk.get("choices") and len(chunk["choices"]) > 0:
delta = chunk["choices"][0].get("delta", {})
content = delta.get("content", "")
if content:
print(content, end="", flush=True)
print("\n")
# TTS
if client.tts_endpoints:
print("=== TTS Speech Generation ===")
audio_data = await client.tts(
text="Hello from UncloseAI Python async client! This demonstrates text to speech with streaming support.",
voice="alloy"
)
with open("speech.mp3", "wb") as f:
f.write(audio_data)
print(f"✓ Speech file created: speech.mp3 ({len(audio_data)} bytes)\n")
print("=== Examples Complete ===")
if __name__ == "__main__":
asyncio.run(main())

View file

@ -4,10 +4,10 @@ FROM python:3.13-alpine
WORKDIR /app
COPY requirements.txt /app/requirements.txt
COPY examples.py /app/examples.py
COPY uncloseai.py /app/uncloseai.py
RUN chmod +x /app/examples.py
RUN chmod +x /app/uncloseai.py
RUN pip3 install --no-cache-dir -r /app/requirements.txt
CMD ["python3", "/app/examples.py"]
CMD ["python3", "/app/uncloseai.py"]

View file

@ -1,83 +0,0 @@
#!/usr/bin/env python3
"""
uncloseai.com API Examples in Python
Demonstrates Hermes AI, Qwen Coder, and TTS endpoints
"""
from openai import OpenAI
print("=== uncloseai.com Python Examples ===")
print()
# Example 1: Hermes AI Chat (Non-Streaming)
print("1. Hermes AI - General Purpose Chat")
print(" Asking: 'Give a Python Fizzbuzz solution in one line of code?'")
print()
hermes_client = OpenAI(
base_url="https://hermes.ai.unturf.com/v1",
api_key="dummy-key"
)
hermes_response = hermes_client.chat.completions.create(
model="adamo1139/Hermes-3-Llama-3.1-8B-FP8-Dynamic",
messages=[{"role": "user", "content": "Give a Python Fizzbuzz solution in one line of code?"}],
temperature=0.5,
max_tokens=150
)
print("Response:")
print(hermes_response.choices[0].message.content)
print()
print("---")
print()
# Example 2: Qwen 3 Coder - Specialized Coding Model
print("2. Qwen 3 Coder - Specialized for Code")
print(" Asking: 'Write a Python function to validate an email address'")
print()
qwen_client = OpenAI(
base_url="https://qwen.ai.unturf.com/v1",
api_key="dummy-key"
)
qwen_response = qwen_client.chat.completions.create(
model="hf.co/unsloth/Qwen3-Coder-30B-A3B-Instruct-GGUF:Q4_K_M",
messages=[{"role": "user", "content": "Write a Python function to validate an email address"}],
temperature=0.5,
max_tokens=200
)
print("Response:")
print(qwen_response.choices[0].message.content)
print()
print("---")
print()
# Example 3: Text-to-Speech
print("3. Text-to-Speech Generation")
print(" Converting text to speech and saving to speech.mp3")
print()
tts_client = OpenAI(
base_url="https://speech.ai.unturf.com/v1",
api_key="YOLO"
)
with tts_client.audio.speech.with_streaming_response.create(
model="tts-1",
voice="alloy",
input="Hello from Python! Today is a wonderful day to build something people love!"
) as response:
response.stream_to_file("speech.mp3")
import os
if os.path.exists("speech.mp3"):
file_size = os.path.getsize("speech.mp3")
print(f"✓ Speech file created: speech.mp3 ({file_size} bytes)")
else:
print("✗ Failed to create speech file")
print()
print("=== Examples Complete ===")

View file

@ -0,0 +1,305 @@
#!/usr/bin/env python3
"""
UncloseAI - Python Client using OpenAI SDK
A Python client library for OpenAI-compatible APIs with streaming support
Compatible with vLLM, Ollama, and OpenAI-compatible endpoints
"""
from openai import OpenAI
import os
import requests
from typing import List, Dict, Optional, Iterator
class UncloseAI:
"""Client for OpenAI-compatible API endpoints using OpenAI SDK"""
def __init__(
self,
model_endpoints: Optional[List[str]] = None,
tts_endpoints: Optional[List[str]] = None,
api_key: str = "dummy-key",
timeout: int = 30
):
"""
Initialize UncloseAI client with automatic model discovery
Args:
model_endpoints: List of model endpoint URLs (defaults to MODEL_ENDPOINT_* env vars)
tts_endpoints: List of TTS endpoint URLs (defaults to TTS_ENDPOINT_* env vars)
api_key: API key for authentication (default: "dummy-key")
timeout: Request timeout in seconds
"""
self.timeout = timeout
self.api_key = api_key
self.models: List[Dict] = []
self.tts_endpoints: List[str] = []
# Discover endpoints from environment or use provided
if model_endpoints is None:
model_endpoints = self._discover_env_endpoints("MODEL_ENDPOINT")
if tts_endpoints is None:
tts_endpoints = self._discover_env_endpoints("TTS_ENDPOINT")
# Discover models from each endpoint
for endpoint in model_endpoints:
self._discover_models_from_endpoint(endpoint)
self.tts_endpoints = tts_endpoints
def _discover_env_endpoints(self, prefix: str) -> List[str]:
"""Discover endpoints from environment variables like PREFIX_1, PREFIX_2, ..."""
endpoints = []
for i in range(1, 10000):
endpoint = os.getenv(f"{prefix}_{i}")
if not endpoint:
break
endpoints.append(endpoint)
return endpoints
def _discover_models_from_endpoint(self, endpoint: str) -> None:
"""Discover available models from an endpoint"""
try:
response = requests.get(f"{endpoint}/models", timeout=10)
if response.status_code == 200:
data = response.json()
for model in data.get("data", []):
model_id = model["id"]
# Filter out modelperm-* and chatcmpl-* entries
if model_id.startswith("modelperm-") or model_id.startswith("chatcmpl-"):
continue
self.models.append({
"id": model_id,
"endpoint": endpoint,
"max_tokens": model.get("max_model_len", 8192)
})
except Exception:
# Silently skip failed endpoints
pass
def list_models(self) -> List[Dict]:
"""Return list of discovered models with their metadata"""
return self.models.copy()
def chat(
self,
messages: List[Dict[str, str]],
model: Optional[str] = None,
max_tokens: int = 100,
temperature: float = 0.7,
**kwargs
) -> Dict:
"""
Non-streaming chat completion
Args:
messages: List of message dicts with 'role' and 'content'
model: Model ID (defaults to first available model)
max_tokens: Maximum tokens in response
temperature: Sampling temperature
**kwargs: Additional parameters to pass to the API
Returns:
Response dict with 'choices' containing the completion
"""
model_info = self._get_model_info(model)
client = OpenAI(
base_url=f"{model_info['endpoint']}/v1",
api_key=self.api_key,
timeout=self.timeout
)
response = client.chat.completions.create(
model=model_info["id"],
messages=messages,
max_tokens=max_tokens,
temperature=temperature,
**kwargs
)
# Convert OpenAI response to dict format
return {
"id": response.id,
"model": response.model,
"choices": [
{
"index": choice.index,
"message": {
"role": choice.message.role,
"content": choice.message.content
},
"finish_reason": choice.finish_reason
}
for choice in response.choices
],
"usage": {
"prompt_tokens": response.usage.prompt_tokens,
"completion_tokens": response.usage.completion_tokens,
"total_tokens": response.usage.total_tokens
}
}
def chat_stream(
self,
messages: List[Dict[str, str]],
model: Optional[str] = None,
max_tokens: int = 500,
temperature: float = 0.7,
**kwargs
) -> Iterator[str]:
"""
Streaming chat completion using OpenAI SDK
Args:
messages: List of message dicts with 'role' and 'content'
model: Model ID (defaults to first available model)
max_tokens: Maximum tokens in response
temperature: Sampling temperature
**kwargs: Additional parameters to pass to the API
Yields:
Content strings as they arrive
"""
model_info = self._get_model_info(model)
client = OpenAI(
base_url=f"{model_info['endpoint']}/v1",
api_key=self.api_key,
timeout=self.timeout
)
stream = client.chat.completions.create(
model=model_info["id"],
messages=messages,
max_tokens=max_tokens,
temperature=temperature,
stream=True,
**kwargs
)
for chunk in stream:
if chunk.choices[0].delta.content:
yield chunk.choices[0].delta.content
def tts(
self,
text: str,
voice: str = "alloy",
model: str = "tts-1",
output_file: str = "speech.mp3"
) -> str:
"""
Generate speech from text
Args:
text: Input text to convert to speech
voice: Voice name (alloy, echo, fable, onyx, nova, shimmer)
model: TTS model (tts-1 or tts-1-hd)
output_file: Path to save the audio file
Returns:
Path to the saved audio file
"""
if not self.tts_endpoints:
raise ValueError("No TTS endpoints available")
endpoint = self.tts_endpoints[0]
client = OpenAI(
base_url=f"{endpoint}/v1",
api_key=self.api_key,
timeout=self.timeout
)
with client.audio.speech.with_streaming_response.create(
model=model,
voice=voice,
input=text
) as response:
response.stream_to_file(output_file)
return output_file
def _get_model_info(self, model: Optional[str] = None) -> Dict:
"""Get model info by ID or return first available model"""
if not self.models:
raise ValueError("No models available. Check endpoint configuration.")
if model is None:
return self.models[0]
for m in self.models:
if m["id"] == model:
return m
raise ValueError(f"Model '{model}' not found in discovered models")
# Demo usage when run as script
if __name__ == "__main__":
print("=== UncloseAI Python Client (OpenAI SDK) ===\n")
# Initialize client (auto-discovers from environment)
client = UncloseAI()
if not client.models:
print("ERROR: No models discovered. Set environment variables:")
print(" MODEL_ENDPOINT_1, MODEL_ENDPOINT_2, etc.")
exit(1)
print(f"Discovered {len(client.models)} model(s)")
for model in client.models:
print(f" - {model['id']} (max_tokens: {model['max_tokens']})")
print()
# Non-streaming chat example
print("=== Non-Streaming Chat ===")
response = client.chat(
messages=[
{"role": "system", "content": "You are a helpful AI assistant."},
{"role": "user", "content": "Explain quantum computing in one sentence."}
],
max_tokens=100
)
print(f"Model: {response['model']}")
print(f"Response: {response['choices'][0]['message']['content']}\n")
# Streaming chat example
print("=== Streaming Chat ===")
if len(client.models) > 1:
model_id = client.models[1]["id"]
else:
model_id = None
print(f"Model: {model_id or client.models[0]['id']}")
print("Response: ", end="", flush=True)
for content in client.chat_stream(
messages=[
{"role": "system", "content": "You are a coding assistant."},
{"role": "user", "content": "Write a Python function to check if a number is prime"}
],
model=model_id,
max_tokens=200
):
print(content, end="", flush=True)
print("\n")
# TTS example
if client.tts_endpoints:
print("=== TTS Speech Generation ===")
output_path = client.tts(
text="Hello from UncloseAI Python client with OpenAI SDK! This demonstrates text to speech with streaming support.",
voice="alloy",
output_file="speech.mp3"
)
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("=== Examples Complete ===")

View file

@ -4,10 +4,10 @@ FROM python:3.13-alpine
WORKDIR /app
COPY requirements.txt /app/requirements.txt
COPY examples.py /app/examples.py
COPY uncloseai.py /app/uncloseai.py
RUN chmod +x /app/examples.py
RUN chmod +x /app/uncloseai.py
RUN pip3 install --no-cache-dir -r /app/requirements.txt
CMD ["python3", "/app/examples.py"]
CMD ["python3", "/app/uncloseai.py"]

View file

@ -1,100 +0,0 @@
#!/usr/bin/env python3
"""
uncloseai.com API Examples in Python using requests library
Demonstrates direct HTTP calls to Hermes AI, Qwen Coder, and TTS endpoints
"""
import requests
import json
print("=== uncloseai.com Python (requests) Examples ===")
print()
# Example 1: Hermes AI Chat using requests
print("1. Hermes AI - General Purpose Chat (using requests)")
print(" Asking: 'Give a Python Fizzbuzz solution in one line of code?'")
print()
hermes_response = requests.post(
"https://hermes.ai.unturf.com/v1/chat/completions",
headers={
"Content-Type": "application/json",
"Authorization": "Bearer dummy-key"
},
json={
"model": "adamo1139/Hermes-3-Llama-3.1-8B-FP8-Dynamic",
"messages": [{"role": "user", "content": "Give a Python Fizzbuzz solution in one line of code?"}],
"temperature": 0.5,
"max_tokens": 150
}
)
if hermes_response.status_code == 200:
print("Response:")
print(hermes_response.json()["choices"][0]["message"]["content"])
else:
print(f"Error: {hermes_response.status_code} - {hermes_response.text}")
print()
print("---")
print()
# Example 2: Qwen 3 Coder using requests
print("2. Qwen 3 Coder - Specialized for Code (using requests)")
print(" Asking: 'Write a Python function to validate an email address'")
print()
qwen_response = requests.post(
"https://qwen.ai.unturf.com/v1/chat/completions",
headers={
"Content-Type": "application/json",
"Authorization": "Bearer dummy-key"
},
json={
"model": "hf.co/unsloth/Qwen3-Coder-30B-A3B-Instruct-GGUF:Q4_K_M",
"messages": [{"role": "user", "content": "Write a Python function to validate an email address"}],
"temperature": 0.5,
"max_tokens": 200
}
)
if qwen_response.status_code == 200:
print("Response:")
print(qwen_response.json()["choices"][0]["message"]["content"])
else:
print(f"Error: {qwen_response.status_code} - {qwen_response.text}")
print()
print("---")
print()
# Example 3: Text-to-Speech using requests
print("3. Text-to-Speech Generation (using requests)")
print(" Converting text to speech and saving to speech.mp3")
print()
tts_response = requests.post(
"https://speech.ai.unturf.com/v1/audio/speech",
headers={
"Content-Type": "application/json",
"Authorization": "Bearer YOLO"
},
json={
"model": "tts-1",
"voice": "alloy",
"input": "Hello from Python using requests! Today is a wonderful day to build something people love!"
}
)
if tts_response.status_code == 200:
with open("speech.mp3", "wb") as f:
f.write(tts_response.content)
import os
file_size = os.path.getsize("speech.mp3")
print(f"✓ Speech file created: speech.mp3 ({file_size} bytes)")
else:
print(f"✗ Failed to create speech file: {tts_response.status_code}")
print()
print("=== Examples Complete ===")

View file

@ -0,0 +1,328 @@
#!/usr/bin/env python3
"""
uncloseai - Python client library for OpenAI-compatible APIs
Supports streaming and non-streaming chat, model discovery, and TTS
Compatible with vLLM, Ollama, and OpenAI-compatible endpoints
"""
import requests
import json
import os
from typing import List, Dict, Optional, Iterator, Union
class UncloseAI:
"""Client for OpenAI-compatible API endpoints with streaming support"""
def __init__(
self,
model_endpoints: Optional[List[str]] = None,
tts_endpoints: Optional[List[str]] = None,
api_key: Optional[str] = None,
timeout: int = 30
):
"""
Initialize UncloseAI client with automatic model discovery
Args:
model_endpoints: List of model endpoint URLs (defaults to MODEL_ENDPOINT_* env vars)
tts_endpoints: List of TTS endpoint URLs (defaults to TTS_ENDPOINT_* env vars)
api_key: Optional API key for authentication
timeout: Request timeout in seconds
"""
self.timeout = timeout
self.api_key = api_key
self.models: List[Dict] = []
self.tts_endpoints: List[str] = []
# Discover endpoints from environment or use provided
if model_endpoints is None:
model_endpoints = self._discover_env_endpoints("MODEL_ENDPOINT")
if tts_endpoints is None:
tts_endpoints = self._discover_env_endpoints("TTS_ENDPOINT")
# Discover models from each endpoint
for endpoint in model_endpoints:
self._discover_models_from_endpoint(endpoint)
self.tts_endpoints = tts_endpoints
def _discover_env_endpoints(self, prefix: str) -> List[str]:
"""Discover endpoints from environment variables like PREFIX_1, PREFIX_2, ..."""
endpoints = []
for i in range(1, 10000):
endpoint = os.getenv(f"{prefix}_{i}")
if not endpoint:
break
endpoints.append(endpoint)
return endpoints
def _discover_models_from_endpoint(self, endpoint: str) -> None:
"""Discover available models from an endpoint"""
try:
headers = {}
if self.api_key:
headers["Authorization"] = f"Bearer {self.api_key}"
response = requests.get(
f"{endpoint}/models",
headers=headers,
timeout=self.timeout
)
if response.status_code == 200:
data = response.json()
for model in data.get("data", []):
self.models.append({
"id": model["id"],
"endpoint": endpoint,
"max_tokens": model.get("max_model_len", 8192)
})
except Exception as e:
print(f"Warning: Failed to discover models from {endpoint}: {e}")
def list_models(self) -> List[Dict]:
"""Return list of discovered models with their metadata"""
return self.models.copy()
def chat(
self,
messages: List[Dict[str, str]],
model: Optional[str] = None,
max_tokens: int = 100,
temperature: float = 0.7,
**kwargs
) -> Dict:
"""
Non-streaming chat completion
Args:
messages: List of message dicts with 'role' and 'content'
model: Model ID (defaults to first available model)
max_tokens: Maximum tokens in response
temperature: Sampling temperature
**kwargs: Additional parameters to pass to the API
Returns:
Response dict with 'choices' containing the completion
"""
model_info = self._get_model_info(model)
headers = {"Content-Type": "application/json"}
if self.api_key:
headers["Authorization"] = f"Bearer {self.api_key}"
payload = {
"model": model_info["id"],
"messages": messages,
"max_tokens": max_tokens,
"temperature": temperature,
"stream": False,
**kwargs
}
response = requests.post(
f"{model_info['endpoint']}/chat/completions",
headers=headers,
json=payload,
timeout=self.timeout
)
response.raise_for_status()
return response.json()
def chat_stream(
self,
messages: List[Dict[str, str]],
model: Optional[str] = None,
max_tokens: int = 500,
temperature: float = 0.7,
**kwargs
) -> Iterator[Dict]:
"""
Streaming chat completion using Server-Sent Events
Args:
messages: List of message dicts with 'role' and 'content'
model: Model ID (defaults to first available model)
max_tokens: Maximum tokens in response
temperature: Sampling temperature
**kwargs: Additional parameters to pass to the API
Yields:
Chunk dicts with 'choices' containing delta content
"""
model_info = self._get_model_info(model)
headers = {"Content-Type": "application/json"}
if self.api_key:
headers["Authorization"] = f"Bearer {self.api_key}"
payload = {
"model": model_info["id"],
"messages": messages,
"max_tokens": max_tokens,
"temperature": temperature,
"stream": True,
**kwargs
}
response = requests.post(
f"{model_info['endpoint']}/chat/completions",
headers=headers,
json=payload,
timeout=self.timeout,
stream=True
)
response.raise_for_status()
# Parse SSE stream
for line in response.iter_lines():
if not line:
continue
line = line.decode('utf-8')
# SSE format: "data: {...}"
if line.startswith('data: '):
data = line[6:] # Remove "data: " prefix
# Check for stream termination
if data.strip() == '[DONE]':
break
try:
chunk = json.loads(data)
yield chunk
except json.JSONDecodeError:
continue
def tts(
self,
text: str,
voice: str = "alloy",
model: str = "tts-1",
response_format: str = "mp3"
) -> bytes:
"""
Generate speech from text
Args:
text: Input text to convert to speech
voice: Voice name (alloy, echo, fable, onyx, nova, shimmer)
model: TTS model (tts-1 or tts-1-hd)
response_format: Audio format (mp3, opus, aac, flac)
Returns:
Audio data as bytes
"""
if not self.tts_endpoints:
raise ValueError("No TTS endpoints available")
endpoint = self.tts_endpoints[0]
headers = {"Content-Type": "application/json"}
if self.api_key:
headers["Authorization"] = f"Bearer {self.api_key}"
payload = {
"model": model,
"voice": voice,
"input": text,
"response_format": response_format
}
response = requests.post(
f"{endpoint}/audio/speech",
headers=headers,
json=payload,
timeout=self.timeout
)
response.raise_for_status()
return response.content
def _get_model_info(self, model: Optional[str] = None) -> Dict:
"""Get model info by ID or return first available model"""
if not self.models:
raise ValueError("No models available. Check endpoint configuration.")
if model is None:
return self.models[0]
for m in self.models:
if m["id"] == model:
return m
raise ValueError(f"Model '{model}' not found in discovered models")
# Demo usage when run as script
if __name__ == "__main__":
print("=== UncloseAI Python Client (with Streaming) ===\n")
# Initialize client (auto-discovers from environment)
client = UncloseAI()
if not client.models:
print("ERROR: No models discovered. Set environment variables:")
print(" MODEL_ENDPOINT_1, MODEL_ENDPOINT_2, etc.")
exit(1)
print(f"Discovered {len(client.models)} model(s)")
for model in client.models:
print(f" - {model['id']} (max_tokens: {model['max_tokens']})")
print()
# Non-streaming chat example
print("=== Non-Streaming Chat ===")
response = client.chat(
messages=[
{"role": "system", "content": "You are a helpful AI assistant."},
{"role": "user", "content": "Explain quantum computing in one sentence."}
],
max_tokens=100
)
print(f"Model: {response['model']}")
print(f"Response: {response['choices'][0]['message']['content']}\n")
# Streaming chat example
print("=== Streaming Chat ===")
if len(client.models) > 1:
model_id = client.models[1]["id"]
else:
model_id = None
print(f"Model: {model_id or client.models[0]['id']}")
print("Response: ", end="", flush=True)
for chunk in client.chat_stream(
messages=[
{"role": "system", "content": "You are a coding assistant."},
{"role": "user", "content": "Write a Python function to check if a number is prime"}
],
model=model_id,
max_tokens=200
):
if chunk.get("choices") and len(chunk["choices"]) > 0:
delta = chunk["choices"][0].get("delta", {})
content = delta.get("content", "")
if content:
print(content, end="", flush=True)
print("\n")
# TTS example
if client.tts_endpoints:
print("=== TTS Speech Generation ===")
audio_data = client.tts(
text="Hello from UncloseAI Python client! This demonstrates text to speech with streaming support.",
voice="alloy"
)
with open("speech.mp3", "wb") as f:
f.write(audio_data)
print(f"✓ Speech file created: speech.mp3 ({len(audio_data)} bytes)\n")
print("=== Examples Complete ===")