Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,15 @@ CLAWDBOT_AUTH_TOKEN=your-openclaw-gateway-token
# Session key prefix — change if running multiple instances on the same gateway
GATEWAY_SESSION_KEY=voice-main-1

# =============================================================================
# LLM provider keys (optional — configured through OpenClaw / admin model config)
# =============================================================================

# NEAR AI Cloud (OpenAI-compatible TEE inference)
# Get key: https://cloud.near.ai
# NEARAI_API_KEY=your-nearai-api-key
# NEARAI_BASE_URL=https://cloud-api.near.ai/v1

# =============================================================================
# TTS — choose one or more providers
# =============================================================================
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ See [`deploy/`](deploy/) for the full production setup including SSL, nginx reve
All configuration is in `.env`. Copy `.env.example` to `.env` and fill in your values.

**Required:**
- An LLM provider API key (OpenAI, Anthropic, Groq, Z.AI, or any OpenClaw-compatible provider)
- An LLM provider API key (OpenAI, Anthropic, Groq, Z.AI, NEAR AI Cloud, or any OpenClaw-compatible provider)
- `CLAWDBOT_AUTH_TOKEN` — set during `npx openvoiceui setup` or in OpenClaw's setup wizard

**Optional but recommended:**
Expand All @@ -177,6 +177,7 @@ See [`.env.example`](.env.example) for all available options with descriptions.
|----------|--------|
| OpenClaw Gateway | Built-in — routes to OpenAI, Anthropic, Groq, Z.AI, and more |
| Z.AI (GLM-5-turbo) | Built-in |
| NEAR AI Cloud | OpenAI-compatible TEE inference via OpenClaw |
| Groq (Llama, Qwen) | Via OpenClaw |
| Google Gemini | Via OpenClaw |
| MiniMax | Via OpenClaw |
Expand Down
4 changes: 4 additions & 0 deletions SETUP.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ GROQ_API_KEY=your-groq-key
SECRET_KEY=any-random-string-here
```

Optional: set `NEARAI_API_KEY=your-nearai-key` to enable NEAR AI Cloud TEE inference.

**Optional: enable the coding-agent skill**

The coding-agent skill lets the AI write code, create files, and run commands
Expand Down Expand Up @@ -160,6 +162,8 @@ Point openvoiceui at your running OpenClaw gateway instead of starting a new one
SECRET_KEY=any-random-string-here
```

Optional: set `NEARAI_API_KEY=your-nearai-key` to enable NEAR AI Cloud TEE inference.

4. Start only the openvoiceui and supertonic services (skip the built-in openclaw):
```bash
docker compose up --build openvoiceui supertonic
Expand Down
17 changes: 17 additions & 0 deletions config/providers.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ llm:
fallback_provider: clawdbot
modules:
- providers.llm.zai_provider
- providers.llm.nearai_provider
- providers.llm.clawdbot_provider

providers:
Expand All @@ -25,6 +26,22 @@ llm:
- id: glm-4-plus
name: "GLM-4 Plus"

nearai:
name: "NEAR AI Cloud"
api_key: ${NEARAI_API_KEY}
base_url: https://cloud-api.near.ai/v1
default_model: zai-org/GLM-5.1-FP8
priority: 15
models:
- id: zai-org/GLM-5.1-FP8
name: "GLM 5.1"
- id: Qwen/Qwen3.6-35B-A3B-FP8
name: "Qwen 3.6 35B A3B FP8"
- id: Qwen/Qwen3.5-122B-A10B
name: "Qwen3.5 122B A10B"
- id: Qwen/Qwen3-VL-30B-A3B-Instruct
name: "Qwen3-VL 30B A3B Instruct"

clawdbot:
name: "Clawdbot Gateway"
gateway_url: ${CLAWDBOT_GATEWAY_URL}
Expand Down
3 changes: 3 additions & 0 deletions docs/getting-started/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ OpenVoiceUI needs at least one LLM provider to power conversations. The AI gatew
|----------|---------|-------------------|
| Groq (Llama, Qwen) | `GROQ_API_KEY` | [console.groq.com](https://console.groq.com) |
| Z.AI (GLM-5-turbo) | Set via OpenClaw config | [z.ai](https://z.ai) |
| NEAR AI Cloud (TEE inference) | `NEARAI_API_KEY` | [cloud.near.ai](https://cloud.near.ai) |
| OpenAI (GPT-4o) | `OPENAI_API_KEY` | [platform.openai.com](https://platform.openai.com) |
| Anthropic (Claude) | `ANTHROPIC_API_KEY` | [console.anthropic.com](https://console.anthropic.com) |

Expand Down Expand Up @@ -106,6 +107,8 @@ Complete list of all recognized environment variables:
| `GATEWAY_SESSION_KEY` | No | `voice-main-1` | Session key prefix. Change if running multiple instances on the same gateway. |
| `OPENCLAW_VERSION` | No | `2026.3.13` | Docker build arg: OpenClaw version to install. Only change if you have verified compatibility. |
| `CANVAS_PAGES_DIR` | No | `runtime/canvas-pages/` | Where canvas HTML pages are stored. Docker: leave unset (uses volume mount). VPS: set to the path created by the deploy script. |
| `NEARAI_API_KEY` | No | -- | NEAR AI Cloud API key for OpenAI-compatible TEE inference. |
| `NEARAI_BASE_URL` | No | `https://cloud-api.near.ai/v1` | Override for the NEAR AI Cloud OpenAI-compatible endpoint. |
| `GROQ_API_KEY` | Recommended | -- | Groq API key for Orpheus TTS and Whisper STT. |
| `USE_GROQ` | No | -- | Set `true` to enable Groq integration. |
| `USE_GROQ_TTS` | No | -- | Set `true` to enable Groq Orpheus TTS. |
Expand Down
2 changes: 2 additions & 0 deletions docs/getting-started/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ GROQ_API_KEY=your-groq-api-key
SECRET_KEY=any-random-string-here
```

Optional: set `NEARAI_API_KEY=your-nearai-key` to enable NEAR AI Cloud TEE inference.

See [Configuration](/getting-started/configuration) for the full list of environment variables.

### 2. Optional: enable coding agent
Expand Down
2 changes: 2 additions & 0 deletions docs/reference/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,8 @@ Set in `app.py` after_request handler.
| Variable | Default | Purpose |
|----------|---------|---------|
| `CLAWDBOT_GATEWAY_URL` | `ws://127.0.0.1:18791` | Gateway WebSocket URL |
| `NEARAI_API_KEY` | — | NEAR AI Cloud TEE inference |
| `NEARAI_BASE_URL` | `https://cloud-api.near.ai/v1` | Optional NEAR AI endpoint override |
| `GROQ_API_KEY` | — | Groq Orpheus TTS |
| `FAL_KEY` | — | Qwen3-TTS via fal.ai |
| `SUNO_API_KEY` | — | AI song generation |
Expand Down
8 changes: 8 additions & 0 deletions install.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,13 @@ module.exports = {
placeholder: "",
required: false,
},
{
key: "NEARAI_API_KEY",
title: "[RECOMMENDED] NEAR AI Cloud API Key — TEE inference",
description: "cloud.near.ai — OpenAI-compatible private inference",
placeholder: "",
required: false,
},
{
key: "ANTHROPIC_API_KEY",
title: "[RECOMMENDED] Anthropic API Key — Claude models",
Expand Down Expand Up @@ -314,6 +321,7 @@ module.exports = {
PINOKIO_DEEPGRAM_API_KEY: "{{input.DEEPGRAM_API_KEY}}",
PINOKIO_ANTHROPIC_API_KEY: "{{input.ANTHROPIC_API_KEY}}",
PINOKIO_ZAI_API_KEY: "{{input.ZAI_API_KEY}}",
PINOKIO_NEARAI_API_KEY: "{{input.NEARAI_API_KEY}}",
PINOKIO_OPENAI_API_KEY: "{{input.OPENAI_API_KEY}}",
PINOKIO_GEMINI_API_KEY: "{{input.GEMINI_API_KEY}}",
PINOKIO_OPENROUTER_API_KEY: "{{input.OPENROUTER_API_KEY}}",
Expand Down
2 changes: 1 addition & 1 deletion profiles/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
"properties": {
"provider": {
"type": "string",
"enum": ["zai", "clawdbot", "gateway", "openai", "ollama", "anthropic", "hume", "elevenlabs"]
"enum": ["zai", "nearai", "clawdbot", "gateway", "openai", "ollama", "anthropic", "hume", "elevenlabs"]
},
"model": { "type": "string" },
"config_id": { "type": "string" },
Expand Down
1 change: 1 addition & 0 deletions providers/llm/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@
# Import concrete providers so their registry.register() calls fire
from providers.llm import zai_provider # noqa: F401
from providers.llm import clawdbot_provider # noqa: F401
from providers.llm import nearai_provider # noqa: F401

__all__ = ["LLMProvider", "LLMResponse", "LLMError"]
142 changes: 142 additions & 0 deletions providers/llm/nearai_provider.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
"""
NEAR AI Cloud LLM provider.

OpenAI-compatible chat completions over NEAR AI Cloud TEE inference.
"""

import os
import time
from typing import Any, Dict, Iterator, List, Optional

from providers.llm.base import LLMError, LLMProvider, LLMResponse
from providers.registry import ProviderType, registry


class NearAIProvider(LLMProvider):
"""NEAR AI Cloud provider via OpenAI-compatible REST API."""

DEFAULT_BASE_URL = "https://cloud-api.near.ai/v1"
DEFAULT_MODEL = "zai-org/GLM-5.1-FP8"

def __init__(self, config: Dict[str, Any] = None) -> None:
super().__init__(config)
self.api_key = self._resolve_api_key()
self.base_url = self._resolve_base_url()
self.default_model = self._config.get("default_model", self.DEFAULT_MODEL)

def _resolve_api_key(self) -> str:
key = self._config.get("api_key", "")
if key and not key.startswith("${"):
return key
return os.getenv("NEARAI_API_KEY", "")

def _resolve_base_url(self) -> str:
base_url = (
self._config.get("base_url")
or self._config.get("baseUrl")
or os.getenv("NEARAI_BASE_URL", "")
or self.DEFAULT_BASE_URL
)
return base_url.rstrip("/")

def _chat_url(self) -> str:
return f"{self.base_url}/chat/completions"

def _build_messages(
self,
messages: List[Dict[str, str]],
system_prompt: Optional[str],
) -> List[Dict[str, str]]:
full_messages: List[Dict[str, str]] = []
if system_prompt:
full_messages.append({"role": "system", "content": system_prompt})

for message in messages:
role = message.get("role", "user")
if role == "developer":
role = "system"
full_messages.append({
"role": role,
"content": message.get("content", ""),
})
return full_messages

def generate(
self,
messages: List[Dict[str, str]],
system_prompt: Optional[str] = None,
model: Optional[str] = None,
**kwargs,
) -> LLMResponse:
try:
import requests # type: ignore
except ImportError:
raise LLMError("nearai", "requests library not installed")

model = model or self.default_model
max_tokens = kwargs.get("max_tokens", kwargs.get("max_completion_tokens", 512))
timeout = kwargs.get("timeout", 30)

payload: Dict[str, Any] = {
"model": model,
"messages": self._build_messages(messages, system_prompt),
"max_tokens": max_tokens,
}

if "temperature" in kwargs:
payload["temperature"] = kwargs["temperature"]
if "tools" in kwargs:
payload["tools"] = kwargs["tools"]
if "tool_choice" in kwargs:
payload["tool_choice"] = kwargs["tool_choice"]

start = time.time()
try:
resp = requests.post(
self._chat_url(),
headers={
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json",
},
json=payload,
timeout=timeout,
)
resp.raise_for_status()
except Exception as exc:
raise LLMError("nearai", f"API request failed: {exc}") from exc

data = resp.json()
latency_ms = (time.time() - start) * 1000
choice = data["choices"][0]

return LLMResponse(
content=choice["message"]["content"],
model=model,
provider="nearai",
usage=data.get("usage", {}),
latency_ms=latency_ms,
finish_reason=choice.get("finish_reason", "stop"),
raw_response=data,
)

def generate_stream(
self,
messages: List[Dict[str, str]],
system_prompt: Optional[str] = None,
model: Optional[str] = None,
**kwargs,
) -> Iterator[str]:
response = self.generate(messages, system_prompt, model, **kwargs)
yield response.content

def is_available(self) -> bool:
return bool(self.api_key)

def get_info(self) -> Dict[str, Any]:
info = super().get_info()
info["name"] = self._config.get("name", "NEAR AI Cloud")
info["base_url"] = self.base_url
return info


registry.register(ProviderType.LLM, "nearai", NearAIProvider)
17 changes: 17 additions & 0 deletions routes/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,16 @@ def list_clients():

_OPENCLAW_CONFIG_PATH = Path('/app/runtime/openclaw.json')

# Static admin list sampled from GET https://cloud-api.near.ai/v1/model/list on 2026-05-21.
_NEARAI_MODELS = [
{'id': 'zai-org/GLM-5.1-FP8', 'name': 'GLM 5.1', 'contextWindow': 202752},
{'id': 'Qwen/Qwen3.6-35B-A3B-FP8', 'name': 'Qwen 3.6 35B A3B FP8', 'contextWindow': 262144},
{'id': 'Qwen/Qwen3.5-122B-A10B', 'name': 'Qwen3.5 122B A10B', 'contextWindow': 131072},
{'id': 'Qwen/Qwen3-VL-30B-A3B-Instruct', 'name': 'Qwen3-VL 30B A3B Instruct', 'contextWindow': 256000},
{'id': 'google/gemma-4-31B-it', 'name': 'Gemma 4 31B Instruct', 'contextWindow': 262144},
{'id': 'openai/gpt-oss-120b', 'name': 'GPT OSS 120B', 'contextWindow': 131000},
]

# Known providers and their config shape
_AI_PROVIDERS = {
'mx': {
Expand Down Expand Up @@ -664,6 +674,13 @@ def list_clients():
{'id': 'llama-4-maverick-17b-128e-instruct', 'name': 'Llama 4 Maverick', 'contextWindow': 131072},
],
},
'nearai': {
'name': 'NEAR AI Cloud',
'envKey': 'NEARAI_API_KEY',
'baseUrl': 'https://cloud-api.near.ai/v1',
'api': 'openai-completions',
'models': _NEARAI_MODELS,
},
'google': {
'name': 'Google Gemini',
'envKey': 'GEMINI_API_KEY',
Expand Down
26 changes: 26 additions & 0 deletions setup-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,16 @@ const PORT = getKey("PORT") || "5001";
const token = crypto.randomBytes(24).toString("hex");
const secret = crypto.randomBytes(32).toString("hex");

// Sampled from GET https://cloud-api.near.ai/v1/model/list on 2026-05-21.
const NEARAI_MODELS = [
{ id: "zai-org/GLM-5.1-FP8", name: "GLM 5.1", contextWindow: 202752 },
{ id: "Qwen/Qwen3.6-35B-A3B-FP8", name: "Qwen 3.6 35B A3B FP8", contextWindow: 262144 },
{ id: "Qwen/Qwen3.5-122B-A10B", name: "Qwen3.5 122B A10B", contextWindow: 131072 },
{ id: "Qwen/Qwen3-VL-30B-A3B-Instruct", name: "Qwen3-VL 30B A3B Instruct", contextWindow: 256000 },
{ id: "google/gemma-4-31B-it", name: "Gemma 4 31B Instruct", contextWindow: 262144 },
{ id: "openai/gpt-oss-120b", name: "GPT OSS 120B", contextWindow: 131000 },
];

// ---------------------------------------------------------------------------
// 1. Write openclaw.json (nested gateway/agents format for v2026.3.2+)
// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -67,6 +77,20 @@ const openclawConfig = {
// Plugins configured by individual plugin installers
};

if (getKey("NEARAI_API_KEY")) {
openclawConfig.models = {
mode: "merge",
providers: {
nearai: {
baseUrl: "https://cloud-api.near.ai/v1",
api: "openai-completions",
apiKey: "${NEARAI_API_KEY}",
models: NEARAI_MODELS,
},
},
};
}

fs.mkdirSync("openclaw-data/workspace", { recursive: true });
fs.writeFileSync(
"openclaw-data/openclaw.json",
Expand Down Expand Up @@ -95,6 +119,7 @@ const envLines = [
const envKeyList = [
"ANTHROPIC_API_KEY", "OPENAI_API_KEY", "GEMINI_API_KEY",
"OPENROUTER_API_KEY", "MISTRAL_API_KEY", "XAI_API_KEY", "ZAI_API_KEY",
"NEARAI_API_KEY",
"CEREBRAS_API_KEY", "TOGETHER_API_KEY", "HF_TOKEN",
"MOONSHOT_API_KEY", "KIMI_API_KEY", "MINIMAX_API_KEY", "QIANFAN_API_KEY",
"MODELSTUDIO_API_KEY", "XIAOMI_API_KEY", "VOLCANO_ENGINE_API_KEY",
Expand Down Expand Up @@ -135,6 +160,7 @@ const providerMap = {
MISTRAL_API_KEY: "mistral",
XAI_API_KEY: "xai",
ZAI_API_KEY: "zai",
NEARAI_API_KEY: "nearai",
CEREBRAS_API_KEY: "cerebras",
TOGETHER_API_KEY: "together",
HF_TOKEN: "huggingface",
Expand Down
1 change: 1 addition & 0 deletions src/admin.html
Original file line number Diff line number Diff line change
Expand Up @@ -1756,6 +1756,7 @@
llm:[
{id:'gateway',name:'OpenClaw Gateway',detail:'GLM-5-turbo via OpenClaw. Full tools, memory, canvas.'},
{id:'zai',name:'Z.AI Direct',detail:'GLM-5-turbo / GLM-5.1. No agent tools.'},
{id:'nearai',name:'NEAR AI Cloud',detail:'TEE inference via OpenClaw. OpenAI-compatible.'},
{id:'groq',name:'Groq',detail:'Llama 4 Scout / Qwen3-32b. Fast inference.'},
{id:'minimax',name:'MiniMax',detail:'M2.7-highspeed. Custom provider "mx".'},
{id:'kimi',name:'Kimi',detail:'K2.5 / Moonshot AI.'},
Expand Down
Loading