diff --git a/docs/providers/near-ai.md b/docs/providers/near-ai.md new file mode 100644 index 000000000..6c9ceb5b0 --- /dev/null +++ b/docs/providers/near-ai.md @@ -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) diff --git a/src/agents/model-auth.ts b/src/agents/model-auth.ts index 680d0f53c..f351a77c0 100644 --- a/src/agents/model-auth.ts +++ b/src/agents/model-auth.ts @@ -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; diff --git a/src/agents/models-config.providers.ts b/src/agents/models-config.providers.ts index 996f09dd0..11cb1f9ac 100644 --- a/src/agents/models-config.providers.ts +++ b/src/agents/models-config.providers.ts @@ -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; @@ -350,6 +355,14 @@ async function buildVeniceProvider(): Promise { }; } +function buildNearAiProvider(): ProviderConfig { + return { + baseUrl: NEAR_AI_BASE_URL, + api: "openai-completions", + models: NEAR_AI_MODEL_CATALOG.map(buildNearAiModelDefinition), + }; +} + async function buildOllamaProvider(): Promise { 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"] = { diff --git a/src/agents/near-ai-models.ts b/src/agents/near-ai-models.ts new file mode 100644 index 000000000..034dc01c1 --- /dev/null +++ b/src/agents/near-ai-models.ts @@ -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, + }; +} diff --git a/src/commands/auth-choice-options.ts b/src/commands/auth-choice-options.ts index f13eef365..f86a2f22f 100644 --- a/src/commands/auth-choice-options.ts +++ b/src/commands/auth-choice-options.ts @@ -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)", diff --git a/src/commands/auth-choice.apply.api-providers.ts b/src/commands/auth-choice.apply.api-providers.ts index 8be02008b..15d5eaceb 100644 --- a/src/commands/auth-choice.apply.api-providers.ts +++ b/src/commands/auth-choice.apply.api-providers.ts @@ -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") { diff --git a/src/commands/onboard-auth.config-core.ts b/src/commands/onboard-auth.config-core.ts index 0d3a8523a..7cd23c1ad 100644 --- a/src/commands/onboard-auth.config-core.ts +++ b/src/commands/onboard-auth.config-core.ts @@ -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) + ? { + fallbacks: (existingModel as { fallbacks?: string[] }).fallbacks, + } + : undefined), + primary: NEAR_AI_DEFAULT_MODEL_REF, + }, + }, + }, + }; +} + export function applyAuthProfileConfig( cfg: ClawdbotConfig, params: { diff --git a/src/commands/onboard-auth.credentials.ts b/src/commands/onboard-auth.credentials.ts index 0c7dff409..07731b10f 100644 --- a/src/commands/onboard-auth.credentials.ts +++ b/src/commands/onboard-auth.credentials.ts @@ -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"; diff --git a/src/commands/onboard-auth.ts b/src/commands/onboard-auth.ts index b122d89cf..20a5f4ed7 100644 --- a/src/commands/onboard-auth.ts +++ b/src/commands/onboard-auth.ts @@ -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, diff --git a/src/commands/onboard-types.ts b/src/commands/onboard-types.ts index 84c15afc4..ffdf3e2b6 100644 --- a/src/commands/onboard-types.ts +++ b/src/commands/onboard-types.ts @@ -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;