{"repo": "ca0071/mobimax", "files": {"app/src/main/java/ai/affiora/mobileclaw/agent/AiProvider.kt": {"d37839791303bacfc3f00254c067b4af80ae17c4": {"t": 1785746535.3698304, "n": 14904, "h": "c45a077e30930b14", "c": "package ai.affiora.mobileclaw.agent\n\n/**\n * How credentials are sent to the provider.\n *\n * - [API_KEY_RAW]: value injected directly into a custom header (no Bearer prefix).\n * - [API_KEY]: Authorization: Bearer .\n * - [BEARER_TOKEN]: Authorization: Bearer (semantically an OAuth token).\n */\nenum class AuthType {\n API_KEY,\n API_KEY_RAW,\n BEARER_TOKEN,\n}\n\n/**\n * Supported AI providers: 10 cloud + 1 on-device, 3 auth types.\n */\nenum class AiProvider(\n val id: String,\n val displayName: String,\n val baseUrl: String,\n val authType: AuthType,\n val authHeader: String,\n val tokenHint: String,\n val models: List,\n val extraHeaders: Map = emptyMap(),\n /** Path appended to baseUrl for chat completions. Default matches OpenAI convention. */\n val chatCompletionsPath: String = \"/v1/chat/completions\",\n /** True if user must enter their own baseUrl (e.g. self-hosted Ollama / LM Studio / vLLM). */\n val requiresCustomBaseUrl: Boolean = false,\n) {\n ANTHROPIC(\n id = \"anthropic\",\n displayName = \"Anthropic (API Key)\",\n baseUrl = \"https://api.anthropic.com\",\n authType = AuthType.API_KEY_RAW,\n authHeader = \"x-api-key\",\n tokenHint = \"Paste your Anthropic API key (sk-ant-...)\",\n models = CLAUDE_MODELS,\n extraHeaders = mapOf(\n \"anthropic-version\" to \"2023-06-01\",\n \"anthropic-beta\" to \"fine-grained-tool-streaming-2025-05-14,interleaved-thinking-2025-05-14\",\n ),\n ),\n ANTHROPIC_TOKEN(\n id = \"anthropic-token\",\n displayName = \"Anthropic (Setup Token)\",\n baseUrl = \"https://api.anthropic.com\",\n authType = AuthType.BEARER_TOKEN,\n authHeader = \"Authorization\",\n tokenHint = \"Run `claude setup-token` in terminal and paste here\",\n models = CLAUDE_MODELS,\n extraHeaders = mapOf(\n \"anthropic-version\" to \"2023-06-01\",\n \"anthropic-beta\" to \"claude-code-20250219,oauth-2025-04-20,fine-grained-tool-streaming-2025-05-14,interleaved-thinking-2025-05-14\",\n ),\n ),\n OPENAI(\n id = \"openai\",\n displayName = \"OpenAI\",\n baseUrl = \"https://api.openai.com\",\n authType = AuthType.API_KEY,\n authHeader = \"Authorization\",\n tokenHint = \"Paste your OpenAI API key (sk-...)\",\n models = OPENAI_MODELS,\n ),\n OPENAI_CODEX(\n id = \"openai-codex\",\n displayName = \"OpenAI Codex\",\n baseUrl = \"https://api.openai.com\",\n authType = AuthType.BEARER_TOKEN,\n authHeader = \"Authorization\",\n tokenHint = \"Paste your OpenAI Codex bearer token\",\n models = OPENAI_MODELS,\n ),\n GOOGLE(\n id = \"google\",\n displayName = \"Google Gemini\",\n baseUrl = \"https://generativelanguage.googleapis.com\",\n authType = AuthType.API_KEY_RAW,\n authHeader = \"x-goog-api-key\",\n tokenHint = \"Paste your Google AI API key\",\n models = listOf(\n AiModel(\"gemini-3.1-pro-preview\", \"Gemini 3.1 Pro\"),\n AiModel(\"gemini-3-flash-preview\", \"Gemini 3 Flash\"),\n AiModel(\"gemini-2.5-pro\", \"Gemini 2.5 Pro\"),\n AiModel(\"gemini-2.5-flash\", \"Gemini 2.5 Flash\"),\n AiModel(\"gemini-2.5-flash-lite\", \"Gemini 2.5 Flash-Lite\"),\n AiModel(\"gemma-4-31b-it\", \"Gemma 4 31B (free)\"),\n AiModel(\"gemma-4-26b-a4b-it\", \"Gemma 4 26B (free)\"),\n ),\n ),\n OPENROUTER(\n id = \"openrouter\",\n displayName = \"OpenRouter\",\n baseUrl = \"https://openrouter.ai/api\",\n authType = AuthType.API_KEY,\n authHeader = \"Authorization\",\n tokenHint = \"Paste your OpenRouter API key\",\n models = listOf(\n // Free models\n AiModel(\"google/gemma-4-31b-it:free\", \"Gemma 4 31B (free)\"),\n AiModel(\"google/gemma-4-26b-a4b-it:free\", \"Gemma 4 26B (free)\"),\n AiModel(\"nvidia/nemotron-3-super-120b-a12b:free\", \"Nemotron 3 Super 120B (free)\"),\n AiModel(\"nvidia/nemotron-3-nano-30b-a3b:free\", \"Nemotron 3 Nano 30B (free)\"),\n AiModel(\"minimax/minimax-m2.5:free\", \"MiniMax M2.5 (free)\"),\n AiModel(\"stepfun/step-3.5-flash:free\", \"Step 3.5 Flash (free)\"),\n AiModel(\"arcee-ai/trinity-large-preview:free\", \"Arcee Trinity Large (free)\"),\n AiModel(\"liquid/lfm-2.5-1.2b-thinking:free\", \"Liquid LFM 2.5 1.2B Thinking (free)\"),\n AiModel(\"openrouter/free\", \"Auto (free models router)\"),\n // Paid models\n AiModel(\"anthropic/claude-sonnet-4-6\", \"Claude Sonnet 4.6\"),\n AiModel(\"anthropic/claude-opus-4-6\", \"Claude Opus 4.6\"),\n AiModel(\"openai/gpt-5.4\", \"GPT-5.4\"),\n AiModel(\"google/gemini-2.5-pro\", \"Gemini 2.5 Pro\"),\n AiModel(\"deepseek/deepseek-v4\", \"DeepSeek V4\"),\n AiModel(\"deepseek/deepseek-r1\", \"DeepSeek R1\"),\n ),\n ),\n MISTRAL(\n id = \"mistral\",\n displayName = \"Mistral\",\n baseUrl = \"https://api.mistral.ai\",\n authType = AuthType.API_KEY,\n authHeader = \"Authorization\",\n tokenHint = \"Paste your Mistral API key\",\n models = listOf(\n AiModel(\"mistral-large-latest\", \"Mistral Large 3\"),\n AiModel(\"magistral-medium-latest\", \"Magistral Medium\"),\n AiModel(\"magistral-small-latest\", \"Magistral Small\"),\n AiModel(\"codestral-latest\", \"Codestral\"),\n AiModel(\"ministral-8b-latest\", \"Ministral 8B\"),\n ),\n ),\n TOGETHER(\n id = \"together\",\n displayName = \"Together AI\",\n baseUrl = \"https://api.together.xyz\",\n authType = AuthType.API_KEY,\n authHeader = \"Authorization\",\n tokenHint = \"Paste your Together AI API key\",\n models = listOf(\n AiModel(\"meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8\", \"Llama 4 Maverick\"),\n AiModel(\"meta-llama/Llama-4-Scout-17B-16E-Instruct\", \"Llama 4 Scout\"),\n AiModel(\"meta-llama/Meta-Llama-3.3-70B-Instruct-Turbo\", \"Llama 3.3 70B Turbo\"),\n AiModel(\"deepseek-ai/DeepSeek-V3\", \"DeepSeek V3\"),\n AiModel(\"Qwen/Qwen3-235B-A22B-fp8\", \"Qwen 3 235B\"),\n ),\n ),\n GROQ(\n id = \"groq\",\n displayName = \"Groq\",\n baseUrl = \"https://api.groq.com/openai\",\n authType = AuthType.API_KEY,\n authHeader = \"Authorization\",\n tokenHint = \"Paste your Groq API key\",\n models = listOf(\n AiModel(\"llama-3.3-70b-versatile\", \"Llama 3.3 70B Versatile\"),\n AiModel(\"llama-3.1-8b-instant\", \"Llama 3.1 8B Instant\"),\n AiModel(\"llama4-scout-17b-16e-instruct\", \"Llama 4 Scout\"),\n AiModel(\"qwen-qwq-32b\", \"Qwen QwQ 32B\"),\n ),\n ),\n XAI(\n id = \"xai\",\n displayName = \"xAI (Grok)\",\n baseUrl = \"https://api.x.ai\",\n authType = AuthType.API_KEY,\n authHeader = \"Authorization\",\n tokenHint = \"Paste your xAI API key\",\n models = listOf(\n AiModel(\"grok-4\", \"Grok 4\"),\n AiModel(\"grok-3-beta\", \"Grok 3\"),\n AiModel(\"grok-3-mini-beta\", \"Grok 3 Mini\"),\n ),\n ),\n DEEPSEEK(\n id = \"deepseek\",\n displayName = \"DeepSeek\",\n baseUrl = \"https://api.deepseek.com\",\n authType = AuthType.API_KEY,\n authHeader = \"Authorization\",\n tokenHint = \"Paste your DeepSeek API key\",\n models = listOf(\n AiModel(\"deepseek-chat\", \"DeepSeek V4\"),\n AiModel(\"deepseek-reasoner\", \"DeepSeek R1\"),\n ),\n ),\n FIREWORKS(\n id = \"fireworks\",\n displayName = \"Fireworks AI\",\n baseUrl = \"https://api.fireworks.ai/inference\",\n authType = AuthType.API_KEY,\n authHeader = \"Authorization\",\n tokenHint = \"Paste your Fireworks AI API key\",\n models = listOf(\n AiModel(\"accounts/fireworks/models/llama4-scout-instruct-basic\", \"Llama 4 Scout\"),\n AiModel(\"accounts/fireworks/models/llama4-maverick-instruct-basic\", \"Llama 4 Maverick\"),\n AiModel(\"accounts/fireworks/models/deepseek-v3\", \"DeepSeek V3\"),\n AiModel(\"accounts/fireworks/models/qwen3-235b-a22b\", \"Qwen 3 235B\"),\n ),\n ),\n QWEN(\n id = \"qwen\",\n displayName = \"Qwen (Alibaba)\",\n baseUrl = \"https://dashscope-intl.aliyuncs.com/compatible-mode\",\n authType = AuthType.API_KEY,\n authHeader = \"Authorization\",\n tokenHint = \"Paste your DashScope API key\",\n models = listOf(\n AiModel(\"qwen3.6-plus\", \"Qwen 3.6 Plus\"),\n AiModel(\"qwen3-max\", \"Qwen 3 Max\"),\n AiModel(\"qwen3.5-plus\", \"Qwen 3.5 Plus\"),\n AiModel(\"qwen3.5-flash\", \"Qwen 3.5 Flash\"),\n AiModel(\"qwen3-coder-plus\", \"Qwen 3 Coder Plus\"),\n ),\n ),\n STEPFUN(\n id = \"stepfun\",\n displayName = \"StepFun\",\n baseUrl = \"https://api.stepfun.com\",\n authType = AuthType.API_KEY,\n authHeader = \"Authorization\",\n tokenHint = \"Paste your StepFun API key\",\n models = listOf(\n AiModel(\"step-3.5-flash\", \"Step 3.5 Flash\"),\n AiModel(\"step-2-16k\", \"Step 2 16K\"),\n AiModel(\"step-1-256k\", \"Step 1 256K\"),\n AiModel(\"step-1-128k\", \"Step 1 128K\"),\n ),\n ),\n NVIDIA_NIM(\n id = \"nvidia-nim\",\n displayName = \"NVIDIA NIM\",\n baseUrl = \"https://integrate.api.nvidia.com\",\n authType = AuthType.API_KEY,\n authHeader = \"Authorization\",\n tokenHint = \"Paste your NVIDIA API key (nvapi-...) — get one at build.nvidia.com\",\n models = listOf(\n AiModel(\"meta/llama-3.3-70b-instruct\", \"Llama 3.3 70B\"),\n AiModel(\"meta/llama-4-maverick-17b-128e-instruct\", \"Llama 4 Maverick\"),\n AiModel(\"meta/llama-4-scout-17b-16e-instruct\", \"Llama 4 Scout\"),\n AiModel(\"nvidia/nemotron-3-super-120b-a12b\", \"Nemotron 3 Super 120B\"),\n AiModel(\"nvidia/llama-3.1-nemotron-ultra-253b-v1\", \"Nemotron Ultra 253B\"),\n AiModel(\"minimaxai/minimax-m2.7\", \"MiniMax M2.7\"),\n AiModel(\"minimaxai/minimax-m2.5\", \"MiniMax M2.5\"),\n AiModel(\"deepseek-ai/deepseek-r1\", \"DeepSeek R1\"),\n AiModel(\"deepseek-ai/deepseek-v3\", \"DeepSeek V3\"),\n AiModel(\"qwen/qwen3-235b-a22b\", \"Qwen 3 235B\"),\n AiModel(\"mistralai/mistral-large\", \"Mistral Large\"),\n ),\n ),\n ZAI(\n id = \"zai\",\n displayName = \"z.ai (GLM)\",\n baseUrl = \"https://api.z.ai/api/paas/v4\",\n authType = AuthType.API_KEY,\n authHeader = \"Authorization\",\n tokenHint = \"Paste your z.ai API key — get one at z.ai/model-api\",\n models = listOf(\n AiModel(\"glm-5.1\", \"GLM 5.1\"),\n AiModel(\"glm-4.7\", \"GLM 4.7\"),\n AiModel(\"glm-4.6\", \"GLM 4.6\"),\n AiModel(\"glm-4.5\", \"GLM 4.5\"),\n AiModel(\"glm-4.5-air\", \"GLM 4.5 Air\"),\n AiModel(\"glm-4.7-flash\", \"GLM 4.7 Flash (free)\"),\n AiModel(\"glm-4.5-flash\", \"GLM 4.5 Flash (free)\"),\n ),\n chatCompletionsPath = \"/chat/completions\",\n ),\n CUSTOM(\n id = \"custom\",\n displayName = \"Custom (Ollama / LM Studio / vLLM)\",\n baseUrl = \"\", // user-provided\n authType = AuthType.API_KEY,\n authHeader = \"Authorization\",\n tokenHint = \"Optional bearer token (leave blank for Ollama). Set Base URL + Model ID below.\",\n models = emptyList(), // user-provided\n // User enters full base URL including /v1 (e.g. http://host:11434/v1) — only /chat/completions appended\n chatCompletionsPath = \"/chat/completions\",\n requiresCustomBaseUrl = true,\n ),\n BEDROCK(\n id = \"bedrock\",\n displayName = \"Amazon Bedrock\",\n baseUrl = \"\", // region-specific, built dynamically\n authType = AuthType.API_KEY,\n authHeader = \"\",\n tokenHint = \"Paste JSON: {\\\"access_key\\\":\\\"AKIA...\\\",\\\"secret_key\\\":\\\"...\\\",\\\"region\\\":\\\"us-east-1\\\"}\",\n models = listOf(\n AiModel(\"anthropic.claude-sonnet-4-6-20250514-v1:0\", \"Claude Sonnet 4.6\"),\n AiModel(\"anthropic.claude-haiku-4-5-20251001-v1:0\", \"Claude Haiku 4.5\"),\n AiModel(\"us.anthropic.claude-opus-4-6-20250610-v1:0\", \"Claude Opus 4.6 (cross-region)\"),\n AiModel(\"us.meta.llama4-maverick-17b-instruct-v1:0\", \"Llama 4 Maverick\"),\n AiModel(\"us.meta.llama4-scout-17b-instruct-v1:0\", \"Llama 4 Scout\"),\n AiModel(\"amazon.nova-pro-v1:0\", \"Amazon Nova Pro\"),\n AiModel(\"amazon.nova-lite-v1:0\", \"Amazon Nova Lite\"),\n AiModel(\"mistral.mistral-large-2407-v1:0\", \"Mistral Large 2407\"),\n ),\n ),\n LOCAL_GEMMA(\n id = \"local-gemma\",\n displayName = \"On-Device (Gemma 4)\",\n baseUrl = \"\",\n authType = AuthType.API_KEY,\n authHeader = \"\",\n tokenHint = \"No API key needed — runs on device\",\n models = listOf(\n AiModel(\"gemma-4-e2b\", \"Gemma 4 E2B (2.6 GB)\"),\n AiModel(\"gemma-4-e4b\", \"Gemma 4 E4B (3.7 GB)\"),\n ),\n );\n\n /** The effective provider ID for API routing (anthropic-token → anthropic, openai-codex → openai). */\n val effectiveId: String\n get() = when (this) {\n ANTHROPIC_TOKEN -> \"anthropic\"\n OPENAI_CODEX -> \"openai\"\n else -> id\n }\n\n /** Whether this is an on-device local model provider (no API key, no network). */\n val isLocal: Boolean\n get() = this == LOCAL_GEMMA\n\n /** Whether this is Amazon Bedrock (uses SigV4 auth + Converse API). */\n val isBedrock: Boolean\n get() = this == BEDROCK\n\n /** Whether this is an Anthropic-compatible provider. */\n val isAnthropic: Boolean\n get() = this == ANTHROPIC || this == ANTHROPIC_TOKEN\n\n /** Whether this is an OpenAI-compatible provider. */\n val isOpenAiCompatible: Boolean\n get() = this in OPENAI_COMPATIBLE_PROVIDERS\n\n companion object {\n private val OPENAI_COMPATIBLE_PROVIDERS = setOf(\n OPENAI, OPENAI_CODEX, OPENROUTER, MISTRAL,\n TOGETHER, GROQ, XAI, DEEPSEEK, FIREWORKS,\n QWEN, STEPFUN, NVIDIA_NIM, ZAI, CUSTOM,\n )\n\n fun fromId(id: String): AiProvider =\n entries.firstOrNull { it.id == id } ?: ANTHROPIC\n }\n}\n\nprivate val CLAUDE_MODELS = listOf(\n AiModel(\"claude-sonnet-4-6\", \"Claude Sonnet 4.6\"),\n AiModel(\"claude-haiku-4-5\", \"Claude Haiku 4.5\"),\n AiModel(\"claude-opus-4-6\", \"Claude Opus 4.6\"),\n)\n\nprivate val OPENAI_MODELS = listOf(\n AiModel(\"gpt-5.4\", \"GPT-5.4\"),\n AiModel(\"gpt-5.4-mini\", \"GPT-5.4 Mini\"),\n AiModel(\"gpt-5.4-nano\", \"GPT-5.4 Nano\"),\n AiModel(\"gpt-4.1\", \"GPT-4.1\"),\n AiModel(\"gpt-4.1-mini\", \"GPT-4.1 Mini\"),\n AiModel(\"gpt-4o\", \"GPT-4o\"),\n AiModel(\"o3-mini\", \"o3-mini\"),\n)\n\ndata class AiModel(\n val id: String,\n val displayName: String,\n)\n"}, "0cff3efbb22a583a894f95a404ec3395b9053563": {"t": 1785765421.5448155, "n": 14904, "h": "c45a077e30930b14", "c": "package ai.affiora.mobileclaw.agent\n\n/**\n * How credentials are sent to the provider.\n *\n * - [API_KEY_RAW]: value injected directly into a custom header (no Bearer prefix).\n * - [API_KEY]: Authorization: Bearer .\n * - [BEARER_TOKEN]: Authorization: Bearer (semantically an OAuth token).\n */\nenum class AuthType {\n API_KEY,\n API_KEY_RAW,\n BEARER_TOKEN,\n}\n\n/**\n * Supported AI providers: 10 cloud + 1 on-device, 3 auth types.\n */\nenum class AiProvider(\n val id: String,\n val displayName: String,\n val baseUrl: String,\n val authType: AuthType,\n val authHeader: String,\n val tokenHint: String,\n val models: List,\n val extraHeaders: Map = emptyMap(),\n /** Path appended to baseUrl for chat completions. Default matches OpenAI convention. */\n val chatCompletionsPath: String = \"/v1/chat/completions\",\n /** True if user must enter their own baseUrl (e.g. self-hosted Ollama / LM Studio / vLLM). */\n val requiresCustomBaseUrl: Boolean = false,\n) {\n ANTHROPIC(\n id = \"anthropic\",\n displayName = \"Anthropic (API Key)\",\n baseUrl = \"https://api.anthropic.com\",\n authType = AuthType.API_KEY_RAW,\n authHeader = \"x-api-key\",\n tokenHint = \"Paste your Anthropic API key (sk-ant-...)\",\n models = CLAUDE_MODELS,\n extraHeaders = mapOf(\n \"anthropic-version\" to \"2023-06-01\",\n \"anthropic-beta\" to \"fine-grained-tool-streaming-2025-05-14,interleaved-thinking-2025-05-14\",\n ),\n ),\n ANTHROPIC_TOKEN(\n id = \"anthropic-token\",\n displayName = \"Anthropic (Setup Token)\",\n baseUrl = \"https://api.anthropic.com\",\n authType = AuthType.BEARER_TOKEN,\n authHeader = \"Authorization\",\n tokenHint = \"Run `claude setup-token` in terminal and paste here\",\n models = CLAUDE_MODELS,\n extraHeaders = mapOf(\n \"anthropic-version\" to \"2023-06-01\",\n \"anthropic-beta\" to \"claude-code-20250219,oauth-2025-04-20,fine-grained-tool-streaming-2025-05-14,interleaved-thinking-2025-05-14\",\n ),\n ),\n OPENAI(\n id = \"openai\",\n displayName = \"OpenAI\",\n baseUrl = \"https://api.openai.com\",\n authType = AuthType.API_KEY,\n authHeader = \"Authorization\",\n tokenHint = \"Paste your OpenAI API key (sk-...)\",\n models = OPENAI_MODELS,\n ),\n OPENAI_CODEX(\n id = \"openai-codex\",\n displayName = \"OpenAI Codex\",\n baseUrl = \"https://api.openai.com\",\n authType = AuthType.BEARER_TOKEN,\n authHeader = \"Authorization\",\n tokenHint = \"Paste your OpenAI Codex bearer token\",\n models = OPENAI_MODELS,\n ),\n GOOGLE(\n id = \"google\",\n displayName = \"Google Gemini\",\n baseUrl = \"https://generativelanguage.googleapis.com\",\n authType = AuthType.API_KEY_RAW,\n authHeader = \"x-goog-api-key\",\n tokenHint = \"Paste your Google AI API key\",\n models = listOf(\n AiModel(\"gemini-3.1-pro-preview\", \"Gemini 3.1 Pro\"),\n AiModel(\"gemini-3-flash-preview\", \"Gemini 3 Flash\"),\n AiModel(\"gemini-2.5-pro\", \"Gemini 2.5 Pro\"),\n AiModel(\"gemini-2.5-flash\", \"Gemini 2.5 Flash\"),\n AiModel(\"gemini-2.5-flash-lite\", \"Gemini 2.5 Flash-Lite\"),\n AiModel(\"gemma-4-31b-it\", \"Gemma 4 31B (free)\"),\n AiModel(\"gemma-4-26b-a4b-it\", \"Gemma 4 26B (free)\"),\n ),\n ),\n OPENROUTER(\n id = \"openrouter\",\n displayName = \"OpenRouter\",\n baseUrl = \"https://openrouter.ai/api\",\n authType = AuthType.API_KEY,\n authHeader = \"Authorization\",\n tokenHint = \"Paste your OpenRouter API key\",\n models = listOf(\n // Free models\n AiModel(\"google/gemma-4-31b-it:free\", \"Gemma 4 31B (free)\"),\n AiModel(\"google/gemma-4-26b-a4b-it:free\", \"Gemma 4 26B (free)\"),\n AiModel(\"nvidia/nemotron-3-super-120b-a12b:free\", \"Nemotron 3 Super 120B (free)\"),\n AiModel(\"nvidia/nemotron-3-nano-30b-a3b:free\", \"Nemotron 3 Nano 30B (free)\"),\n AiModel(\"minimax/minimax-m2.5:free\", \"MiniMax M2.5 (free)\"),\n AiModel(\"stepfun/step-3.5-flash:free\", \"Step 3.5 Flash (free)\"),\n AiModel(\"arcee-ai/trinity-large-preview:free\", \"Arcee Trinity Large (free)\"),\n AiModel(\"liquid/lfm-2.5-1.2b-thinking:free\", \"Liquid LFM 2.5 1.2B Thinking (free)\"),\n AiModel(\"openrouter/free\", \"Auto (free models router)\"),\n // Paid models\n AiModel(\"anthropic/claude-sonnet-4-6\", \"Claude Sonnet 4.6\"),\n AiModel(\"anthropic/claude-opus-4-6\", \"Claude Opus 4.6\"),\n AiModel(\"openai/gpt-5.4\", \"GPT-5.4\"),\n AiModel(\"google/gemini-2.5-pro\", \"Gemini 2.5 Pro\"),\n AiModel(\"deepseek/deepseek-v4\", \"DeepSeek V4\"),\n AiModel(\"deepseek/deepseek-r1\", \"DeepSeek R1\"),\n ),\n ),\n MISTRAL(\n id = \"mistral\",\n displayName = \"Mistral\",\n baseUrl = \"https://api.mistral.ai\",\n authType = AuthType.API_KEY,\n authHeader = \"Authorization\",\n tokenHint = \"Paste your Mistral API key\",\n models = listOf(\n AiModel(\"mistral-large-latest\", \"Mistral Large 3\"),\n AiModel(\"magistral-medium-latest\", \"Magistral Medium\"),\n AiModel(\"magistral-small-latest\", \"Magistral Small\"),\n AiModel(\"codestral-latest\", \"Codestral\"),\n AiModel(\"ministral-8b-latest\", \"Ministral 8B\"),\n ),\n ),\n TOGETHER(\n id = \"together\",\n displayName = \"Together AI\",\n baseUrl = \"https://api.together.xyz\",\n authType = AuthType.API_KEY,\n authHeader = \"Authorization\",\n tokenHint = \"Paste your Together AI API key\",\n models = listOf(\n AiModel(\"meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8\", \"Llama 4 Maverick\"),\n AiModel(\"meta-llama/Llama-4-Scout-17B-16E-Instruct\", \"Llama 4 Scout\"),\n AiModel(\"meta-llama/Meta-Llama-3.3-70B-Instruct-Turbo\", \"Llama 3.3 70B Turbo\"),\n AiModel(\"deepseek-ai/DeepSeek-V3\", \"DeepSeek V3\"),\n AiModel(\"Qwen/Qwen3-235B-A22B-fp8\", \"Qwen 3 235B\"),\n ),\n ),\n GROQ(\n id = \"groq\",\n displayName = \"Groq\",\n baseUrl = \"https://api.groq.com/openai\",\n authType = AuthType.API_KEY,\n authHeader = \"Authorization\",\n tokenHint = \"Paste your Groq API key\",\n models = listOf(\n AiModel(\"llama-3.3-70b-versatile\", \"Llama 3.3 70B Versatile\"),\n AiModel(\"llama-3.1-8b-instant\", \"Llama 3.1 8B Instant\"),\n AiModel(\"llama4-scout-17b-16e-instruct\", \"Llama 4 Scout\"),\n AiModel(\"qwen-qwq-32b\", \"Qwen QwQ 32B\"),\n ),\n ),\n XAI(\n id = \"xai\",\n displayName = \"xAI (Grok)\",\n baseUrl = \"https://api.x.ai\",\n authType = AuthType.API_KEY,\n authHeader = \"Authorization\",\n tokenHint = \"Paste your xAI API key\",\n models = listOf(\n AiModel(\"grok-4\", \"Grok 4\"),\n AiModel(\"grok-3-beta\", \"Grok 3\"),\n AiModel(\"grok-3-mini-beta\", \"Grok 3 Mini\"),\n ),\n ),\n DEEPSEEK(\n id = \"deepseek\",\n displayName = \"DeepSeek\",\n baseUrl = \"https://api.deepseek.com\",\n authType = AuthType.API_KEY,\n authHeader = \"Authorization\",\n tokenHint = \"Paste your DeepSeek API key\",\n models = listOf(\n AiModel(\"deepseek-chat\", \"DeepSeek V4\"),\n AiModel(\"deepseek-reasoner\", \"DeepSeek R1\"),\n ),\n ),\n FIREWORKS(\n id = \"fireworks\",\n displayName = \"Fireworks AI\",\n baseUrl = \"https://api.fireworks.ai/inference\",\n authType = AuthType.API_KEY,\n authHeader = \"Authorization\",\n tokenHint = \"Paste your Fireworks AI API key\",\n models = listOf(\n AiModel(\"accounts/fireworks/models/llama4-scout-instruct-basic\", \"Llama 4 Scout\"),\n AiModel(\"accounts/fireworks/models/llama4-maverick-instruct-basic\", \"Llama 4 Maverick\"),\n AiModel(\"accounts/fireworks/models/deepseek-v3\", \"DeepSeek V3\"),\n AiModel(\"accounts/fireworks/models/qwen3-235b-a22b\", \"Qwen 3 235B\"),\n ),\n ),\n QWEN(\n id = \"qwen\",\n displayName = \"Qwen (Alibaba)\",\n baseUrl = \"https://dashscope-intl.aliyuncs.com/compatible-mode\",\n authType = AuthType.API_KEY,\n authHeader = \"Authorization\",\n tokenHint = \"Paste your DashScope API key\",\n models = listOf(\n AiModel(\"qwen3.6-plus\", \"Qwen 3.6 Plus\"),\n AiModel(\"qwen3-max\", \"Qwen 3 Max\"),\n AiModel(\"qwen3.5-plus\", \"Qwen 3.5 Plus\"),\n AiModel(\"qwen3.5-flash\", \"Qwen 3.5 Flash\"),\n AiModel(\"qwen3-coder-plus\", \"Qwen 3 Coder Plus\"),\n ),\n ),\n STEPFUN(\n id = \"stepfun\",\n displayName = \"StepFun\",\n baseUrl = \"https://api.stepfun.com\",\n authType = AuthType.API_KEY,\n authHeader = \"Authorization\",\n tokenHint = \"Paste your StepFun API key\",\n models = listOf(\n AiModel(\"step-3.5-flash\", \"Step 3.5 Flash\"),\n AiModel(\"step-2-16k\", \"Step 2 16K\"),\n AiModel(\"step-1-256k\", \"Step 1 256K\"),\n AiModel(\"step-1-128k\", \"Step 1 128K\"),\n ),\n ),\n NVIDIA_NIM(\n id = \"nvidia-nim\",\n displayName = \"NVIDIA NIM\",\n baseUrl = \"https://integrate.api.nvidia.com\",\n authType = AuthType.API_KEY,\n authHeader = \"Authorization\",\n tokenHint = \"Paste your NVIDIA API key (nvapi-...) — get one at build.nvidia.com\",\n models = listOf(\n AiModel(\"meta/llama-3.3-70b-instruct\", \"Llama 3.3 70B\"),\n AiModel(\"meta/llama-4-maverick-17b-128e-instruct\", \"Llama 4 Maverick\"),\n AiModel(\"meta/llama-4-scout-17b-16e-instruct\", \"Llama 4 Scout\"),\n AiModel(\"nvidia/nemotron-3-super-120b-a12b\", \"Nemotron 3 Super 120B\"),\n AiModel(\"nvidia/llama-3.1-nemotron-ultra-253b-v1\", \"Nemotron Ultra 253B\"),\n AiModel(\"minimaxai/minimax-m2.7\", \"MiniMax M2.7\"),\n AiModel(\"minimaxai/minimax-m2.5\", \"MiniMax M2.5\"),\n AiModel(\"deepseek-ai/deepseek-r1\", \"DeepSeek R1\"),\n AiModel(\"deepseek-ai/deepseek-v3\", \"DeepSeek V3\"),\n AiModel(\"qwen/qwen3-235b-a22b\", \"Qwen 3 235B\"),\n AiModel(\"mistralai/mistral-large\", \"Mistral Large\"),\n ),\n ),\n ZAI(\n id = \"zai\",\n displayName = \"z.ai (GLM)\",\n baseUrl = \"https://api.z.ai/api/paas/v4\",\n authType = AuthType.API_KEY,\n authHeader = \"Authorization\",\n tokenHint = \"Paste your z.ai API key — get one at z.ai/model-api\",\n models = listOf(\n AiModel(\"glm-5.1\", \"GLM 5.1\"),\n AiModel(\"glm-4.7\", \"GLM 4.7\"),\n AiModel(\"glm-4.6\", \"GLM 4.6\"),\n AiModel(\"glm-4.5\", \"GLM 4.5\"),\n AiModel(\"glm-4.5-air\", \"GLM 4.5 Air\"),\n AiModel(\"glm-4.7-flash\", \"GLM 4.7 Flash (free)\"),\n AiModel(\"glm-4.5-flash\", \"GLM 4.5 Flash (free)\"),\n ),\n chatCompletionsPath = \"/chat/completions\",\n ),\n CUSTOM(\n id = \"custom\",\n displayName = \"Custom (Ollama / LM Studio / vLLM)\",\n baseUrl = \"\", // user-provided\n authType = AuthType.API_KEY,\n authHeader = \"Authorization\",\n tokenHint = \"Optional bearer token (leave blank for Ollama). Set Base URL + Model ID below.\",\n models = emptyList(), // user-provided\n // User enters full base URL including /v1 (e.g. http://host:11434/v1) — only /chat/completions appended\n chatCompletionsPath = \"/chat/completions\",\n requiresCustomBaseUrl = true,\n ),\n BEDROCK(\n id = \"bedrock\",\n displayName = \"Amazon Bedrock\",\n baseUrl = \"\", // region-specific, built dynamically\n authType = AuthType.API_KEY,\n authHeader = \"\",\n tokenHint = \"Paste JSON: {\\\"access_key\\\":\\\"AKIA...\\\",\\\"secret_key\\\":\\\"...\\\",\\\"region\\\":\\\"us-east-1\\\"}\",\n models = listOf(\n AiModel(\"anthropic.claude-sonnet-4-6-20250514-v1:0\", \"Claude Sonnet 4.6\"),\n AiModel(\"anthropic.claude-haiku-4-5-20251001-v1:0\", \"Claude Haiku 4.5\"),\n AiModel(\"us.anthropic.claude-opus-4-6-20250610-v1:0\", \"Claude Opus 4.6 (cross-region)\"),\n AiModel(\"us.meta.llama4-maverick-17b-instruct-v1:0\", \"Llama 4 Maverick\"),\n AiModel(\"us.meta.llama4-scout-17b-instruct-v1:0\", \"Llama 4 Scout\"),\n AiModel(\"amazon.nova-pro-v1:0\", \"Amazon Nova Pro\"),\n AiModel(\"amazon.nova-lite-v1:0\", \"Amazon Nova Lite\"),\n AiModel(\"mistral.mistral-large-2407-v1:0\", \"Mistral Large 2407\"),\n ),\n ),\n LOCAL_GEMMA(\n id = \"local-gemma\",\n displayName = \"On-Device (Gemma 4)\",\n baseUrl = \"\",\n authType = AuthType.API_KEY,\n authHeader = \"\",\n tokenHint = \"No API key needed — runs on device\",\n models = listOf(\n AiModel(\"gemma-4-e2b\", \"Gemma 4 E2B (2.6 GB)\"),\n AiModel(\"gemma-4-e4b\", \"Gemma 4 E4B (3.7 GB)\"),\n ),\n );\n\n /** The effective provider ID for API routing (anthropic-token → anthropic, openai-codex → openai). */\n val effectiveId: String\n get() = when (this) {\n ANTHROPIC_TOKEN -> \"anthropic\"\n OPENAI_CODEX -> \"openai\"\n else -> id\n }\n\n /** Whether this is an on-device local model provider (no API key, no network). */\n val isLocal: Boolean\n get() = this == LOCAL_GEMMA\n\n /** Whether this is Amazon Bedrock (uses SigV4 auth + Converse API). */\n val isBedrock: Boolean\n get() = this == BEDROCK\n\n /** Whether this is an Anthropic-compatible provider. */\n val isAnthropic: Boolean\n get() = this == ANTHROPIC || this == ANTHROPIC_TOKEN\n\n /** Whether this is an OpenAI-compatible provider. */\n val isOpenAiCompatible: Boolean\n get() = this in OPENAI_COMPATIBLE_PROVIDERS\n\n companion object {\n private val OPENAI_COMPATIBLE_PROVIDERS = setOf(\n OPENAI, OPENAI_CODEX, OPENROUTER, MISTRAL,\n TOGETHER, GROQ, XAI, DEEPSEEK, FIREWORKS,\n QWEN, STEPFUN, NVIDIA_NIM, ZAI, CUSTOM,\n )\n\n fun fromId(id: String): AiProvider =\n entries.firstOrNull { it.id == id } ?: ANTHROPIC\n }\n}\n\nprivate val CLAUDE_MODELS = listOf(\n AiModel(\"claude-sonnet-4-6\", \"Claude Sonnet 4.6\"),\n AiModel(\"claude-haiku-4-5\", \"Claude Haiku 4.5\"),\n AiModel(\"claude-opus-4-6\", \"Claude Opus 4.6\"),\n)\n\nprivate val OPENAI_MODELS = listOf(\n AiModel(\"gpt-5.4\", \"GPT-5.4\"),\n AiModel(\"gpt-5.4-mini\", \"GPT-5.4 Mini\"),\n AiModel(\"gpt-5.4-nano\", \"GPT-5.4 Nano\"),\n AiModel(\"gpt-4.1\", \"GPT-4.1\"),\n AiModel(\"gpt-4.1-mini\", \"GPT-4.1 Mini\"),\n AiModel(\"gpt-4o\", \"GPT-4o\"),\n AiModel(\"o3-mini\", \"o3-mini\"),\n)\n\ndata class AiModel(\n val id: String,\n val displayName: String,\n)\n"}, "be8565cc35a01292754eddb261fb17e08b7ea064": {"t": 1785765422.8618884, "n": 14778, "h": "8e8fd7b170567295", "c": "package ai.affiora.mobileclaw.agent\n\n/**\n * How credentials are sent to the provider.\n *\n * - [API_KEY_RAW]: value injected directly into a custom header (no Bearer prefix).\n * - [API_KEY]: Authorization: Bearer .\n * - [BEARER_TOKEN]: Authorization: Bearer (semantically an OAuth token).\n */\nenum class AuthType {\n API_KEY,\n API_KEY_RAW,\n BEARER_TOKEN,\n}\n\n/**\n * Supported AI providers: 10 cloud + 1 on-device, 3 auth types.\n */\nenum class AiProvider(\n val id: String,\n val displayName: String,\n val baseUrl: String,\n val authType: AuthType,\n val authHeader: String,\n val tokenHint: String,\n val models: List,\n val extraHeaders: Map = emptyMap(),\n /** Path appended to baseUrl for chat completions. Default matches OpenAI convention. */\n val chatCompletionsPath: String = \"/v1/chat/completions\",\n /** True if user must enter their own baseUrl (e.g. self-hosted Ollama / LM Studio / vLLM). */\n val requiresCustomBaseUrl: Boolean = false,\n) {\n ANTHROPIC(\n id = \"anthropic\",\n displayName = \"Anthropic (API Key)\",\n baseUrl = \"https://api.anthropic.com\",\n authType = AuthType.API_KEY_RAW,\n authHeader = \"x-api-key\",\n tokenHint = \"Paste your Anthropic API key (sk-ant-...)\",\n models = CLAUDE_MODELS,\n extraHeaders = mapOf(\n \"anthropic-version\" to \"2023-06-01\",\n \"anthropic-beta\" to \"fine-grained-tool-streaming-2025-05-14,interleaved-thinking-2025-05-14\",\n ),\n ),\n ANTHROPIC_TOKEN(\n id = \"anthropic-token\",\n displayName = \"Anthropic (Setup Token)\",\n baseUrl = \"https://api.anthropic.com\",\n authType = AuthType.BEARER_TOKEN,\n authHeader = \"Authorization\",\n tokenHint = \"Run `claude setup-token` in terminal and paste here\",\n models = CLAUDE_MODELS,\n extraHeaders = mapOf(\n \"anthropic-version\" to \"2023-06-01\",\n \"anthropic-beta\" to \"claude-code-20250219,oauth-2025-04-20,fine-grained-tool-streaming-2025-05-14,interleaved-thinking-2025-05-14\",\n ),\n ),\n OPENAI(\n id = \"openai\",\n displayName = \"OpenAI\",\n baseUrl = \"https://api.openai.com\",\n authType = AuthType.API_KEY,\n authHeader = \"Authorization\",\n tokenHint = \"Paste your OpenAI API key (sk-...)\",\n models = OPENAI_MODELS,\n ),\n OPENAI_CODEX(\n id = \"openai-codex\",\n displayName = \"OpenAI Codex\",\n baseUrl = \"https://api.openai.com\",\n authType = AuthType.BEARER_TOKEN,\n authHeader = \"Authorization\",\n tokenHint = \"Paste your OpenAI Codex bearer token\",\n models = OPENAI_MODELS,\n ),\n GOOGLE(\n id = \"google\",\n displayName = \"Google Gemini\",\n baseUrl = \"https://generativelanguage.googleapis.com\",\n authType = AuthType.API_KEY_RAW,\n authHeader = \"x-goog-api-key\",\n tokenHint = \"Paste your Google AI API key\",\n models = listOf(\n AiModel(\"gemini-3.1-pro-preview\", \"Gemini 3.1 Pro\"),\n AiModel(\"gemini-3-flash-preview\", \"Gemini 3 Flash\"),\n AiModel(\"gemini-2.5-pro\", \"Gemini 2.5 Pro\"),\n AiModel(\"gemini-2.5-flash\", \"Gemini 2.5 Flash\"),\n AiModel(\"gemini-2.5-flash-lite\", \"Gemini 2.5 Flash-Lite\"),\n AiModel(\"gemma-4-31b-it\", \"Gemma 4 31B (free)\"),\n AiModel(\"gemma-4-26b-a4b-it\", \"Gemma 4 26B (free)\"),\n ),\n ),\n OPENROUTER(\n id = \"openrouter\",\n displayName = \"OpenRouter\",\n baseUrl = \"https://openrouter.ai/api\",\n authType = AuthType.API_KEY,\n authHeader = \"Authorization\",\n tokenHint = \"Paste your OpenRouter API key\",\n models = listOf(\n // Free models\n AiModel(\"google/gemma-4-31b-it:free\", \"Gemma 4 31B (free)\"),\n AiModel(\"google/gemma-4-26b-a4b-it:free\", \"Gemma 4 26B (free)\"),\n AiModel(\"nvidia/nemotron-3-super-120b-a12b:free\", \"Nemotron 3 Super 120B (free)\"),\n AiModel(\"nvidia/nemotron-3-nano-30b-a3b:free\", \"Nemotron 3 Nano 30B (free)\"),\n AiModel(\"minimax/minimax-m2.5:free\", \"MiniMax M2.5 (free)\"),\n AiModel(\"stepfun/step-3.5-flash:free\", \"Step 3.5 Flash (free)\"),\n AiModel(\"arcee-ai/trinity-large-preview:free\", \"Arcee Trinity Large (free)\"),\n AiModel(\"liquid/lfm-2.5-1.2b-thinking:free\", \"Liquid LFM 2.5 1.2B Thinking (free)\"),\n AiModel(\"openrouter/free\", \"Auto (free models router)\"),\n // Paid models\n AiModel(\"anthropic/claude-sonnet-4-6\", \"Claude Sonnet 4.6\"),\n AiModel(\"anthropic/claude-opus-4-6\", \"Claude Opus 4.6\"),\n AiModel(\"openai/gpt-5.4\", \"GPT-5.4\"),\n AiModel(\"google/gemini-2.5-pro\", \"Gemini 2.5 Pro\"),\n AiModel(\"deepseek/deepseek-v4\", \"DeepSeek V4\"),\n AiModel(\"deepseek/deepseek-r1\", \"DeepSeek R1\"),\n ),\n ),\n MISTRAL(\n id = \"mistral\",\n displayName = \"Mistral\",\n baseUrl = \"https://api.mistral.ai\",\n authType = AuthType.API_KEY,\n authHeader = \"Authorization\",\n tokenHint = \"Paste your Mistral API key\",\n models = listOf(\n AiModel(\"mistral-large-latest\", \"Mistral Large 3\"),\n AiModel(\"magistral-medium-latest\", \"Magistral Medium\"),\n AiModel(\"magistral-small-latest\", \"Magistral Small\"),\n AiModel(\"codestral-latest\", \"Codestral\"),\n AiModel(\"ministral-8b-latest\", \"Ministral 8B\"),\n ),\n ),\n TOGETHER(\n id = \"together\",\n displayName = \"Together AI\",\n baseUrl = \"https://api.together.xyz\",\n authType = AuthType.API_KEY,\n authHeader = \"Authorization\",\n tokenHint = \"Paste your Together AI API key\",\n models = listOf(\n AiModel(\"meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8\", \"Llama 4 Maverick\"),\n AiModel(\"meta-llama/Llama-4-Scout-17B-16E-Instruct\", \"Llama 4 Scout\"),\n AiModel(\"meta-llama/Meta-Llama-3.3-70B-Instruct-Turbo\", \"Llama 3.3 70B Turbo\"),\n AiModel(\"deepseek-ai/DeepSeek-V3\", \"DeepSeek V3\"),\n AiModel(\"Qwen/Qwen3-235B-A22B-fp8\", \"Qwen 3 235B\"),\n ),\n ),\n GROQ(\n id = \"groq\",\n displayName = \"Groq\",\n baseUrl = \"https://api.groq.com/openai\",\n authType = AuthType.API_KEY,\n authHeader = \"Authorization\",\n tokenHint = \"Paste your Groq API key\",\n models = listOf(\n AiModel(\"llama-3.3-70b-versatile\", \"Llama 3.3 70B Versatile\"),\n AiModel(\"llama-3.1-8b-instant\", \"Llama 3.1 8B Instant\"),\n AiModel(\"llama4-scout-17b-16e-instruct\", \"Llama 4 Scout\"),\n AiModel(\"qwen-qwq-32b\", \"Qwen QwQ 32B\"),\n ),\n ),\n XAI(\n id = \"xai\",\n displayName = \"xAI (Grok)\",\n baseUrl = \"https://api.x.ai\",\n authType = AuthType.API_KEY,\n authHeader = \"Authorization\",\n tokenHint = \"Paste your xAI API key\",\n models = listOf(\n AiModel(\"grok-4\", \"Grok 4\"),\n AiModel(\"grok-3-beta\", \"Grok 3\"),\n AiModel(\"grok-3-mini-beta\", \"Grok 3 Mini\"),\n ),\n ),\n DEEPSEEK(\n id = \"deepseek\",\n displayName = \"DeepSeek\",\n baseUrl = \"https://api.deepseek.com\",\n authType = AuthType.API_KEY,\n authHeader = \"Authorization\",\n tokenHint = \"Paste your DeepSeek API key\",\n models = listOf(\n AiModel(\"deepseek-chat\", \"DeepSeek V4\"),\n AiModel(\"deepseek-reasoner\", \"DeepSeek R1\"),\n ),\n ),\n FIREWORKS(\n id = \"fireworks\",\n displayName = \"Fireworks AI\",\n baseUrl = \"https://api.fireworks.ai/inference\",\n authType = AuthType.API_KEY,\n authHeader = \"Authorization\",\n tokenHint = \"Paste your Fireworks AI API key\",\n models = listOf(\n AiModel(\"accounts/fireworks/models/llama4-scout-instruct-basic\", \"Llama 4 Scout\"),\n AiModel(\"accounts/fireworks/models/llama4-maverick-instruct-basic\", \"Llama 4 Maverick\"),\n AiModel(\"accounts/fireworks/models/deepseek-v3\", \"DeepSeek V3\"),\n AiModel(\"accounts/fireworks/models/qwen3-235b-a22b\", \"Qwen 3 235B\"),\n ),\n ),\n QWEN(\n id = \"qwen\",\n displayName = \"Qwen (Alibaba)\",\n baseUrl = \"https://dashscope-intl.aliyuncs.com/compatible-mode\",\n authType = AuthType.API_KEY,\n authHeader = \"Authorization\",\n tokenHint = \"Paste your DashScope API key\",\n models = listOf(\n AiModel(\"qwen3.6-plus\", \"Qwen 3.6 Plus\"),\n AiModel(\"qwen3-max\", \"Qwen 3 Max\"),\n AiModel(\"qwen3.5-plus\", \"Qwen 3.5 Plus\"),\n AiModel(\"qwen3.5-flash\", \"Qwen 3.5 Flash\"),\n AiModel(\"qwen3-coder-plus\", \"Qwen 3 Coder Plus\"),\n ),\n ),\n STEPFUN(\n id = \"stepfun\",\n displayName = \"StepFun\",\n baseUrl = \"https://api.stepfun.com\",\n authType = AuthType.API_KEY,\n authHeader = \"Authorization\",\n tokenHint = \"Paste your StepFun API key\",\n models = listOf(\n AiModel(\"step-3.5-flash\", \"Step 3.5 Flash\"),\n AiModel(\"step-2-16k\", \"Step 2 16K\"),\n AiModel(\"step-1-256k\", \"Step 1 256K\"),\n AiModel(\"step-1-128k\", \"Step 1 128K\"),\n ),\n ),\n NVIDIA_NIM(\n id = \"nvidia-nim\",\n displayName = \"NVIDIA NIM\",\n baseUrl = \"https://integrate.api.nvidia.com\",\n authType = AuthType.API_KEY,\n authHeader = \"Authorization\",\n tokenHint = \"Paste your NVIDIA API key (nvapi-...) — get one at build.nvidia.com\",\n models = listOf(\n AiModel(\"meta/llama-3.3-70b-instruct\", \"Llama 3.3 70B\"),\n AiModel(\"meta/llama-4-maverick-17b-128e-instruct\", \"Llama 4 Maverick\"),\n AiModel(\"meta/llama-4-scout-17b-16e-instruct\", \"Llama 4 Scout\"),\n AiModel(\"nvidia/nemotron-3-super-120b-a12b\", \"Nemotron 3 Super 120B\"),\n AiModel(\"nvidia/llama-3.1-nemotron-ultra-253b-v1\", \"Nemotron Ultra 253B\"),\n AiModel(\"deepseek-ai/deepseek-r1\", \"DeepSeek R1\"),\n AiModel(\"deepseek-ai/deepseek-v3\", \"DeepSeek V3\"),\n AiModel(\"qwen/qwen3-235b-a22b\", \"Qwen 3 235B\"),\n AiModel(\"mistralai/mistral-large\", \"Mistral Large\"),\n ),\n ),\n ZAI(\n id = \"zai\",\n displayName = \"z.ai (GLM)\",\n baseUrl = \"https://api.z.ai/api/paas/v4\",\n authType = AuthType.API_KEY,\n authHeader = \"Authorization\",\n tokenHint = \"Paste your z.ai API key — get one at z.ai/model-api\",\n models = listOf(\n AiModel(\"glm-5.1\", \"GLM 5.1\"),\n AiModel(\"glm-4.7\", \"GLM 4.7\"),\n AiModel(\"glm-4.6\", \"GLM 4.6\"),\n AiModel(\"glm-4.5\", \"GLM 4.5\"),\n AiModel(\"glm-4.5-air\", \"GLM 4.5 Air\"),\n AiModel(\"glm-4.7-flash\", \"GLM 4.7 Flash (free)\"),\n AiModel(\"glm-4.5-flash\", \"GLM 4.5 Flash (free)\"),\n ),\n chatCompletionsPath = \"/chat/completions\",\n ),\n CUSTOM(\n id = \"custom\",\n displayName = \"Custom (Ollama / LM Studio / vLLM)\",\n baseUrl = \"\", // user-provided\n authType = AuthType.API_KEY,\n authHeader = \"Authorization\",\n tokenHint = \"Optional bearer token (leave blank for Ollama). Set Base URL + Model ID below.\",\n models = emptyList(), // user-provided\n // User enters full base URL including /v1 (e.g. http://host:11434/v1) — only /chat/completions appended\n chatCompletionsPath = \"/chat/completions\",\n requiresCustomBaseUrl = true,\n ),\n BEDROCK(\n id = \"bedrock\",\n displayName = \"Amazon Bedrock\",\n baseUrl = \"\", // region-specific, built dynamically\n authType = AuthType.API_KEY,\n authHeader = \"\",\n tokenHint = \"Paste JSON: {\\\"access_key\\\":\\\"AKIA...\\\",\\\"secret_key\\\":\\\"...\\\",\\\"region\\\":\\\"us-east-1\\\"}\",\n models = listOf(\n AiModel(\"anthropic.claude-sonnet-4-6-20250514-v1:0\", \"Claude Sonnet 4.6\"),\n AiModel(\"anthropic.claude-haiku-4-5-20251001-v1:0\", \"Claude Haiku 4.5\"),\n AiModel(\"us.anthropic.claude-opus-4-6-20250610-v1:0\", \"Claude Opus 4.6 (cross-region)\"),\n AiModel(\"us.meta.llama4-maverick-17b-instruct-v1:0\", \"Llama 4 Maverick\"),\n AiModel(\"us.meta.llama4-scout-17b-instruct-v1:0\", \"Llama 4 Scout\"),\n AiModel(\"amazon.nova-pro-v1:0\", \"Amazon Nova Pro\"),\n AiModel(\"amazon.nova-lite-v1:0\", \"Amazon Nova Lite\"),\n AiModel(\"mistral.mistral-large-2407-v1:0\", \"Mistral Large 2407\"),\n ),\n ),\n LOCAL_GEMMA(\n id = \"local-gemma\",\n displayName = \"On-Device (Gemma 4)\",\n baseUrl = \"\",\n authType = AuthType.API_KEY,\n authHeader = \"\",\n tokenHint = \"No API key needed — runs on device\",\n models = listOf(\n AiModel(\"gemma-4-e2b\", \"Gemma 4 E2B (2.6 GB)\"),\n AiModel(\"gemma-4-e4b\", \"Gemma 4 E4B (3.7 GB)\"),\n ),\n );\n\n /** The effective provider ID for API routing (anthropic-token → anthropic, openai-codex → openai). */\n val effectiveId: String\n get() = when (this) {\n ANTHROPIC_TOKEN -> \"anthropic\"\n OPENAI_CODEX -> \"openai\"\n else -> id\n }\n\n /** Whether this is an on-device local model provider (no API key, no network). */\n val isLocal: Boolean\n get() = this == LOCAL_GEMMA\n\n /** Whether this is Amazon Bedrock (uses SigV4 auth + Converse API). */\n val isBedrock: Boolean\n get() = this == BEDROCK\n\n /** Whether this is an Anthropic-compatible provider. */\n val isAnthropic: Boolean\n get() = this == ANTHROPIC || this == ANTHROPIC_TOKEN\n\n /** Whether this is an OpenAI-compatible provider. */\n val isOpenAiCompatible: Boolean\n get() = this in OPENAI_COMPATIBLE_PROVIDERS\n\n companion object {\n private val OPENAI_COMPATIBLE_PROVIDERS = setOf(\n OPENAI, OPENAI_CODEX, OPENROUTER, MISTRAL,\n TOGETHER, GROQ, XAI, DEEPSEEK, FIREWORKS,\n QWEN, STEPFUN, NVIDIA_NIM, ZAI, CUSTOM,\n )\n\n fun fromId(id: String): AiProvider =\n entries.firstOrNull { it.id == id } ?: ANTHROPIC\n }\n}\n\nprivate val CLAUDE_MODELS = listOf(\n AiModel(\"claude-sonnet-4-6\", \"Claude Sonnet 4.6\"),\n AiModel(\"claude-haiku-4-5\", \"Claude Haiku 4.5\"),\n AiModel(\"claude-opus-4-6\", \"Claude Opus 4.6\"),\n)\n\nprivate val OPENAI_MODELS = listOf(\n AiModel(\"gpt-5.4\", \"GPT-5.4\"),\n AiModel(\"gpt-5.4-mini\", \"GPT-5.4 Mini\"),\n AiModel(\"gpt-5.4-nano\", \"GPT-5.4 Nano\"),\n AiModel(\"gpt-4.1\", \"GPT-4.1\"),\n AiModel(\"gpt-4.1-mini\", \"GPT-4.1 Mini\"),\n AiModel(\"gpt-4o\", \"GPT-4o\"),\n AiModel(\"o3-mini\", \"o3-mini\"),\n)\n\ndata class AiModel(\n val id: String,\n val displayName: String,\n)\n"}, "7c097633cf40ed42f201a0bda2b04288079de6cd": {"t": 1785765424.1096168, "n": 9752, "h": "3a73a67b554f8397", "c": "package ai.affiora.mobileclaw.agent\n\n/**\n * How credentials are sent to the provider.\n *\n * - [API_KEY_RAW]: value injected directly into a custom header (no Bearer prefix).\n * - [API_KEY]: Authorization: Bearer .\n * - [BEARER_TOKEN]: Authorization: Bearer (semantically an OAuth token).\n */\nenum class AuthType {\n API_KEY,\n API_KEY_RAW,\n BEARER_TOKEN,\n}\n\n/**\n * Supported AI providers: 10 cloud + 1 on-device, 3 auth types.\n */\nenum class AiProvider(\n val id: String,\n val displayName: String,\n val baseUrl: String,\n val authType: AuthType,\n val authHeader: String,\n val tokenHint: String,\n val models: List,\n val extraHeaders: Map = emptyMap(),\n) {\n ANTHROPIC(\n id = \"anthropic\",\n displayName = \"Anthropic (API Key)\",\n baseUrl = \"https://api.anthropic.com\",\n authType = AuthType.API_KEY_RAW,\n authHeader = \"x-api-key\",\n tokenHint = \"Paste your Anthropic API key (sk-ant-...)\",\n models = CLAUDE_MODELS,\n extraHeaders = mapOf(\n \"anthropic-version\" to \"2023-06-01\",\n \"anthropic-beta\" to \"fine-grained-tool-streaming-2025-05-14,interleaved-thinking-2025-05-14\",\n ),\n ),\n ANTHROPIC_TOKEN(\n id = \"anthropic-token\",\n displayName = \"Anthropic (Setup Token)\",\n baseUrl = \"https://api.anthropic.com\",\n authType = AuthType.BEARER_TOKEN,\n authHeader = \"Authorization\",\n tokenHint = \"Run `claude setup-token` in terminal and paste here\",\n models = CLAUDE_MODELS,\n extraHeaders = mapOf(\n \"anthropic-version\" to \"2023-06-01\",\n \"anthropic-beta\" to \"claude-code-20250219,oauth-2025-04-20,fine-grained-tool-streaming-2025-05-14,interleaved-thinking-2025-05-14\",\n ),\n ),\n OPENAI(\n id = \"openai\",\n displayName = \"OpenAI\",\n baseUrl = \"https://api.openai.com\",\n authType = AuthType.API_KEY,\n authHeader = \"Authorization\",\n tokenHint = \"Paste your OpenAI API key (sk-...)\",\n models = OPENAI_MODELS,\n ),\n OPENAI_CODEX(\n id = \"openai-codex\",\n displayName = \"OpenAI Codex\",\n baseUrl = \"https://api.openai.com\",\n authType = AuthType.BEARER_TOKEN,\n authHeader = \"Authorization\",\n tokenHint = \"Paste your OpenAI Codex bearer token\",\n models = OPENAI_MODELS,\n ),\n GOOGLE(\n id = \"google\",\n displayName = \"Google Gemini\",\n baseUrl = \"https://generativelanguage.googleapis.com\",\n authType = AuthType.API_KEY_RAW,\n authHeader = \"x-goog-api-key\",\n tokenHint = \"Paste your Google AI API key\",\n models = listOf(\n AiModel(\"gemini-3.1-pro-preview\", \"Gemini 3.1 Pro\"),\n AiModel(\"gemini-3-flash-preview\", \"Gemini 3 Flash\"),\n AiModel(\"gemini-2.5-pro\", \"Gemini 2.5 Pro\"),\n AiModel(\"gemini-2.5-flash\", \"Gemini 2.5 Flash\"),\n AiModel(\"gemini-2.5-flash-lite\", \"Gemini 2.5 Flash-Lite\"),\n AiModel(\"gemma-4-31b-it\", \"Gemma 4 31B (free)\"),\n AiModel(\"gemma-4-26b-a4b-it\", \"Gemma 4 26B (free)\"),\n ),\n ),\n OPENROUTER(\n id = \"openrouter\",\n displayName = \"OpenRouter\",\n baseUrl = \"https://openrouter.ai/api\",\n authType = AuthType.API_KEY,\n authHeader = \"Authorization\",\n tokenHint = \"Paste your OpenRouter API key\",\n models = listOf(\n // Free models\n AiModel(\"google/gemma-4-31b-it:free\", \"Gemma 4 31B (free)\"),\n AiModel(\"google/gemma-4-26b-a4b-it:free\", \"Gemma 4 26B (free)\"),\n AiModel(\"nvidia/nemotron-3-super-120b-a12b:free\", \"Nemotron 3 Super 120B (free)\"),\n AiModel(\"nvidia/nemotron-3-nano-30b-a3b:free\", \"Nemotron 3 Nano 30B (free)\"),\n AiModel(\"minimax/minimax-m2.5:free\", \"MiniMax M2.5 (free)\"),\n AiModel(\"stepfun/step-3.5-flash:free\", \"Step 3.5 Flash (free)\"),\n AiModel(\"openrouter/free\", \"Auto (free models router)\"),\n // Paid models\n AiModel(\"anthropic/claude-sonnet-4-6\", \"Claude Sonnet 4.6\"),\n AiModel(\"anthropic/claude-opus-4-6\", \"Claude Opus 4.6\"),\n AiModel(\"openai/gpt-5.4\", \"GPT-5.4\"),\n AiModel(\"google/gemini-2.5-pro\", \"Gemini 2.5 Pro\"),\n AiModel(\"deepseek/deepseek-v4\", \"DeepSeek V4\"),\n AiModel(\"deepseek/deepseek-r1\", \"DeepSeek R1\"),\n ),\n ),\n MISTRAL(\n id = \"mistral\",\n displayName = \"Mistral\",\n baseUrl = \"https://api.mistral.ai\",\n authType = AuthType.API_KEY,\n authHeader = \"Authorization\",\n tokenHint = \"Paste your Mistral API key\",\n models = listOf(\n AiModel(\"mistral-large-latest\", \"Mistral Large 3\"),\n AiModel(\"magistral-medium-latest\", \"Magistral Medium\"),\n AiModel(\"magistral-small-latest\", \"Magistral Small\"),\n AiModel(\"codestral-latest\", \"Codestral\"),\n AiModel(\"ministral-8b-latest\", \"Ministral 8B\"),\n ),\n ),\n TOGETHER(\n id = \"together\",\n displayName = \"Together AI\",\n baseUrl = \"https://api.together.xyz\",\n authType = AuthType.API_KEY,\n authHeader = \"Authorization\",\n tokenHint = \"Paste your Together AI API key\",\n models = listOf(\n AiModel(\"meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8\", \"Llama 4 Maverick\"),\n AiModel(\"meta-llama/Llama-4-Scout-17B-16E-Instruct\", \"Llama 4 Scout\"),\n AiModel(\"meta-llama/Meta-Llama-3.3-70B-Instruct-Turbo\", \"Llama 3.3 70B Turbo\"),\n AiModel(\"deepseek-ai/DeepSeek-V3\", \"DeepSeek V3\"),\n AiModel(\"Qwen/Qwen3-235B-A22B-fp8\", \"Qwen 3 235B\"),\n ),\n ),\n GROQ(\n id = \"groq\",\n displayName = \"Groq\",\n baseUrl = \"https://api.groq.com/openai\",\n authType = AuthType.API_KEY,\n authHeader = \"Authorization\",\n tokenHint = \"Paste your Groq API key\",\n models = listOf(\n AiModel(\"llama-3.3-70b-versatile\", \"Llama 3.3 70B Versatile\"),\n AiModel(\"llama-3.1-8b-instant\", \"Llama 3.1 8B Instant\"),\n AiModel(\"llama4-scout-17b-16e-instruct\", \"Llama 4 Scout\"),\n AiModel(\"qwen-qwq-32b\", \"Qwen QwQ 32B\"),\n ),\n ),\n XAI(\n id = \"xai\",\n displayName = \"xAI (Grok)\",\n baseUrl = \"https://api.x.ai\",\n authType = AuthType.API_KEY,\n authHeader = \"Authorization\",\n tokenHint = \"Paste your xAI API key\",\n models = listOf(\n AiModel(\"grok-4\", \"Grok 4\"),\n AiModel(\"grok-3-beta\", \"Grok 3\"),\n AiModel(\"grok-3-mini-beta\", \"Grok 3 Mini\"),\n ),\n ),\n DEEPSEEK(\n id = \"deepseek\",\n displayName = \"DeepSeek\",\n baseUrl = \"https://api.deepseek.com\",\n authType = AuthType.API_KEY,\n authHeader = \"Authorization\",\n tokenHint = \"Paste your DeepSeek API key\",\n models = listOf(\n AiModel(\"deepseek-chat\", \"DeepSeek V4\"),\n AiModel(\"deepseek-reasoner\", \"DeepSeek R1\"),\n ),\n ),\n FIREWORKS(\n id = \"fireworks\",\n displayName = \"Fireworks AI\",\n baseUrl = \"https://api.fireworks.ai/inference\",\n authType = AuthType.API_KEY,\n authHeader = \"Authorization\",\n tokenHint = \"Paste your Fireworks AI API key\",\n models = listOf(\n AiModel(\"accounts/fireworks/models/llama4-scout-instruct-basic\", \"Llama 4 Scout\"),\n AiModel(\"accounts/fireworks/models/llama4-maverick-instruct-basic\", \"Llama 4 Maverick\"),\n AiModel(\"accounts/fireworks/models/deepseek-v3\", \"DeepSeek V3\"),\n AiModel(\"accounts/fireworks/models/qwen3-235b-a22b\", \"Qwen 3 235B\"),\n ),\n ),\n LOCAL_GEMMA(\n id = \"local-gemma\",\n displayName = \"On-Device (Gemma 4)\",\n baseUrl = \"\",\n authType = AuthType.API_KEY,\n authHeader = \"\",\n tokenHint = \"No API key needed — runs on device\",\n models = listOf(\n AiModel(\"gemma-4-e2b\", \"Gemma 4 E2B (2.6 GB)\"),\n AiModel(\"gemma-4-e4b\", \"Gemma 4 E4B (3.7 GB)\"),\n ),\n );\n\n /** The effective provider ID for API routing (anthropic-token → anthropic, openai-codex → openai). */\n val effectiveId: String\n get() = when (this) {\n ANTHROPIC_TOKEN -> \"anthropic\"\n OPENAI_CODEX -> \"openai\"\n else -> id\n }\n\n /** Whether this is an on-device local model provider (no API key, no network). */\n val isLocal: Boolean\n get() = this == LOCAL_GEMMA\n\n /** Whether this is an Anthropic-compatible provider. */\n val isAnthropic: Boolean\n get() = this == ANTHROPIC || this == ANTHROPIC_TOKEN\n\n /** Whether this is an OpenAI-compatible provider. */\n val isOpenAiCompatible: Boolean\n get() = this in OPENAI_COMPATIBLE_PROVIDERS\n\n companion object {\n private val OPENAI_COMPATIBLE_PROVIDERS = setOf(\n OPENAI, OPENAI_CODEX, OPENROUTER, MISTRAL,\n TOGETHER, GROQ, XAI, DEEPSEEK, FIREWORKS,\n )\n\n fun fromId(id: String): AiProvider =\n entries.firstOrNull { it.id == id } ?: ANTHROPIC\n }\n}\n\nprivate val CLAUDE_MODELS = listOf(\n AiModel(\"claude-sonnet-4-6\", \"Claude Sonnet 4.6\"),\n AiModel(\"claude-haiku-4-5\", \"Claude Haiku 4.5\"),\n AiModel(\"claude-opus-4-6\", \"Claude Opus 4.6\"),\n)\n\nprivate val OPENAI_MODELS = listOf(\n AiModel(\"gpt-5.4\", \"GPT-5.4\"),\n AiModel(\"gpt-5.4-mini\", \"GPT-5.4 Mini\"),\n AiModel(\"gpt-5.4-nano\", \"GPT-5.4 Nano\"),\n AiModel(\"gpt-4.1\", \"GPT-4.1\"),\n AiModel(\"gpt-4.1-mini\", \"GPT-4.1 Mini\"),\n AiModel(\"gpt-4o\", \"GPT-4o\"),\n AiModel(\"o3-mini\", \"o3-mini\"),\n)\n\ndata class AiModel(\n val id: String,\n val displayName: String,\n)\n"}}}}