feat(near-ai): add Near AI provider with updated model catalog

- Add Near AI as OpenAI-compatible provider
- Models: DeepSeek V3.1, GPT-OSS-120B, Qwen3-30B, GLM-4.6, GLM-4.7
- Auth via API key (NEAR_AI_API_KEY or clawdbot auth)
- Privacy-focused inference using Intel TDX/NVIDIA TEE
- Base URL: https://cloud-api.near.ai/v1
This commit is contained in:
Firat Sertgoz 2026-01-26 02:27:45 +04:00
parent 6859e1e6a6
commit 6d7ee8ba6c
No known key found for this signature in database
GPG Key ID: D5001141982D0383
10 changed files with 452 additions and 0 deletions

166
docs/providers/near-ai.md Normal file
View File

@ -0,0 +1,166 @@
---
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 near-ai-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 near-ai-api-key \
--near-ai-api-key "your-api-key"
```
### 3. Verify Setup
```bash
clawdbot chat --model near-ai/deepseek-ai/DeepSeek-V3.1 "Hello, are you working?"
```
## Model Selection
After setup, you can use any available NEAR AI model:
```bash
clawdbot models set near-ai/deepseek-ai/DeepSeek-V3.1
clawdbot models set near-ai/meta-llama/Llama-3.3-70B-Instruct
```
List all available models:
```bash
clawdbot models list | grep near-ai
```
## Available Models
| Model ID | Name | Features |
|----------|------|----------|
| `deepseek-ai/DeepSeek-V3.1` | DeepSeek V3.1 | Reasoning, 128k context |
| `meta-llama/Llama-3.3-70B-Instruct` | Llama 3.3 70B | General, 131k context |
## Configure via `clawdbot configure`
1. Run `clawdbot configure`
2. Select **Model/auth**
3. Choose **NEAR AI**
## Usage Examples
```bash
# Use default model
clawdbot chat --model near-ai/deepseek-ai/DeepSeek-V3.1
# Use Llama
clawdbot chat --model near-ai/meta-llama/Llama-3.3-70B-Instruct
# Send a message
clawdbot agent --message "Explain quantum computing" --model near-ai/deepseek-ai/DeepSeek-V3.1
```
## Troubleshooting
### API key not recognized
```bash
echo $NEARAI_API_KEY
clawdbot models list | grep near-ai
```
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: "near-ai/deepseek-ai/DeepSeek-V3.1" } } },
models: {
mode: "merge",
providers: {
"near-ai": {
baseUrl: "https://cloud-api.near.ai/v1",
apiKey: "${NEARAI_API_KEY}",
api: "openai-completions",
models: [
{
id: "deepseek-ai/DeepSeek-V3.1",
name: "DeepSeek V3.1",
reasoning: true,
input: ["text"],
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
contextWindow: 128000,
maxTokens: 8192
}
]
}
}
}
}
```
## 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",
"near-ai": "NEARAI_API_KEY",
};
const envVar = envMap[normalized];
if (!envVar) return null;

View File

@ -12,6 +12,11 @@ import {
SYNTHETIC_BASE_URL,
SYNTHETIC_MODEL_CATALOG,
} from "./synthetic-models.js";
import {
buildNearAiModelDefinition,
NEAR_AI_BASE_URL,
NEAR_AI_MODEL_CATALOG,
} from "./near-ai-models.js";
import { discoverVeniceModels, VENICE_BASE_URL } from "./venice-models.js";
type ModelsConfig = NonNullable<ClawdbotConfig["models"]>;
@ -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("near-ai") ??
resolveApiKeyFromProfiles({ provider: "near-ai", store: authStore });
if (nearAiKey) {
providers["near-ai"] = { ...buildNearAiProvider(), apiKey: nearAiKey };
}
const qwenProfiles = listProfilesForProvider(authStore, "qwen-portal");
if (qwenProfiles.length > 0) {
providers["qwen-portal"] = {

View File

@ -0,0 +1,88 @@
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 = "deepseek-ai/DeepSeek-V3.1";
export const NEAR_AI_DEFAULT_MODEL_REF = `near-ai/${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 inference is private by default - prompts/responses are not logged.
*/
export const NEAR_AI_MODEL_CATALOG = [
{
id: "deepseek-ai/DeepSeek-V3.1",
name: "DeepSeek V3.1",
reasoning: true,
input: ["text"],
contextWindow: 128000,
maxTokens: 8192,
cost: { input: 1.05, output: 3.1, cacheRead: 0, cacheWrite: 0 },
},
{
id: "openai/gpt-oss-120b",
name: "GPT OSS 120B",
reasoning: false,
input: ["text"],
contextWindow: 131000,
maxTokens: 8192,
cost: { input: 0.15, output: 0.55, cacheRead: 0, cacheWrite: 0 },
},
{
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 },
},
{
id: "zai-org/GLM-4.6",
name: "GLM 4.6",
reasoning: false,
input: ["text"],
contextWindow: 200000,
maxTokens: 8192,
cost: { input: 0.85, output: 3.3, cacheRead: 0, cacheWrite: 0 },
},
{
id: "zai-org/GLM-4.7",
name: "GLM 4.7",
reasoning: false,
input: ["text"],
contextWindow: 131072,
maxTokens: 8192,
cost: { input: 0.85, output: 3.3, cacheRead: 0, cacheWrite: 0 },
},
] 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,
};
}

View File

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

View File

@ -17,6 +17,8 @@ import {
applyKimiCodeProviderConfig,
applyMoonshotConfig,
applyMoonshotProviderConfig,
applyNearAiConfig,
applyNearAiProviderConfig,
applyOpencodeZenConfig,
applyOpencodeZenProviderConfig,
applyOpenrouterConfig,
@ -30,6 +32,7 @@ import {
applyZaiConfig,
KIMI_CODE_MODEL_REF,
MOONSHOT_DEFAULT_MODEL_REF,
NEAR_AI_DEFAULT_MODEL_REF,
OPENROUTER_DEFAULT_MODEL_REF,
SYNTHETIC_DEFAULT_MODEL_REF,
VENICE_DEFAULT_MODEL_REF,
@ -37,6 +40,7 @@ import {
setGeminiApiKey,
setKimiCodeApiKey,
setMoonshotApiKey,
setNearAiApiKey,
setOpencodeZenApiKey,
setOpenrouterApiKey,
setSyntheticApiKey,
@ -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 === "near-ai") {
authChoice = "near-ai-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 === "near-ai-api-key") {
let hasCredential = false;
if (!hasCredential && params.opts?.token && params.opts?.tokenProvider === "near-ai") {
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("near-ai");
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: "near-ai:default",
provider: "near-ai",
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

@ -4,6 +4,12 @@ import {
SYNTHETIC_DEFAULT_MODEL_REF,
SYNTHETIC_MODEL_CATALOG,
} from "../agents/synthetic-models.js";
import {
buildNearAiModelDefinition,
NEAR_AI_BASE_URL,
NEAR_AI_DEFAULT_MODEL_REF,
NEAR_AI_MODEL_CATALOG,
} from "../agents/near-ai-models.js";
import {
buildVeniceModelDefinition,
VENICE_BASE_URL,
@ -411,6 +417,81 @@ export function applyVeniceConfig(cfg: ClawdbotConfig): ClawdbotConfig {
};
}
/**
* 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: ClawdbotConfig): ClawdbotConfig {
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 ?? "DeepSeek V3.1",
};
const providers = { ...cfg.models?.providers };
const existingProvider = providers["near-ai"];
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["near-ai"] = {
...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: ClawdbotConfig): ClawdbotConfig {
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: ClawdbotConfig,
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: "near-ai:default",
credential: {
type: "api_key",
provider: "near-ai",
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,12 +3,15 @@ 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/near-ai-models.js";
export {
applyAuthProfileConfig,
applyKimiCodeConfig,
applyKimiCodeProviderConfig,
applyMoonshotConfig,
applyMoonshotProviderConfig,
applyNearAiConfig,
applyNearAiProviderConfig,
applyOpenrouterConfig,
applyOpenrouterProviderConfig,
applySyntheticConfig,
@ -39,6 +42,7 @@ export {
setKimiCodeApiKey,
setMinimaxApiKey,
setMoonshotApiKey,
setNearAiApiKey,
setOpencodeZenApiKey,
setOpenrouterApiKey,
setSyntheticApiKey,

View File

@ -17,6 +17,7 @@ export type AuthChoice =
| "kimi-code-api-key"
| "synthetic-api-key"
| "venice-api-key"
| "near-ai-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;