feat: nearai model provider

This commit is contained in:
Robert Yan 2026-01-29 17:26:29 +08:00
parent 6372242da7
commit 537ddff08b
15 changed files with 473 additions and 1 deletions

View File

@ -45,6 +45,7 @@ See [Venice AI](/providers/venice).
- [GLM models](/providers/glm)
- [MiniMax](/providers/minimax)
- [Venius (Venice AI, privacy-focused)](/providers/venice)
- [NEAR AI (private, verifiable)](/providers/nearai)
- [Ollama (local models)](/providers/ollama)
## Transcription providers

View File

@ -42,6 +42,7 @@ See [Venice AI](/providers/venice).
- [GLM models](/providers/glm)
- [MiniMax](/providers/minimax)
- [Venius (Venice AI)](/providers/venice)
- [NEAR AI (private, verifiable)](/providers/nearai)
- [Amazon Bedrock](/bedrock)
For the full provider catalog (xAI, Groq, Mistral, etc.) and advanced configuration,

163
docs/providers/nearai.md Normal file
View File

@ -0,0 +1,163 @@
---
summary: "Use NEAR AI private inference in Clawdbot"
read_when:
- You want private inference with Intel TDX/NVIDIA TEE
- You want NEAR AI setup guidance
---
# NEAR AI
NEAR AI provides privacy-focused AI inference using confidential computing. All inference runs inside Intel TDX (Trust Domain Extensions) and NVIDIA TEE (Trusted Execution Environment), ensuring your prompts and responses are never logged or exposed to the host system.
## Why NEAR AI in Clawdbot
- **Private inference** - all computation happens inside secure enclaves.
- **Cryptographic verification** - outputs are signed inside TEE before leaving.
- **No logging** - prompts and responses are never stored.
- OpenAI-compatible `/v1` endpoints.
## Privacy Technology
NEAR AI uses two layers of hardware-based security:
| Technology | Purpose |
|------------|---------|
| **Intel TDX** | Isolates AI workloads in confidential VMs |
| **NVIDIA TEE** | GPU-level isolation for model weights and computations |
All AI outputs are cryptographically signed inside the TEE before leaving the secure environment, ensuring authenticity and integrity of responses.
## Features
- **OpenAI-compatible API**: Standard `/v1/chat/completions` endpoint
- **Streaming**: Supported on all models
- **Function calling**: Supported
- **Vision**: Supported on vision-capable models
## Setup
### 1. Get API Key
1. Sign up at [cloud.near.ai](https://cloud.near.ai)
2. Go to your dashboard and generate an API key
3. Copy your API key
### 2. Configure Clawdbot
**Option A: Environment Variable**
```bash
export NEARAI_API_KEY="your-api-key"
```
**Option B: Interactive Setup (Recommended)**
```bash
clawdbot onboard --auth-choice nearai-api-key
```
This will:
1. Prompt for your API key (or use existing `NEARAI_API_KEY`)
2. Configure the provider automatically
3. Set NEAR AI as your default model
**Option C: Non-interactive**
```bash
clawdbot onboard --non-interactive \
--auth-choice nearai-api-key \
--nearai-api-key "your-api-key"
```
### 3. Verify Setup
```bash
clawdbot chat --model nearai/zai-org/GLM-4.7 "Hello, are you working?"
```
## Model Selection
After setup, you can use any available NEAR AI model:
```bash
clawdbot models set nearai/zai-org/GLM-4.7
clawdbot models set nearai/deepseek-ai/DeepSeek-V3.1
```
List all available models:
```bash
clawdbot models list | grep nearai
```
## Available Models
> **Note:** The model list may change. See the latest available models at [cloud.near.ai/models](https://cloud.near.ai/models).
| Model ID | Name | Context | Cost ($/M tokens) |
|----------|------|---------|-------------------|
| `deepseek-ai/DeepSeek-V3.1` | DeepSeek V3.1 | 128K | $1.05 in / $3.10 out |
| `openai/gpt-oss-120b` | GPT OSS 120B | 131K | $0.15 in / $0.55 out |
| `Qwen/Qwen3-30B-A3B-Instruct-2507` | Qwen3 30B | 262K | $0.15 in / $0.55 out |
| `zai-org/GLM-4.7` | GLM 4.7 (default) | 131K | $0.85 in / $3.30 out |
## Configure via `clawdbot configure`
1. Run `clawdbot configure`
2. Select **Model/auth**
3. Choose **NEAR AI**
## Usage Examples
```bash
# Use default model (GLM 4.7)
clawdbot chat --model nearai/zai-org/GLM-4.7
# Use DeepSeek for reasoning tasks
clawdbot chat --model nearai/deepseek-ai/DeepSeek-V3.1
# Use Qwen for long context (262K!)
clawdbot chat --model nearai/Qwen/Qwen3-30B-A3B-Instruct-2507
# Send a message
clawdbot agent --message "Explain quantum computing" --model nearai/zai-org/GLM-4.7
```
## Troubleshooting
### API key not recognized
```bash
echo $NEARAI_API_KEY
clawdbot models list | grep nearai
```
Ensure the environment variable is set correctly.
### Connection issues
NEAR AI API is at `https://cloud-api.near.ai/v1`. Ensure your network allows HTTPS connections.
## Config file example
```json5
{
env: { NEARAI_API_KEY: "..." },
agents: { defaults: { model: { primary: "nearai/zai-org/GLM-4.7" } } },
models: {
mode: "merge",
providers: {
"nearai": {
baseUrl: "https://cloud-api.near.ai/v1",
apiKey: "${NEARAI_API_KEY}",
api: "openai-completions"
// Models are auto-discovered from the built-in catalog
}
}
}
}
```
## Links
- [NEAR AI Cloud](https://cloud.near.ai)
- [NEAR AI Documentation](https://docs.near.ai)

View File

@ -285,6 +285,7 @@ export function resolveEnvApiKey(provider: string): EnvApiKeyResult | null {
venice: "VENICE_API_KEY",
mistral: "MISTRAL_API_KEY",
opencode: "OPENCODE_API_KEY",
nearai: "NEARAI_API_KEY",
};
const envVar = envMap[normalized];
if (!envVar) return null;

View File

@ -41,4 +41,26 @@ describe("normalizeModelCompat", () => {
const normalized = normalizeModelCompat(model);
expect(normalized.compat?.supportsDeveloperRole).toBe(false);
});
it("forces supportsDeveloperRole off for nearai provider", () => {
const model = {
...baseModel(),
provider: "nearai",
baseUrl: "https://cloud-api.near.ai/v1",
};
delete (model as { compat?: unknown }).compat;
const normalized = normalizeModelCompat(model);
expect(normalized.compat?.supportsDeveloperRole).toBe(false);
});
it("forces supportsDeveloperRole off for near.ai baseUrl", () => {
const model = {
...baseModel(),
provider: "custom",
baseUrl: "https://cloud-api.near.ai/v1",
};
delete (model as { compat?: unknown }).compat;
const normalized = normalizeModelCompat(model);
expect(normalized.compat?.supportsDeveloperRole).toBe(false);
});
});

View File

@ -7,7 +7,8 @@ function isOpenAiCompletionsModel(model: Model<Api>): model is Model<"openai-com
export function normalizeModelCompat(model: Model<Api>): Model<Api> {
const baseUrl = model.baseUrl ?? "";
const isZai = model.provider === "zai" || baseUrl.includes("api.z.ai");
if (!isZai || !isOpenAiCompletionsModel(model)) return model;
const isNearAi = model.provider === "nearai" || baseUrl.includes("cloud-api.near.ai");
if ((!isZai && !isNearAi) || !isOpenAiCompletionsModel(model)) return model;
const openaiModel = model as Model<"openai-completions">;
const compat = openaiModel.compat ?? undefined;

View File

@ -13,6 +13,11 @@ import {
SYNTHETIC_MODEL_CATALOG,
} from "./synthetic-models.js";
import { discoverVeniceModels, VENICE_BASE_URL } from "./venice-models.js";
import {
buildNearAiModelDefinition,
NEAR_AI_BASE_URL,
NEAR_AI_MODEL_CATALOG,
} from "./nearai-models.js";
type ModelsConfig = NonNullable<MoltbotConfig["models"]>;
export type ProviderConfig = NonNullable<ModelsConfig["providers"]>[string];
@ -350,6 +355,14 @@ async function buildVeniceProvider(): Promise<ProviderConfig> {
};
}
function buildNearAiProvider(): ProviderConfig {
return {
baseUrl: NEAR_AI_BASE_URL,
api: "openai-completions",
models: NEAR_AI_MODEL_CATALOG.map(buildNearAiModelDefinition),
};
}
async function buildOllamaProvider(): Promise<ProviderConfig> {
const models = await discoverOllamaModels();
return {
@ -402,6 +415,13 @@ export async function resolveImplicitProviders(params: {
providers.venice = { ...(await buildVeniceProvider()), apiKey: veniceKey };
}
const nearAiKey =
resolveEnvApiKeyVarName("nearai") ??
resolveApiKeyFromProfiles({ provider: "nearai", store: authStore });
if (nearAiKey) {
providers["nearai"] = { ...buildNearAiProvider(), apiKey: nearAiKey };
}
const qwenProfiles = listProfilesForProvider(authStore, "qwen-portal");
if (qwenProfiles.length > 0) {
providers["qwen-portal"] = {

View File

@ -0,0 +1,85 @@
import type { ModelDefinitionConfig } from "../config/types.js";
export const NEAR_AI_BASE_URL = "https://cloud-api.near.ai/v1";
export const NEAR_AI_DEFAULT_MODEL_ID = "zai-org/GLM-4.7";
export const NEAR_AI_DEFAULT_MODEL_REF = `nearai/${NEAR_AI_DEFAULT_MODEL_ID}`;
// NEAR AI uses credit-based pricing (per million tokens).
export const NEAR_AI_DEFAULT_COST = {
input: 0,
output: 0,
cacheRead: 0,
cacheWrite: 0,
};
/**
* Static catalog of NEAR AI models.
*
* NEAR AI provides privacy-focused inference using:
* - Intel TDX (Trust Domain Extensions) for confidential VMs
* - NVIDIA TEE for GPU-level isolation
* - Cryptographic signing of all AI outputs inside TEE
*
* All NEAR AI models are fully private - prompts/responses are not logged.
* The `privacy` field is set to "private" for all models.
*/
export const NEAR_AI_MODEL_CATALOG = [
{
id: "deepseek-ai/DeepSeek-V3.1",
name: "DeepSeek V3.1",
reasoning: false,
input: ["text"],
contextWindow: 128000,
maxTokens: 8192,
cost: { input: 1.05, output: 3.1, cacheRead: 0, cacheWrite: 0 },
privacy: "private",
},
{
id: "openai/gpt-oss-120b",
name: "GPT OSS 120B",
reasoning: true,
input: ["text"],
contextWindow: 131000,
maxTokens: 8192,
cost: { input: 0.15, output: 0.55, cacheRead: 0, cacheWrite: 0 },
privacy: "private",
},
{
id: "Qwen/Qwen3-30B-A3B-Instruct-2507",
name: "Qwen3 30B Instruct",
reasoning: false,
input: ["text"],
contextWindow: 262144,
maxTokens: 8192,
cost: { input: 0.15, output: 0.55, cacheRead: 0, cacheWrite: 0 },
privacy: "private",
},
{
id: "zai-org/GLM-4.7",
name: "GLM 4.7",
reasoning: true,
input: ["text"],
contextWindow: 131072,
maxTokens: 8192,
cost: { input: 0.85, output: 3.3, cacheRead: 0, cacheWrite: 0 },
privacy: "private",
},
] as const;
export type NearAiCatalogEntry = (typeof NEAR_AI_MODEL_CATALOG)[number];
/**
* Build a ModelDefinitionConfig from a NEAR AI catalog entry.
*/
export function buildNearAiModelDefinition(entry: NearAiCatalogEntry): ModelDefinitionConfig {
return {
id: entry.id,
name: entry.name,
reasoning: entry.reasoning,
input: [...entry.input],
cost: entry.cost,
contextWindow: entry.contextWindow,
maxTokens: entry.maxTokens,
privacy: entry.privacy,
};
}

View File

@ -20,6 +20,7 @@ export type AuthChoiceGroupId =
| "minimax"
| "synthetic"
| "venice"
| "nearai"
| "qwen";
export type AuthChoiceGroup = {
@ -71,6 +72,12 @@ const AUTH_CHOICE_GROUP_DEFS: {
hint: "Privacy-focused (uncensored models)",
choices: ["venice-api-key"],
},
{
value: "nearai",
label: "NEAR AI",
hint: "Private and verifiable inference (TEE)",
choices: ["nearai-api-key"],
},
{
value: "google",
label: "Google",
@ -147,6 +154,11 @@ export function buildAuthChoiceOptions(params: {
label: "Venice AI API key",
hint: "Privacy-focused inference (uncensored models)",
});
options.push({
value: "nearai-api-key",
label: "NEAR AI API key",
hint: "Private and verifiable inference with Intel TDX/NVIDIA TEE",
});
options.push({
value: "github-copilot",
label: "GitHub Copilot (GitHub device login)",

View File

@ -25,6 +25,8 @@ import {
applySyntheticProviderConfig,
applyVeniceConfig,
applyVeniceProviderConfig,
applyNearAiConfig,
applyNearAiProviderConfig,
applyVercelAiGatewayConfig,
applyVercelAiGatewayProviderConfig,
applyZaiConfig,
@ -33,6 +35,7 @@ import {
OPENROUTER_DEFAULT_MODEL_REF,
SYNTHETIC_DEFAULT_MODEL_REF,
VENICE_DEFAULT_MODEL_REF,
NEAR_AI_DEFAULT_MODEL_REF,
VERCEL_AI_GATEWAY_DEFAULT_MODEL_REF,
setGeminiApiKey,
setKimiCodeApiKey,
@ -41,6 +44,7 @@ import {
setOpenrouterApiKey,
setSyntheticApiKey,
setVeniceApiKey,
setNearAiApiKey,
setVercelAiGatewayApiKey,
setZaiApiKey,
ZAI_DEFAULT_MODEL_REF,
@ -83,6 +87,8 @@ export async function applyAuthChoiceApiProviders(
authChoice = "synthetic-api-key";
} else if (params.opts.tokenProvider === "venice") {
authChoice = "venice-api-key";
} else if (params.opts.tokenProvider === "nearai") {
authChoice = "nearai-api-key";
} else if (params.opts.tokenProvider === "opencode") {
authChoice = "opencode-zen";
}
@ -522,6 +528,65 @@ export async function applyAuthChoiceApiProviders(
return { config: nextConfig, agentModelOverride };
}
if (authChoice === "nearai-api-key") {
let hasCredential = false;
if (!hasCredential && params.opts?.token && params.opts?.tokenProvider === "nearai") {
await setNearAiApiKey(normalizeApiKeyInput(params.opts.token), params.agentDir);
hasCredential = true;
}
if (!hasCredential) {
await params.prompter.note(
[
"NEAR AI provides privacy-focused inference using Intel TDX and NVIDIA TEE.",
"Get your API key at: https://cloud.near.ai",
"All inference is private - prompts/responses are not logged.",
].join("\n"),
"NEAR AI",
);
}
const envKey = resolveEnvApiKey("nearai");
if (envKey) {
const useExisting = await params.prompter.confirm({
message: `Use existing NEARAI_API_KEY (${envKey.source}, ${formatApiKeyPreview(envKey.apiKey)})?`,
initialValue: true,
});
if (useExisting) {
await setNearAiApiKey(envKey.apiKey, params.agentDir);
hasCredential = true;
}
}
if (!hasCredential) {
const key = await params.prompter.text({
message: "Enter NEAR AI API key",
validate: validateApiKeyInput,
});
await setNearAiApiKey(normalizeApiKeyInput(String(key)), params.agentDir);
}
nextConfig = applyAuthProfileConfig(nextConfig, {
profileId: "nearai:default",
provider: "nearai",
mode: "api_key",
});
{
const applied = await applyDefaultModelChoice({
config: nextConfig,
setDefaultModel: params.setDefaultModel,
defaultModel: NEAR_AI_DEFAULT_MODEL_REF,
applyDefaultConfig: applyNearAiConfig,
applyProviderConfig: applyNearAiProviderConfig,
noteDefault: NEAR_AI_DEFAULT_MODEL_REF,
noteAgentModel,
prompter: params.prompter,
});
nextConfig = applied.config;
agentModelOverride = applied.agentModelOverride ?? agentModelOverride;
}
return { config: nextConfig, agentModelOverride };
}
if (authChoice === "opencode-zen") {
let hasCredential = false;
if (!hasCredential && params.opts?.token && params.opts?.tokenProvider === "opencode") {

View File

@ -20,6 +20,7 @@ const PREFERRED_PROVIDER_BY_AUTH_CHOICE: Partial<Record<AuthChoice, string>> = {
"zai-api-key": "zai",
"synthetic-api-key": "synthetic",
"venice-api-key": "venice",
"nearai-api-key": "nearai",
"github-copilot": "github-copilot",
"copilot-proxy": "copilot-proxy",
"minimax-cloud": "minimax",

View File

@ -10,6 +10,12 @@ import {
VENICE_DEFAULT_MODEL_REF,
VENICE_MODEL_CATALOG,
} from "../agents/venice-models.js";
import {
buildNearAiModelDefinition,
NEAR_AI_BASE_URL,
NEAR_AI_DEFAULT_MODEL_REF,
NEAR_AI_MODEL_CATALOG,
} from "../agents/nearai-models.js";
import type { MoltbotConfig } from "../config/config.js";
import {
OPENROUTER_DEFAULT_MODEL_REF,
@ -411,6 +417,81 @@ export function applyVeniceConfig(cfg: MoltbotConfig): MoltbotConfig {
};
}
/**
* Apply NEAR AI provider configuration without changing the default model.
* Registers NEAR AI models and sets up the provider, but preserves existing model selection.
*/
export function applyNearAiProviderConfig(cfg: MoltbotConfig): MoltbotConfig {
const models = { ...cfg.agents?.defaults?.models };
models[NEAR_AI_DEFAULT_MODEL_REF] = {
...models[NEAR_AI_DEFAULT_MODEL_REF],
alias: models[NEAR_AI_DEFAULT_MODEL_REF]?.alias ?? "GLM-4.7",
};
const providers = { ...cfg.models?.providers };
const existingProvider = providers["nearai"];
const existingModels = Array.isArray(existingProvider?.models) ? existingProvider.models : [];
const nearAiModels = NEAR_AI_MODEL_CATALOG.map(buildNearAiModelDefinition);
const mergedModels = [
...existingModels,
...nearAiModels.filter((model) => !existingModels.some((existing) => existing.id === model.id)),
];
const { apiKey: existingApiKey, ...existingProviderRest } = (existingProvider ?? {}) as Record<
string,
unknown
> as { apiKey?: string };
const resolvedApiKey = typeof existingApiKey === "string" ? existingApiKey : undefined;
const normalizedApiKey = resolvedApiKey?.trim();
providers["nearai"] = {
...existingProviderRest,
baseUrl: NEAR_AI_BASE_URL,
api: "openai-completions",
...(normalizedApiKey ? { apiKey: normalizedApiKey } : {}),
models: mergedModels.length > 0 ? mergedModels : nearAiModels,
};
return {
...cfg,
agents: {
...cfg.agents,
defaults: {
...cfg.agents?.defaults,
models,
},
},
models: {
mode: cfg.models?.mode ?? "merge",
providers,
},
};
}
/**
* Apply NEAR AI provider configuration AND set NEAR AI as the default model.
* Use this when NEAR AI is the primary provider choice during onboarding.
*/
export function applyNearAiConfig(cfg: MoltbotConfig): MoltbotConfig {
const next = applyNearAiProviderConfig(cfg);
const existingModel = next.agents?.defaults?.model;
return {
...next,
agents: {
...next.agents,
defaults: {
...next.agents?.defaults,
model: {
...(existingModel && "fallbacks" in (existingModel as Record<string, unknown>)
? {
fallbacks: (existingModel as { fallbacks?: string[] }).fallbacks,
}
: undefined),
primary: NEAR_AI_DEFAULT_MODEL_REF,
},
},
},
};
}
export function applyAuthProfileConfig(
cfg: MoltbotConfig,
params: {

View File

@ -112,6 +112,19 @@ export async function setVeniceApiKey(key: string, agentDir?: string) {
});
}
export async function setNearAiApiKey(key: string, agentDir?: string) {
// Write to resolved agent dir so gateway finds credentials on startup.
upsertAuthProfile({
profileId: "nearai:default",
credential: {
type: "api_key",
provider: "nearai",
key,
},
agentDir: resolveAuthAgentDir(agentDir),
});
}
export const ZAI_DEFAULT_MODEL_REF = "zai/glm-4.7";
export const OPENROUTER_DEFAULT_MODEL_REF = "openrouter/auto";
export const VERCEL_AI_GATEWAY_DEFAULT_MODEL_REF = "vercel-ai-gateway/anthropic/claude-opus-4.5";

View File

@ -3,6 +3,7 @@ export {
SYNTHETIC_DEFAULT_MODEL_REF,
} from "../agents/synthetic-models.js";
export { VENICE_DEFAULT_MODEL_ID, VENICE_DEFAULT_MODEL_REF } from "../agents/venice-models.js";
export { NEAR_AI_DEFAULT_MODEL_ID, NEAR_AI_DEFAULT_MODEL_REF } from "../agents/nearai-models.js";
export {
applyAuthProfileConfig,
applyKimiCodeConfig,
@ -15,6 +16,8 @@ export {
applySyntheticProviderConfig,
applyVeniceConfig,
applyVeniceProviderConfig,
applyNearAiConfig,
applyNearAiProviderConfig,
applyVercelAiGatewayConfig,
applyVercelAiGatewayProviderConfig,
applyZaiConfig,
@ -43,6 +46,7 @@ export {
setOpenrouterApiKey,
setSyntheticApiKey,
setVeniceApiKey,
setNearAiApiKey,
setVercelAiGatewayApiKey,
setZaiApiKey,
writeOAuthCredentials,

View File

@ -17,6 +17,7 @@ export type AuthChoice =
| "kimi-code-api-key"
| "synthetic-api-key"
| "venice-api-key"
| "nearai-api-key"
| "codex-cli"
| "apiKey"
| "gemini-api-key"
@ -70,6 +71,7 @@ export type OnboardOptions = {
minimaxApiKey?: string;
syntheticApiKey?: string;
veniceApiKey?: string;
nearaiApiKey?: string;
opencodeZenApiKey?: string;
gatewayPort?: number;
gatewayBind?: GatewayBind;