diff --git a/.env.example b/.env.example index b7248388458..a11c596cae2 100644 --- a/.env.example +++ b/.env.example @@ -83,6 +83,10 @@ HYPERBOLIC_API_KEY=your_hyperbolic_api_key_here # Get your API key from: https://openrouter.ai/keys OPEN_ROUTER_API_KEY=your_openrouter_api_key_here +# TokenLab (Unified model provider) +# Get your API key from: https://tokenlab.sh/ +TOKENLAB_API_KEY=your_tokenlab_api_key_here + # ====================================== # CUSTOM PROVIDER BASE URLS (Optional) # ====================================== diff --git a/.env.production b/.env.production index 84d2d75bf89..f8abafb1f0a 100644 --- a/.env.production +++ b/.env.production @@ -25,6 +25,11 @@ ANTHROPIC_API_KEY= # You only need this environment variable set if you want to use OpenRouter models OPEN_ROUTER_API_KEY= +# Get your TokenLab API key - +# https://tokenlab.sh/ +# You only need this environment variable set if you want to use TokenLab models +TOKENLAB_API_KEY= + # Get your Google Generative AI API Key by following these instructions - # https://console.cloud.google.com/apis/credentials # You only need this environment variable set if you want to use Google Generative AI models @@ -139,4 +144,4 @@ VITE_SUPABASE_ACCESS_TOKEN= # DEFAULT_NUM_CTX=24576 # Consumes 32GB of VRAM # DEFAULT_NUM_CTX=12288 # Consumes 26GB of VRAM # DEFAULT_NUM_CTX=6144 # Consumes 24GB of VRAM -DEFAULT_NUM_CTX= \ No newline at end of file +DEFAULT_NUM_CTX= diff --git a/README.md b/README.md index 72da566defc..ea10b54a879 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![bolt.diy: AI-Powered Full-Stack Web Development in the Browser](./public/social_preview_index.jpg)](https://bolt.diy) -Welcome to bolt.diy, the official open source version of Bolt.new, which allows you to choose the LLM that you use for each prompt! Currently, you can use OpenAI, Anthropic, Ollama, OpenRouter, Gemini, LMStudio, Mistral, xAI, HuggingFace, DeepSeek, Groq, Cohere, Together, Perplexity, Moonshot (Kimi), Hyperbolic, GitHub Models, Amazon Bedrock, and OpenAI-like providers - and it is easily extended to use any other model supported by the Vercel AI SDK! See the instructions below for running this locally and extending it to include more models. +Welcome to bolt.diy, the official open source version of Bolt.new, which allows you to choose the LLM that you use for each prompt! Currently, you can use OpenAI, Anthropic, Ollama, OpenRouter, TokenLab, Gemini, LMStudio, Mistral, xAI, HuggingFace, DeepSeek, Groq, Cohere, Together, Perplexity, Moonshot (Kimi), Hyperbolic, GitHub Models, Amazon Bedrock, and OpenAI-like providers - and it is easily extended to use any other model supported by the Vercel AI SDK! See the instructions below for running this locally and extending it to include more models. ----- Check the [bolt.diy Docs](https://stackblitz-labs.github.io/bolt.diy/) for more official installation instructions and additional information. @@ -44,7 +44,7 @@ project, please check the [project management guide](./PROJECT.md) to get starte ## Recent Major Additions ### ✅ Completed Features -- **19+ AI Provider Integrations** - OpenAI, Anthropic, Google, Groq, xAI, DeepSeek, Mistral, Cohere, Together, Perplexity, HuggingFace, Ollama, LM Studio, OpenRouter, Moonshot, Hyperbolic, GitHub Models, Amazon Bedrock, OpenAI-like +- **20+ AI Provider Integrations** - OpenAI, Anthropic, Google, Groq, xAI, DeepSeek, Mistral, Cohere, Together, Perplexity, HuggingFace, Ollama, LM Studio, OpenRouter, TokenLab, Moonshot, Hyperbolic, GitHub Models, Amazon Bedrock, OpenAI-like - **Electron Desktop App** - Native desktop experience with full functionality - **Advanced Deployment Options** - Netlify, Vercel, and GitHub Pages deployment - **Supabase Integration** - Database management and query capabilities @@ -342,6 +342,7 @@ LMSTUDIO_BASE_URL=http://127.0.0.1:1234 - **Perplexity** - Sonar models for search and reasoning - **HuggingFace** - Access to HuggingFace model hub - **OpenRouter** - Unified API for multiple model providers +- **TokenLab** - Unified API with OpenAI-compatible chat completions plus native Responses, Anthropic Messages, and Gemini endpoints - **Moonshot (Kimi)** - Kimi AI models - **Hyperbolic** - High-performance model inference - **GitHub Models** - Models available through GitHub diff --git a/app/components/@settings/tabs/providers/cloud/CloudProvidersTab.tsx b/app/components/@settings/tabs/providers/cloud/CloudProvidersTab.tsx index 73118514264..3e761c3042f 100644 --- a/app/components/@settings/tabs/providers/cloud/CloudProvidersTab.tsx +++ b/app/components/@settings/tabs/providers/cloud/CloudProvidersTab.tsx @@ -30,6 +30,7 @@ type ProviderName = | 'OpenAI' | 'OpenRouter' | 'Perplexity' + | 'TokenLab' | 'Together' | 'XAI'; @@ -48,6 +49,7 @@ const PROVIDER_ICONS: Record = { OpenAI: SiOpenai, OpenRouter: FaCloud, Perplexity: SiPerplexity, + TokenLab: BsCloud, Together: BsCloud, XAI: BsRobot, }; @@ -57,6 +59,7 @@ const PROVIDER_DESCRIPTIONS: Partial> = { Anthropic: 'Access Claude and other Anthropic models', Github: 'Use OpenAI models hosted through GitHub infrastructure', OpenAI: 'Use GPT-4, GPT-3.5, and other OpenAI models', + TokenLab: 'Use TokenLab unified model access with OpenAI-compatible chat completions', }; const CloudProvidersTab = () => { diff --git a/app/lib/.server/llm/constants.ts b/app/lib/.server/llm/constants.ts index a78b33b0ce3..134607eb5a0 100644 --- a/app/lib/.server/llm/constants.ts +++ b/app/lib/.server/llm/constants.ts @@ -23,6 +23,7 @@ export const PROVIDER_COMPLETION_LIMITS: Record = { OpenRouter: 8192, Perplexity: 8192, Together: 8192, + TokenLab: 8192, xAI: 8192, LMStudio: 8192, OpenAILike: 8192, diff --git a/app/lib/modules/llm/providers/tokenlab.ts b/app/lib/modules/llm/providers/tokenlab.ts new file mode 100644 index 00000000000..6e9338597f8 --- /dev/null +++ b/app/lib/modules/llm/providers/tokenlab.ts @@ -0,0 +1,266 @@ +import { createOpenAI } from '@ai-sdk/openai'; +import { BaseProvider } from '~/lib/modules/llm/base-provider'; +import type { ModelInfo } from '~/lib/modules/llm/types'; +import type { IProviderSetting } from '~/types/model'; +import type { LanguageModelV1 } from 'ai'; + +const TOKENLAB_API_BASE_URL = 'https://api.tokenlab.sh/v1'; + +interface TokenLabModel { + id?: string; + owned_by?: string; + tokenlab?: TokenLabMetadata; + lemondata?: TokenLabMetadata; +} + +interface TokenLabMetadata { + category?: string; + max_input_tokens?: number; + max_output_tokens?: number; + pricing?: { + input_per_1m?: string | number | null; + output_per_1m?: string | number | null; + }; +} + +interface TokenLabModelsResponse { + data?: TokenLabModel[]; +} + +const toNumber = (value: string | number | null | undefined): number | undefined => { + if (value === undefined || value === null || value === '') { + return undefined; + } + + const parsed = Number(value); + + return Number.isFinite(parsed) ? parsed : undefined; +}; + +const formatContextSize = (tokens: number): string => { + if (tokens >= 1_000_000) { + return `${Math.floor(tokens / 1_000_000)}M`; + } + + if (tokens >= 1_000) { + return `${Math.floor(tokens / 1_000)}k`; + } + + return String(tokens); +}; + +const formatModelLabel = (id: string, owner: string | undefined, metadata: TokenLabMetadata): string => { + const inputPrice = toNumber(metadata.pricing?.input_per_1m); + const outputPrice = toNumber(metadata.pricing?.output_per_1m); + const contextWindow = metadata.max_input_tokens ?? 200_000; + const price = + inputPrice !== undefined && outputPrice !== undefined + ? ` - in:$${inputPrice.toFixed(2)} out:$${outputPrice.toFixed(2)}` + : ''; + const ownerSuffix = owner ? ` (${owner})` : ''; + + return `${id}${ownerSuffix}${price} - context ${formatContextSize(contextWindow)}`; +}; + +export default class TokenLabProvider extends BaseProvider { + name = 'TokenLab'; + getApiKeyLink = 'https://tokenlab.sh/'; + + config = { + apiTokenKey: 'TOKENLAB_API_KEY', + }; + + staticModels: ModelInfo[] = [ + { + name: 'claude-fable-5', + label: 'Claude Fable 5', + provider: 'TokenLab', + maxTokenAllowed: 1_000_000, + maxCompletionTokens: 128_000, + }, + { + name: 'claude-opus-4-8', + label: 'Claude Opus 4.8', + provider: 'TokenLab', + maxTokenAllowed: 1_000_000, + maxCompletionTokens: 128_000, + }, + { + name: 'claude-sonnet-5', + label: 'Claude Sonnet 5', + provider: 'TokenLab', + maxTokenAllowed: 1_000_000, + maxCompletionTokens: 128_000, + }, + { + name: 'glm-5.2', + label: 'GLM 5.2', + provider: 'TokenLab', + maxTokenAllowed: 1_000_000, + maxCompletionTokens: 128_000, + }, + { + name: 'deepseek-v4-pro', + label: 'DeepSeek V4 Pro', + provider: 'TokenLab', + maxTokenAllowed: 1_000_000, + maxCompletionTokens: 384_000, + }, + { + name: 'deepseek-v4-flash', + label: 'DeepSeek V4 Flash', + provider: 'TokenLab', + maxTokenAllowed: 1_000_000, + maxCompletionTokens: 384_000, + }, + { + name: 'gpt-5.5', + label: 'GPT-5.5', + provider: 'TokenLab', + maxTokenAllowed: 1_000_000, + maxCompletionTokens: 128_000, + }, + { name: 'gpt-5.4', label: 'GPT-5.4', provider: 'TokenLab', maxTokenAllowed: 400_000, maxCompletionTokens: 128_000 }, + { + name: 'gpt-5.4-mini', + label: 'GPT-5.4 Mini', + provider: 'TokenLab', + maxTokenAllowed: 400_000, + maxCompletionTokens: 128_000, + }, + { + name: 'minimax-m3', + label: 'MiniMax M3', + provider: 'TokenLab', + maxTokenAllowed: 1_048_576, + maxCompletionTokens: 524_288, + }, + { + name: 'kimi-k2.7-code', + label: 'Kimi K2.7 Code', + provider: 'TokenLab', + maxTokenAllowed: 262_144, + maxCompletionTokens: 131_072, + }, + { + name: 'qwen3.7-max', + label: 'Qwen3.7 Max', + provider: 'TokenLab', + maxTokenAllowed: 991_808, + maxCompletionTokens: 65_536, + }, + { + name: 'gemini-3.5-flash', + label: 'Gemini 3.5 Flash', + provider: 'TokenLab', + maxTokenAllowed: 1_048_576, + maxCompletionTokens: 65_536, + }, + { + name: 'gemini-3.1-flash-lite', + label: 'Gemini 3.1 Flash Lite', + provider: 'TokenLab', + maxTokenAllowed: 1_048_576, + maxCompletionTokens: 65_536, + }, + { + name: 'grok-4.3', + label: 'Grok 4.3', + provider: 'TokenLab', + maxTokenAllowed: 1_000_000, + maxCompletionTokens: 131_072, + }, + { + name: 'grok-4-fast', + label: 'Grok 4 Fast', + provider: 'TokenLab', + maxTokenAllowed: 2_000_000, + maxCompletionTokens: 16_384, + }, + ]; + + async getDynamicModels( + apiKeys?: Record, + settings?: IProviderSetting, + serverEnv: Record = {}, + ): Promise { + const { apiKey } = this.getProviderBaseUrlAndKey({ + apiKeys, + providerSettings: settings, + serverEnv, + defaultBaseUrlKey: '', + defaultApiTokenKey: 'TOKENLAB_API_KEY', + }); + + try { + const headers: Record = { + 'Content-Type': 'application/json', + }; + + if (apiKey) { + headers.Authorization = `Bearer ${apiKey}`; + } + + const response = await fetch(`${TOKENLAB_API_BASE_URL}/models`, { + headers, + signal: this.createTimeoutSignal(), + }); + + if (!response.ok) { + throw new Error(`HTTP ${response.status}: ${response.statusText}`); + } + + const data = (await response.json()) as TokenLabModelsResponse; + + return (data.data ?? []) + .filter((model) => { + const metadata = model.tokenlab ?? model.lemondata; + return Boolean(model.id && metadata?.category === 'chat'); + }) + .map((model) => { + const metadata = (model.tokenlab ?? model.lemondata)!; + const name = model.id!; + + return { + name, + label: formatModelLabel(name, model.owned_by, metadata), + provider: this.name, + maxTokenAllowed: metadata.max_input_tokens ?? 200_000, + maxCompletionTokens: metadata.max_output_tokens ?? 8_192, + }; + }) + .sort((a, b) => a.label.localeCompare(b.label)); + } catch (error) { + console.error('Error getting TokenLab models:', error); + return []; + } + } + + getModelInstance(options: { + model: string; + serverEnv: Env; + apiKeys?: Record; + providerSettings?: Record; + }): LanguageModelV1 { + const { model, serverEnv, apiKeys, providerSettings } = options; + + const { apiKey } = this.getProviderBaseUrlAndKey({ + apiKeys, + providerSettings: providerSettings?.[this.name], + serverEnv: serverEnv as any, + defaultBaseUrlKey: '', + defaultApiTokenKey: 'TOKENLAB_API_KEY', + }); + + if (!apiKey) { + throw new Error(`Missing API key for ${this.name} provider`); + } + + const openai = createOpenAI({ + baseURL: TOKENLAB_API_BASE_URL, + apiKey, + }); + + return openai(model); + } +} diff --git a/app/lib/modules/llm/registry.ts b/app/lib/modules/llm/registry.ts index 01bbe811404..8921a34fe2f 100644 --- a/app/lib/modules/llm/registry.ts +++ b/app/lib/modules/llm/registry.ts @@ -14,6 +14,7 @@ import OpenAILikeProvider from './providers/openai-like'; import OpenAIProvider from './providers/openai'; import PerplexityProvider from './providers/perplexity'; import TogetherProvider from './providers/together'; +import TokenLabProvider from './providers/tokenlab'; import XAIProvider from './providers/xai'; import HyperbolicProvider from './providers/hyperbolic'; import AmazonBedrockProvider from './providers/amazon-bedrock'; @@ -38,6 +39,7 @@ export { OpenRouterProvider, OpenAILikeProvider, PerplexityProvider, + TokenLabProvider, XAIProvider, TogetherProvider, LMStudioProvider, diff --git a/app/lib/services/importExportService.ts b/app/lib/services/importExportService.ts index cdebe4e70cf..5aeb5b16000 100644 --- a/app/lib/services/importExportService.ts +++ b/app/lib/services/importExportService.ts @@ -264,6 +264,7 @@ export class ImportExportService { Mistral: '', OpenAILike: '', Together: '', + TokenLab: '', xAI: '', Perplexity: '', Cohere: '', diff --git a/docs/docs/index.md b/docs/docs/index.md index e5320223635..aea5c30ba9d 100644 --- a/docs/docs/index.md +++ b/docs/docs/index.md @@ -1,6 +1,6 @@ # Welcome to bolt diy -bolt.diy allows you to choose the LLM that you use for each prompt! Currently, you can use models from 19 providers including OpenAI, Anthropic, Ollama, OpenRouter, Google/Gemini, LMStudio, Mistral, xAI, HuggingFace, DeepSeek, Groq, Cohere, Together AI, Perplexity AI, Hyperbolic, Moonshot AI (Kimi), Amazon Bedrock, GitHub Models, and more - with easy extensibility to add any other model supported by the Vercel AI SDK! See the instructions below for running this locally and extending it to include more models. +bolt.diy allows you to choose the LLM that you use for each prompt! Currently, you can use models from 20 providers including OpenAI, Anthropic, Ollama, OpenRouter, TokenLab, Google/Gemini, LMStudio, Mistral, xAI, HuggingFace, DeepSeek, Groq, Cohere, Together AI, Perplexity AI, Hyperbolic, Moonshot AI (Kimi), Amazon Bedrock, GitHub Models, and more - with easy extensibility to add any other model supported by the Vercel AI SDK! See the instructions below for running this locally and extending it to include more models. ## Table of Contents @@ -40,7 +40,7 @@ Also [this pinned post in our community](https://thinktank.ottomator.ai/t/videos ## Features - **AI-powered full-stack web development** directly in your browser with live preview -- **Support for 19 LLM providers** with an extensible architecture to integrate additional models +- **Support for 20 LLM providers** with an extensible architecture to integrate additional models - **Attach images and files to prompts** for better contextual understanding - **Integrated terminal** with WebContainer sandbox for running commands and testing - **Version control with Git** - import/export projects, connect to GitHub repositories