From 2064c83177636236b5163badbee9aa71cb24c755 Mon Sep 17 00:00:00 2001 From: kris1803 Date: Sat, 14 Dec 2024 00:39:27 +0100 Subject: [PATCH 01/24] Fixed console error for SettingsWIndow & Removed ts-nocheck where not needed --- app/commit.json | 2 +- app/components/settings/SettingsWindow.tsx | 2 +- app/lib/.server/llm/api-key.ts | 4 ---- pre-start.cjs | 1 + 4 files changed, 3 insertions(+), 6 deletions(-) diff --git a/app/commit.json b/app/commit.json index beebd2b..ff64112 100644 --- a/app/commit.json +++ b/app/commit.json @@ -1 +1 @@ -{ "commit": "55094392cf4c5bc607aff796680ad50236a4cf20" } +{ "commit": "9666b2ab67d25345542722ab9d870b36ad06252e" } diff --git a/app/components/settings/SettingsWindow.tsx b/app/components/settings/SettingsWindow.tsx index aae0a86..9fb615e 100644 --- a/app/components/settings/SettingsWindow.tsx +++ b/app/components/settings/SettingsWindow.tsx @@ -63,7 +63,7 @@ export const SettingsWindow = ({ open, onClose }: SettingsProps) => { variants={dialogBackdropVariants} /> - + ) { diff --git a/pre-start.cjs b/pre-start.cjs index e6b7001..841e3eb 100644 --- a/pre-start.cjs +++ b/pre-start.cjs @@ -7,4 +7,5 @@ console.log(` ★═══════════════════════════════════════★ `); console.log('📍 Current Commit Version:', commit); +console.log(' Please wait until the URL appears here') console.log('★═══════════════════════════════════════★'); \ No newline at end of file From 69c0bf5873334c25d691b8db4a995b86125a6799 Mon Sep 17 00:00:00 2001 From: Anirban Kar Date: Tue, 17 Dec 2024 14:57:16 +0530 Subject: [PATCH 02/24] fix: added wait till terminal prompt for bolt shell execution --- app/commit.json | 2 +- app/utils/shell.ts | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/app/commit.json b/app/commit.json index 832678f..2d7a657 100644 --- a/app/commit.json +++ b/app/commit.json @@ -1 +1 @@ -{ "commit": "1e72d52278730f7d22448be9d5cf2daf12559486", "version": "0.0.2" } +{ "commit": "0ee373629789f01fb9f54f6747735b51a94a5562" } diff --git a/app/utils/shell.ts b/app/utils/shell.ts index 53b450f..3aad5b3 100644 --- a/app/utils/shell.ts +++ b/app/utils/shell.ts @@ -105,6 +105,7 @@ export class BoltShell { * this.#shellInputStream?.write('\x03'); */ this.terminal.input('\x03'); + await this.waitTillOscCode('prompt'); if (state && state.executionPrms) { await state.executionPrms; @@ -145,6 +146,10 @@ export class BoltShell { terminalOutput.pipeTo( new WritableStream({ write(data) { + /* + * const [, osc] = data.match(/\x1b\]654;([^\x07]+)\x07/) || []; + * console.log('terminal onData', { data,osc }); + */ if (!isInteractive) { const [, osc] = data.match(/\x1b\]654;([^\x07]+)\x07/) || []; From e9e785767528e1ca78b9b09c8ba0b73c37ef368b Mon Sep 17 00:00:00 2001 From: Anirban Kar Date: Tue, 17 Dec 2024 15:01:00 +0530 Subject: [PATCH 03/24] removed logs --- app/commit.json | 2 +- app/utils/shell.ts | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/app/commit.json b/app/commit.json index 2d7a657..85591f4 100644 --- a/app/commit.json +++ b/app/commit.json @@ -1 +1 @@ -{ "commit": "0ee373629789f01fb9f54f6747735b51a94a5562" } +{ "commit": "69c0bf5873334c25d691b8db4a995b86125a6799" } diff --git a/app/utils/shell.ts b/app/utils/shell.ts index 3aad5b3..0ffea42 100644 --- a/app/utils/shell.ts +++ b/app/utils/shell.ts @@ -146,10 +146,6 @@ export class BoltShell { terminalOutput.pipeTo( new WritableStream({ write(data) { - /* - * const [, osc] = data.match(/\x1b\]654;([^\x07]+)\x07/) || []; - * console.log('terminal onData', { data,osc }); - */ if (!isInteractive) { const [, osc] = data.match(/\x1b\]654;([^\x07]+)\x07/) || []; From 206a6d781624bbb79cca765ade38cf4455ce7b4d Mon Sep 17 00:00:00 2001 From: Dustin Loring Date: Tue, 17 Dec 2024 10:19:40 -0500 Subject: [PATCH 04/24] add/toc-for-readme added a TOC for the README file, renamed some headings to better suite the TOC --- README.md | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 84235f8..02ae4eb 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,5 @@ -[![bolt.diy: AI-Powered Full-Stack Web Development in the Browser](./public/social_preview_index.jpg)](https://bolt.diy) - # bolt.diy (Previously oTToDev) +[![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 (previously known as oTToDev and bolt.new ANY LLM), 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, or Groq models - 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. @@ -8,12 +7,24 @@ Check the [bolt.diy Docs](https://stackblitz-labs.github.io/bolt.diy/) for more bolt.diy was originally started by [Cole Medin](https://www.youtube.com/@ColeMedin) but has quickly grown into a massive community effort to build the BEST open source AI coding assistant! -## Join the community for bolt.diy! +## Table of Contents -https://thinktank.ottomator.ai +- [Join the Community](#join-the-community) +- [Requested Additions](#requested-additions) +- [Features](#features) +- [Setup](#setup) +- [Run the Application](#run-the-application) +- [Available Scripts](#available-scripts) +- [Contributing](#contributing) +- [Roadmap](#roadmap) +- [FAQ](#faq) + +## Join the community + +[Join our bolt.diy community here!](https://thinktank.ottomator.ai) -## Requested Additions - Feel Free to Contribute! +## Requested Additions - ✅ OpenRouter Integration (@coleam00) - ✅ Gemini Integration (@jonathands) @@ -60,7 +71,7 @@ https://thinktank.ottomator.ai - ⬜ Perplexity Integration - ⬜ Vertex AI Integration -## bolt.diy Features +## Features - **AI-powered full-stack web development** directly in your browser. - **Support for multiple LLMs** with an extensible architecture to integrate additional models. @@ -70,7 +81,7 @@ https://thinktank.ottomator.ai - **Download projects as ZIP** for easy portability. - **Integration-ready Docker support** for a hassle-free setup. -## Setup bolt.diy +## Setup If you're new to installing software from GitHub, don't worry! If you encounter any issues, feel free to submit an "issue" using the provided links or improve this documentation by forking the repository, editing the instructions, and submitting a pull request. The following instruction will help you get the stable branch up and running on your local machine in no time. From 0e7937b835d84811360b8f6fd9941c0c6021634c Mon Sep 17 00:00:00 2001 From: Dustin Loring Date: Tue, 17 Dec 2024 10:27:04 -0500 Subject: [PATCH 05/24] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 02ae4eb..9925deb 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ bolt.diy was originally started by [Cole Medin](https://www.youtube.com/@ColeMed ## Join the community -[Join our bolt.diy community here!](https://thinktank.ottomator.ai) +[Join the bolt.diy community here, in the thinktank on ottomator.ai!](https://thinktank.ottomator.ai) ## Requested Additions From 8d0f0da04a8b5648e5dc83ce53c78edb4b9f1dee Mon Sep 17 00:00:00 2001 From: GK Date: Tue, 17 Dec 2024 17:30:22 +0000 Subject: [PATCH 06/24] add: xAI grok-2-1212 model --- app/utils/constants.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/utils/constants.ts b/app/utils/constants.ts index 6425995..78db6a0 100644 --- a/app/utils/constants.ts +++ b/app/utils/constants.ts @@ -233,7 +233,10 @@ const PROVIDER_LIST: ProviderInfo[] = [ }, { name: 'xAI', - staticModels: [{ name: 'grok-beta', label: 'xAI Grok Beta', provider: 'xAI', maxTokenAllowed: 8000 }], + staticModels: [ + { name: 'grok-beta', label: 'xAI Grok Beta', provider: 'xAI', maxTokenAllowed: 8000 }, + { name: 'grok-2-1212', label: 'xAI Grok2 1212', provider: 'xAI', maxTokenAllowed: 8000 }, + ], getApiKeyLink: 'https://docs.x.ai/docs/quickstart#creating-an-api-key', }, { From ba4e78809c0e45ae132b5d3e7cd5e3d58bc90d7e Mon Sep 17 00:00:00 2001 From: Dustin Loring Date: Tue, 17 Dec 2024 17:40:16 -0500 Subject: [PATCH 07/24] feat: improved providers list style made the list 2 columns wide and separate out the experimental providers --- .../settings/providers/ProvidersTab.tsx | 147 +++++++++++------- 1 file changed, 93 insertions(+), 54 deletions(-) diff --git a/app/components/settings/providers/ProvidersTab.tsx b/app/components/settings/providers/ProvidersTab.tsx index 281b4c8..464a54d 100644 --- a/app/components/settings/providers/ProvidersTab.tsx +++ b/app/components/settings/providers/ProvidersTab.tsx @@ -6,13 +6,17 @@ import type { IProviderConfig } from '~/types/model'; import { logStore } from '~/lib/stores/logs'; // Import a default fallback icon -import DefaultIcon from '/icons/Default.svg'; // Adjust the path as necessary +import DefaultIcon from '/icons/Default.svg'; + +// List of advanced providers with correct casing +const ADVANCED_PROVIDERS = ['Ollama', 'OpenAILike', 'LMStudio']; export default function ProvidersTab() { const { providers, updateProviderSettings, isLocalModel } = useSettings(); const [filteredProviders, setFilteredProviders] = useState([]); + const [advancedProviders, setAdvancedProviders] = useState([]); + const [regularProviders, setRegularProviders] = useState([]); - // Load base URLs from cookies const [searchTerm, setSearchTerm] = useState(''); useEffect(() => { @@ -31,11 +35,77 @@ export default function ProvidersTab() { newFilteredProviders = newFilteredProviders.filter((provider) => !LOCAL_PROVIDERS.includes(provider.name)); } - newFilteredProviders.sort((a, b) => a.name.localeCompare(b.name)); + // Split providers into regular and advanced + const regular = newFilteredProviders.filter( + (provider) => !ADVANCED_PROVIDERS.includes(provider.name) + ); + const advanced = newFilteredProviders.filter( + (provider) => ADVANCED_PROVIDERS.includes(provider.name) + ); + // Sort advanced providers in specific order - OpenAILike at the end + const advancedOrder = ['Ollama', 'LMStudio', 'OpenAILike']; + advanced.sort((a, b) => { + return advancedOrder.indexOf(a.name) - advancedOrder.indexOf(b.name); + }); + + // Sort regular providers alphabetically + regular.sort((a, b) => a.name.localeCompare(b.name)); + + setRegularProviders(regular); + setAdvancedProviders(advanced); setFilteredProviders(newFilteredProviders); }, [providers, searchTerm, isLocalModel]); + const ProviderCard = ({ provider }: { provider: IProviderConfig }) => ( +
+
+
+ { + e.currentTarget.src = DefaultIcon; + }} + alt={`${provider.name} icon`} + className="w-6 h-6 dark:invert" + /> + {provider.name} +
+ { + updateProviderSettings(provider.name, { ...provider.settings, enabled }); + if (enabled) { + logStore.logProvider(`Provider ${provider.name} enabled`, { provider: provider.name }); + } else { + logStore.logProvider(`Provider ${provider.name} disabled`, { provider: provider.name }); + } + }} + /> +
+ {URL_CONFIGURABLE_PROVIDERS.includes(provider.name) && provider.settings.enabled && ( +
+ + { + const newBaseUrl = e.target.value; + updateProviderSettings(provider.name, { ...provider.settings, baseUrl: newBaseUrl }); + logStore.logProvider(`Base URL updated for ${provider.name}`, { + provider: provider.name, + baseUrl: newBaseUrl, + }); + }} + placeholder={`Enter ${provider.name} base URL`} + className="w-full bg-white dark:bg-bolt-elements-background-depth-4 relative px-2 py-1.5 rounded-md focus:outline-none placeholder-bolt-elements-textTertiary text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary border border-bolt-elements-borderColor" + /> +
+ )} +
+ ); + return (
@@ -47,60 +117,29 @@ export default function ProvidersTab() { className="w-full bg-white dark:bg-bolt-elements-background-depth-4 relative px-2 py-1.5 rounded-md focus:outline-none placeholder-bolt-elements-textTertiary text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary border border-bolt-elements-borderColor" />
- {filteredProviders.map((provider) => ( -
-
-
- { - // Fallback to default icon on error - e.currentTarget.src = DefaultIcon; - }} - alt={`${provider.name} icon`} - className="w-6 h-6 dark:invert" - /> - {provider.name} -
- { - updateProviderSettings(provider.name, { ...provider.settings, enabled }); - if (enabled) { - logStore.logProvider(`Provider ${provider.name} enabled`, { provider: provider.name }); - } else { - logStore.logProvider(`Provider ${provider.name} disabled`, { provider: provider.name }); - } - }} - /> + {/* Regular Providers Grid */} +
+ {regularProviders.map((provider) => ( + + ))} +
+ + {/* Advanced Providers Section */} + {advancedProviders.length > 0 && ( +
+

Experimental Providers

+

+ These providers are experimental features that allow you to run AI models locally or connect to your own infrastructure. + They require additional setup but offer more flexibility for advanced users. +

+
+ {advancedProviders.map((provider) => ( + + ))}
- {/* Base URL input for configurable providers */} - {URL_CONFIGURABLE_PROVIDERS.includes(provider.name) && provider.settings.enabled && ( -
- - { - const newBaseUrl = e.target.value; - updateProviderSettings(provider.name, { ...provider.settings, baseUrl: newBaseUrl }); - logStore.logProvider(`Base URL updated for ${provider.name}`, { - provider: provider.name, - baseUrl: newBaseUrl, - }); - }} - placeholder={`Enter ${provider.name} base URL`} - className="w-full bg-white dark:bg-bolt-elements-background-depth-4 relative px-2 py-1.5 rounded-md focus:outline-none placeholder-bolt-elements-textTertiary text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary border border-bolt-elements-borderColor" - /> -
- )}
- ))} + )}
); } From 62ebfe51a69788229aa62d34afb4ce89b7cd8ac8 Mon Sep 17 00:00:00 2001 From: Anirban Kar Date: Wed, 18 Dec 2024 16:34:18 +0530 Subject: [PATCH 08/24] fix: .env file baseUrl Issue --- app/commit.json | 2 +- app/components/chat/BaseChat.tsx | 6 +- .../settings/providers/ProvidersTab.tsx | 7 +- app/entry.server.tsx | 2 +- app/lib/.server/llm/api-key.ts | 25 +++- app/lib/.server/llm/model.ts | 11 +- app/lib/.server/llm/stream-text.ts | 9 +- app/types/model.ts | 6 +- app/utils/constants.ts | 124 ++++++++++++++---- 9 files changed, 149 insertions(+), 43 deletions(-) diff --git a/app/commit.json b/app/commit.json index b9c669a..4ff5294 100644 --- a/app/commit.json +++ b/app/commit.json @@ -1 +1 @@ -{ "commit": "eb6d4353565be31c6e20bfca2c5aea29e4f45b6d", "version": "0.0.3" } +{ "commit": "fce8999f27c0affbc762dc90de992b5a759ab325" } diff --git a/app/components/chat/BaseChat.tsx b/app/components/chat/BaseChat.tsx index 2084cbb..5db6653 100644 --- a/app/components/chat/BaseChat.tsx +++ b/app/components/chat/BaseChat.tsx @@ -119,6 +119,9 @@ export const BaseChat = React.forwardRef( useEffect(() => { // Load API keys from cookies on component mount + + let parsedApiKeys: Record | undefined = {}; + try { const storedApiKeys = Cookies.get('apiKeys'); @@ -127,6 +130,7 @@ export const BaseChat = React.forwardRef( if (typeof parsedKeys === 'object' && parsedKeys !== null) { setApiKeys(parsedKeys); + parsedApiKeys = parsedKeys; } } } catch (error) { @@ -155,7 +159,7 @@ export const BaseChat = React.forwardRef( Cookies.remove('providers'); } - initializeModelList(providerSettings).then((modelList) => { + initializeModelList({ apiKeys: parsedApiKeys, providerSettings }).then((modelList) => { setModelList(modelList); }); diff --git a/app/components/settings/providers/ProvidersTab.tsx b/app/components/settings/providers/ProvidersTab.tsx index 281b4c8..49a16f6 100644 --- a/app/components/settings/providers/ProvidersTab.tsx +++ b/app/components/settings/providers/ProvidersTab.tsx @@ -87,7 +87,12 @@ export default function ProvidersTab() { type="text" value={provider.settings.baseUrl || ''} onChange={(e) => { - const newBaseUrl = e.target.value; + let newBaseUrl: string | undefined = e.target.value; + + if (newBaseUrl && newBaseUrl.trim().length === 0) { + newBaseUrl = undefined; + } + updateProviderSettings(provider.name, { ...provider.settings, baseUrl: newBaseUrl }); logStore.logProvider(`Base URL updated for ${provider.name}`, { provider: provider.name, diff --git a/app/entry.server.tsx b/app/entry.server.tsx index a44917f..5e92d21 100644 --- a/app/entry.server.tsx +++ b/app/entry.server.tsx @@ -14,7 +14,7 @@ export default async function handleRequest( remixContext: EntryContext, _loadContext: AppLoadContext, ) { - await initializeModelList(); + await initializeModelList({}); const readable = await renderToReadableStream(, { signal: request.signal, diff --git a/app/lib/.server/llm/api-key.ts b/app/lib/.server/llm/api-key.ts index e82d08e..d21f070 100644 --- a/app/lib/.server/llm/api-key.ts +++ b/app/lib/.server/llm/api-key.ts @@ -3,6 +3,7 @@ * Preventing TS checks with files presented in the video for a better presentation. */ import { env } from 'node:process'; +import type { IProviderSetting } from '~/types/model'; export function getAPIKey(cloudflareEnv: Env, provider: string, userApiKeys?: Record) { /** @@ -50,16 +51,30 @@ export function getAPIKey(cloudflareEnv: Env, provider: string, userApiKeys?: Re } } -export function getBaseURL(cloudflareEnv: Env, provider: string) { +export function getBaseURL(cloudflareEnv: Env, provider: string, providerSettings?: Record) { + let settingBaseUrl = providerSettings?.[provider].baseUrl; + + if (settingBaseUrl && settingBaseUrl.length == 0) { + settingBaseUrl = undefined; + } + switch (provider) { case 'Together': - return env.TOGETHER_API_BASE_URL || cloudflareEnv.TOGETHER_API_BASE_URL || 'https://api.together.xyz/v1'; + return ( + settingBaseUrl || + env.TOGETHER_API_BASE_URL || + cloudflareEnv.TOGETHER_API_BASE_URL || + 'https://api.together.xyz/v1' + ); case 'OpenAILike': - return env.OPENAI_LIKE_API_BASE_URL || cloudflareEnv.OPENAI_LIKE_API_BASE_URL; + return settingBaseUrl || env.OPENAI_LIKE_API_BASE_URL || cloudflareEnv.OPENAI_LIKE_API_BASE_URL; case 'LMStudio': - return env.LMSTUDIO_API_BASE_URL || cloudflareEnv.LMSTUDIO_API_BASE_URL || 'http://localhost:1234'; + return ( + settingBaseUrl || env.LMSTUDIO_API_BASE_URL || cloudflareEnv.LMSTUDIO_API_BASE_URL || 'http://localhost:1234' + ); case 'Ollama': { - let baseUrl = env.OLLAMA_API_BASE_URL || cloudflareEnv.OLLAMA_API_BASE_URL || 'http://localhost:11434'; + let baseUrl = + settingBaseUrl || env.OLLAMA_API_BASE_URL || cloudflareEnv.OLLAMA_API_BASE_URL || 'http://localhost:11434'; if (env.RUNNING_IN_DOCKER === 'true') { baseUrl = baseUrl.replace('localhost', 'host.docker.internal'); diff --git a/app/lib/.server/llm/model.ts b/app/lib/.server/llm/model.ts index 1a5aab7..1feb499 100644 --- a/app/lib/.server/llm/model.ts +++ b/app/lib/.server/llm/model.ts @@ -84,6 +84,8 @@ export function getHuggingFaceModel(apiKey: OptionalApiKey, model: string) { } export function getOllamaModel(baseURL: string, model: string) { + console.log({ baseURL, model }); + const ollamaInstance = ollama(model, { numCtx: DEFAULT_NUM_CTX, }) as LanguageModelV1 & { config: any }; @@ -140,7 +142,7 @@ export function getPerplexityModel(apiKey: OptionalApiKey, model: string) { export function getModel( provider: string, model: string, - env: Env, + serverEnv: Env, apiKeys?: Record, providerSettings?: Record, ) { @@ -148,9 +150,12 @@ export function getModel( * let apiKey; // Declare first * let baseURL; */ + // console.log({provider,model}); - const apiKey = getAPIKey(env, provider, apiKeys); // Then assign - const baseURL = providerSettings?.[provider].baseUrl || getBaseURL(env, provider); + const apiKey = getAPIKey(serverEnv, provider, apiKeys); // Then assign + const baseURL = getBaseURL(serverEnv, provider, providerSettings); + + // console.log({apiKey,baseURL}); switch (provider) { case 'Anthropic': diff --git a/app/lib/.server/llm/stream-text.ts b/app/lib/.server/llm/stream-text.ts index 74cdd9d..6bbf568 100644 --- a/app/lib/.server/llm/stream-text.ts +++ b/app/lib/.server/llm/stream-text.ts @@ -151,10 +151,13 @@ export async function streamText(props: { providerSettings?: Record; promptId?: string; }) { - const { messages, env, options, apiKeys, files, providerSettings, promptId } = props; + const { messages, env: serverEnv, options, apiKeys, files, providerSettings, promptId } = props; + + // console.log({serverEnv}); + let currentModel = DEFAULT_MODEL; let currentProvider = DEFAULT_PROVIDER.name; - const MODEL_LIST = await getModelList(apiKeys || {}, providerSettings); + const MODEL_LIST = await getModelList({ apiKeys, providerSettings, serverEnv: serverEnv as any }); const processedMessages = messages.map((message) => { if (message.role === 'user') { const { model, provider, content } = extractPropertiesFromMessage(message); @@ -196,7 +199,7 @@ export async function streamText(props: { } return _streamText({ - model: getModel(currentProvider, currentModel, env, apiKeys, providerSettings) as any, + model: getModel(currentProvider, currentModel, serverEnv, apiKeys, providerSettings) as any, system: systemPrompt, maxTokens: dynamicMaxTokens, messages: convertToCoreMessages(processedMessages as any), diff --git a/app/types/model.ts b/app/types/model.ts index 3bfbfde..a747a3f 100644 --- a/app/types/model.ts +++ b/app/types/model.ts @@ -3,7 +3,11 @@ import type { ModelInfo } from '~/utils/types'; export type ProviderInfo = { staticModels: ModelInfo[]; name: string; - getDynamicModels?: (apiKeys?: Record, providerSettings?: IProviderSetting) => Promise; + getDynamicModels?: ( + apiKeys?: Record, + providerSettings?: IProviderSetting, + serverEnv?: Record, + ) => Promise; getApiKeyLink?: string; labelForGetApiKey?: string; icon?: string; diff --git a/app/utils/constants.ts b/app/utils/constants.ts index 6425995..6595d9c 100644 --- a/app/utils/constants.ts +++ b/app/utils/constants.ts @@ -220,7 +220,6 @@ const PROVIDER_LIST: ProviderInfo[] = [ ], getApiKeyLink: 'https://huggingface.co/settings/tokens', }, - { name: 'OpenAI', staticModels: [ @@ -325,26 +324,46 @@ const staticModels: ModelInfo[] = PROVIDER_LIST.map((p) => p.staticModels).flat( export let MODEL_LIST: ModelInfo[] = [...staticModels]; -export async function getModelList( - apiKeys: Record, - providerSettings?: Record, -) { +export async function getModelList(options: { + apiKeys?: Record; + providerSettings?: Record; + serverEnv?: Record; +}) { + const { apiKeys, providerSettings, serverEnv } = options; + + // console.log({ providerSettings, serverEnv,env:process.env }); MODEL_LIST = [ ...( await Promise.all( PROVIDER_LIST.filter( (p): p is ProviderInfo & { getDynamicModels: () => Promise } => !!p.getDynamicModels, - ).map((p) => p.getDynamicModels(apiKeys, providerSettings?.[p.name])), + ).map((p) => p.getDynamicModels(apiKeys, providerSettings?.[p.name], serverEnv)), ) ).flat(), ...staticModels, ]; + return MODEL_LIST; } -async function getTogetherModels(apiKeys?: Record, settings?: IProviderSetting): Promise { +async function getTogetherModels( + apiKeys?: Record, + settings?: IProviderSetting, + serverEnv: Record = {}, +): Promise { try { - const baseUrl = settings?.baseUrl || import.meta.env.TOGETHER_API_BASE_URL || ''; + let settingsBaseUrl = settings?.baseUrl; + + if (settingsBaseUrl && settingsBaseUrl.length == 0) { + settingsBaseUrl = undefined; + } + + const baseUrl = + settingsBaseUrl || + serverEnv?.TOGETHER_API_BASE_URL || + process.env.TOGETHER_API_BASE_URL || + import.meta.env.TOGETHER_API_BASE_URL || + ''; const provider = 'Together'; if (!baseUrl) { @@ -383,8 +402,19 @@ async function getTogetherModels(apiKeys?: Record, settings?: IP } } -const getOllamaBaseUrl = (settings?: IProviderSetting) => { - const defaultBaseUrl = settings?.baseUrl || import.meta.env.OLLAMA_API_BASE_URL || 'http://localhost:11434'; +const getOllamaBaseUrl = (settings?: IProviderSetting, serverEnv: Record = {}) => { + let settingsBaseUrl = settings?.baseUrl; + + if (settingsBaseUrl && settingsBaseUrl.length == 0) { + settingsBaseUrl = undefined; + } + + const defaultBaseUrl = + settings?.baseUrl || + serverEnv?.OLLAMA_API_BASE_URL || + process.env.OLLAMA_API_BASE_URL || + import.meta.env.OLLAMA_API_BASE_URL || + 'http://localhost:11434'; // Check if we're in the browser if (typeof window !== 'undefined') { @@ -398,9 +428,13 @@ const getOllamaBaseUrl = (settings?: IProviderSetting) => { return isDocker ? defaultBaseUrl.replace('localhost', 'host.docker.internal') : defaultBaseUrl; }; -async function getOllamaModels(apiKeys?: Record, settings?: IProviderSetting): Promise { +async function getOllamaModels( + apiKeys?: Record, + settings?: IProviderSetting, + serverEnv: Record = {}, +): Promise { try { - const baseUrl = getOllamaBaseUrl(settings); + const baseUrl = getOllamaBaseUrl(settings, serverEnv); const response = await fetch(`${baseUrl}/api/tags`); const data = (await response.json()) as OllamaApiResponse; @@ -421,9 +455,21 @@ async function getOllamaModels(apiKeys?: Record, settings?: IPro async function getOpenAILikeModels( apiKeys?: Record, settings?: IProviderSetting, + serverEnv: Record = {}, ): Promise { try { - const baseUrl = settings?.baseUrl || import.meta.env.OPENAI_LIKE_API_BASE_URL || ''; + let settingsBaseUrl = settings?.baseUrl; + + if (settingsBaseUrl && settingsBaseUrl.length == 0) { + settingsBaseUrl = undefined; + } + + const baseUrl = + settingsBaseUrl || + serverEnv.OPENAI_LIKE_API_BASE_URL || + process.env.OPENAI_LIKE_API_BASE_URL || + import.meta.env.OPENAI_LIKE_API_BASE_URL || + ''; if (!baseUrl) { return []; @@ -486,9 +532,24 @@ async function getOpenRouterModels(): Promise { })); } -async function getLMStudioModels(_apiKeys?: Record, settings?: IProviderSetting): Promise { +async function getLMStudioModels( + _apiKeys?: Record, + settings?: IProviderSetting, + serverEnv: Record = {}, +): Promise { try { - const baseUrl = settings?.baseUrl || import.meta.env.LMSTUDIO_API_BASE_URL || 'http://localhost:1234'; + let settingsBaseUrl = settings?.baseUrl; + + if (settingsBaseUrl && settingsBaseUrl.length == 0) { + settingsBaseUrl = undefined; + } + + const baseUrl = + settingsBaseUrl || + serverEnv.LMSTUDIO_API_BASE_URL || + process.env.LMSTUDIO_API_BASE_URL || + import.meta.env.LMSTUDIO_API_BASE_URL || + 'http://localhost:1234'; const response = await fetch(`${baseUrl}/v1/models`); const data = (await response.json()) as any; @@ -503,29 +564,37 @@ async function getLMStudioModels(_apiKeys?: Record, settings?: I } } -async function initializeModelList(providerSettings?: Record): Promise { - let apiKeys: Record = {}; +async function initializeModelList(options: { + env?: Record; + providerSettings?: Record; + apiKeys?: Record; +}): Promise { + const { providerSettings, apiKeys: providedApiKeys, env } = options; + let apiKeys: Record = providedApiKeys || {}; - try { - const storedApiKeys = Cookies.get('apiKeys'); + if (!providedApiKeys) { + try { + const storedApiKeys = Cookies.get('apiKeys'); - if (storedApiKeys) { - const parsedKeys = JSON.parse(storedApiKeys); + if (storedApiKeys) { + const parsedKeys = JSON.parse(storedApiKeys); - if (typeof parsedKeys === 'object' && parsedKeys !== null) { - apiKeys = parsedKeys; + if (typeof parsedKeys === 'object' && parsedKeys !== null) { + apiKeys = parsedKeys; + } } + } catch (error: any) { + logStore.logError('Failed to fetch API keys from cookies', error); + logger.warn(`Failed to fetch apikeys from cookies: ${error?.message}`); } - } catch (error: any) { - logStore.logError('Failed to fetch API keys from cookies', error); - logger.warn(`Failed to fetch apikeys from cookies: ${error?.message}`); } + MODEL_LIST = [ ...( await Promise.all( PROVIDER_LIST.filter( (p): p is ProviderInfo & { getDynamicModels: () => Promise } => !!p.getDynamicModels, - ).map((p) => p.getDynamicModels(apiKeys, providerSettings?.[p.name])), + ).map((p) => p.getDynamicModels(apiKeys, providerSettings?.[p.name], env)), ) ).flat(), ...staticModels, @@ -534,6 +603,7 @@ async function initializeModelList(providerSettings?: Record Date: Wed, 18 Dec 2024 07:01:58 -0600 Subject: [PATCH 09/24] chore: a few documentation enhancements for main README and FAQs --- FAQ.md | 32 ++++++++++++++++++++++++-------- README.md | 6 ++++-- docs/docs/FAQ.md | 20 ++++++++++++++++++++ 3 files changed, 48 insertions(+), 10 deletions(-) diff --git a/FAQ.md b/FAQ.md index ecd4158..dcf250d 100644 --- a/FAQ.md +++ b/FAQ.md @@ -2,6 +2,18 @@ # bolt.diy +## Recommended Models for bolt.diy + +For the best experience with bolt.diy, we recommend using the following models: + +- **Claude 3.5 Sonnet (old)**: Best overall coder, providing excellent results across all use cases +- **Gemini 2.0 Flash**: Exceptional speed while maintaining good performance +- **GPT-4o**: Strong alternative to Claude 3.5 Sonnet with comparable capabilities +- **DeepSeekCoder V2 236b**: Best open source model (available through OpenRouter, DeepSeek API, or self-hosted) +- **Qwen 2.5 Coder 32b**: Best model for self-hosting with reasonable hardware requirements + +**Note**: Models with less than 7b parameters typically lack the capability to properly interact with bolt! + ## FAQ ### How do I get the best results with bolt.diy? @@ -34,14 +46,18 @@ We have seen this error a couple times and for some reason just restarting the D We promise you that we are constantly testing new PRs coming into bolt.diy and the preview is core functionality, so the application is not broken! When you get a blank preview or don’t get a preview, this is generally because the LLM hallucinated bad code or incorrect commands. We are working on making this more transparent so it is obvious. Sometimes the error will appear in developer console too so check that as well. -### How to add a LLM: - -To make new LLMs available to use in this version of bolt.new, head on over to `app/utils/constants.ts` and find the constant MODEL_LIST. Each element in this array is an object that has the model ID for the name (get this from the provider's API documentation), a label for the frontend model dropdown, and the provider. - -By default, Anthropic, OpenAI, Groq, and Ollama are implemented as providers, but the YouTube video for this repo covers how to extend this to work with more providers if you wish! - -When you add a new model to the MODEL_LIST array, it will immediately be available to use when you run the app locally or reload it. For Ollama models, make sure you have the model installed already before trying to use it here! - ### Everything works but the results are bad This goes to the point above about how local LLMs are getting very powerful but you still are going to see better (sometimes much better) results with the largest LLMs like GPT-4o, Claude 3.5 Sonnet, and DeepSeek Coder V2 236b. If you are using smaller LLMs like Qwen-2.5-Coder, consider it more experimental and educational at this point. It can build smaller applications really well, which is super impressive for a local LLM, but for larger scale applications you want to use the larger LLMs still! + +### Received structured exception #0xc0000005: access violation + +If you are getting this, you are probably on Windows. The fix is generally to update the [Visual C++ Redistributable](https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist?view=msvc-170) + +### How to add an LLM: + +To make new LLMs available to use in this version of bolt.new, head on over to `app/utils/constants.ts` and find the constant MODEL_LIST. Each element in this array is an object that has the model ID for the name (get this from the provider's API documentation), a label for the frontend model dropdown, and the provider. + +By default, many providers are already implemented, but the YouTube video for this repo covers how to extend this to work with more providers if you wish! + +When you add a new model to the MODEL_LIST array, it will immediately be available to use when you run the app locally or reload it. diff --git a/README.md b/README.md index c7387ee..7ad4abe 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,9 @@ Welcome to bolt.diy, the official open source version of Bolt.new (previously known as oTToDev and bolt.new ANY LLM), 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, or Groq models - 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 information. This documentation is still being updated after the transfer. +Check the [bolt.diy Docs](https://stackblitz-labs.github.io/bolt.diy/) for more information. + +We have also launched an experimental agent called the "bolt.diy Expert" that can answer common questions about bolt.diy. Find it here on the [oTTomator Live Agent Studio](https://studio.ottomator.ai/). bolt.diy was originally started by [Cole Medin](https://www.youtube.com/@ColeMedin) but has quickly grown into a massive community effort to build the BEST open source AI coding assistant! @@ -211,4 +213,4 @@ Explore upcoming features and priorities on our [Roadmap](https://roadmap.sh/r/o ## FAQ -For answers to common questions, visit our [FAQ Page](FAQ.md). +For answers to common questions, issues, and to see a list of recommended models, visit our [FAQ Page](FAQ.md). diff --git a/docs/docs/FAQ.md b/docs/docs/FAQ.md index 9f18a88..1b645d3 100644 --- a/docs/docs/FAQ.md +++ b/docs/docs/FAQ.md @@ -1,5 +1,19 @@ # Frequently Asked Questions (FAQ) +## What are the best models for bolt.diy? + +For the best experience with bolt.diy, we recommend using the following models: + +- **Claude 3.5 Sonnet (old)**: Best overall coder, providing excellent results across all use cases +- **Gemini 2.0 Flash**: Exceptional speed while maintaining good performance +- **GPT-4o**: Strong alternative to Claude 3.5 Sonnet with comparable capabilities +- **DeepSeekCoder V2 236b**: Best open source model (available through OpenRouter, DeepSeek API, or self-hosted) +- **Qwen 2.5 Coder 32b**: Best model for self-hosting with reasonable hardware requirements + +**Note**: Models with less than 7b parameters typically lack the capability to properly interact with bolt! + +--- + ## How do I get the best results with bolt.diy? - **Be specific about your stack**: @@ -72,6 +86,12 @@ Local LLMs like Qwen-2.5-Coder are powerful for small applications but still exp --- +### **"Received structured exception #0xc0000005: access violation"** + +If you are getting this, you are probably on Windows. The fix is generally to update the [Visual C++ Redistributable](https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist?view=msvc-170) + +--- + ### **"Miniflare or Wrangler errors in Windows"** You will need to make sure you have the latest version of Visual Studio C++ installed (14.40.33816), more information here https://github.com/stackblitz-labs/bolt.diy/issues/19. From 283eb22ae57cfd2341fc5a2748f2f4b5d8107ef1 Mon Sep 17 00:00:00 2001 From: Anirban Kar Date: Wed, 18 Dec 2024 20:04:43 +0530 Subject: [PATCH 10/24] added indicator on settings menu --- app/commit.json | 2 +- app/components/settings/debug/DebugTab.tsx | 17 +- .../settings/providers/ProvidersTab.tsx | 119 ++++++----- app/lib/.server/llm/api-key.ts | 29 ++- app/types/model.ts | 1 + app/utils/constants.ts | 198 ++++++++++++------ vite.config.ts | 2 +- 7 files changed, 237 insertions(+), 131 deletions(-) diff --git a/app/commit.json b/app/commit.json index 4ff5294..b0a4c92 100644 --- a/app/commit.json +++ b/app/commit.json @@ -1 +1 @@ -{ "commit": "fce8999f27c0affbc762dc90de992b5a759ab325" } +{ "commit": "62ebfe51a69788229aa62d34afb4ce89b7cd8ac8" } diff --git a/app/components/settings/debug/DebugTab.tsx b/app/components/settings/debug/DebugTab.tsx index cf2341b..b0cde7d 100644 --- a/app/components/settings/debug/DebugTab.tsx +++ b/app/components/settings/debug/DebugTab.tsx @@ -2,6 +2,7 @@ import React, { useCallback, useEffect, useState } from 'react'; import { useSettings } from '~/lib/hooks/useSettings'; import commit from '~/commit.json'; import { toast } from 'react-toastify'; +import { providerBaseUrlEnvKeys } from '~/utils/constants'; interface ProviderStatus { name: string; @@ -236,7 +237,7 @@ const checkProviderStatus = async (url: string | null, providerName: string): Pr } // Try different endpoints based on provider - const checkUrls = [`${url}/api/health`, `${url}/v1/models`]; + const checkUrls = [`${url}/api/health`, url.endsWith('v1') ? `${url}/models` : `${url}/v1/models`]; console.log(`[Debug] Checking additional endpoints:`, checkUrls); const results = await Promise.all( @@ -321,14 +322,16 @@ export default function DebugTab() { .filter(([, provider]) => LOCAL_PROVIDERS.includes(provider.name)) .map(async ([, provider]) => { const envVarName = - provider.name.toLowerCase() === 'ollama' - ? 'OLLAMA_API_BASE_URL' - : provider.name.toLowerCase() === 'lmstudio' - ? 'LMSTUDIO_API_BASE_URL' - : `REACT_APP_${provider.name.toUpperCase()}_URL`; + providerBaseUrlEnvKeys[provider.name].baseUrlKey || `REACT_APP_${provider.name.toUpperCase()}_URL`; // Access environment variables through import.meta.env - const url = import.meta.env[envVarName] || provider.settings.baseUrl || null; // Ensure baseUrl is used + let settingsUrl = provider.settings.baseUrl; + + if (settingsUrl && settingsUrl.trim().length === 0) { + settingsUrl = undefined; + } + + const url = settingsUrl || import.meta.env[envVarName] || null; // Ensure baseUrl is used console.log(`[Debug] Using URL for ${provider.name}:`, url, `(from ${envVarName})`); const status = await checkProviderStatus(url, provider.name); diff --git a/app/components/settings/providers/ProvidersTab.tsx b/app/components/settings/providers/ProvidersTab.tsx index 49a16f6..20e66ef 100644 --- a/app/components/settings/providers/ProvidersTab.tsx +++ b/app/components/settings/providers/ProvidersTab.tsx @@ -7,6 +7,7 @@ import { logStore } from '~/lib/stores/logs'; // Import a default fallback icon import DefaultIcon from '/icons/Default.svg'; // Adjust the path as necessary +import { providerBaseUrlEnvKeys } from '~/utils/constants'; export default function ProvidersTab() { const { providers, updateProviderSettings, isLocalModel } = useSettings(); @@ -47,65 +48,77 @@ export default function ProvidersTab() { className="w-full bg-white dark:bg-bolt-elements-background-depth-4 relative px-2 py-1.5 rounded-md focus:outline-none placeholder-bolt-elements-textTertiary text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary border border-bolt-elements-borderColor" />
- {filteredProviders.map((provider) => ( -
-
-
- { - // Fallback to default icon on error - e.currentTarget.src = DefaultIcon; - }} - alt={`${provider.name} icon`} - className="w-6 h-6 dark:invert" - /> - {provider.name} -
- { - updateProviderSettings(provider.name, { ...provider.settings, enabled }); + {filteredProviders.map((provider) => { + const envBaseUrlKey = providerBaseUrlEnvKeys[provider.name].baseUrlKey; + const envBaseUrl = envBaseUrlKey ? import.meta.env[envBaseUrlKey] : undefined; - if (enabled) { - logStore.logProvider(`Provider ${provider.name} enabled`, { provider: provider.name }); - } else { - logStore.logProvider(`Provider ${provider.name} disabled`, { provider: provider.name }); - } - }} - /> -
- {/* Base URL input for configurable providers */} - {URL_CONFIGURABLE_PROVIDERS.includes(provider.name) && provider.settings.enabled && ( -
- - { - let newBaseUrl: string | undefined = e.target.value; + return ( +
+
+
+ { + // Fallback to default icon on error + e.currentTarget.src = DefaultIcon; + }} + alt={`${provider.name} icon`} + className="w-6 h-6 dark:invert" + /> + {provider.name} +
+ { + updateProviderSettings(provider.name, { ...provider.settings, enabled }); - if (newBaseUrl && newBaseUrl.trim().length === 0) { - newBaseUrl = undefined; + if (enabled) { + logStore.logProvider(`Provider ${provider.name} enabled`, { provider: provider.name }); + } else { + logStore.logProvider(`Provider ${provider.name} disabled`, { provider: provider.name }); } - - updateProviderSettings(provider.name, { ...provider.settings, baseUrl: newBaseUrl }); - logStore.logProvider(`Base URL updated for ${provider.name}`, { - provider: provider.name, - baseUrl: newBaseUrl, - }); }} - placeholder={`Enter ${provider.name} base URL`} - className="w-full bg-white dark:bg-bolt-elements-background-depth-4 relative px-2 py-1.5 rounded-md focus:outline-none placeholder-bolt-elements-textTertiary text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary border border-bolt-elements-borderColor" />
- )} -
- ))} + {/* Base URL input for configurable providers */} + {URL_CONFIGURABLE_PROVIDERS.includes(provider.name) && provider.settings.enabled && ( +
+ {envBaseUrl && ( + + )} + + { + let newBaseUrl: string | undefined = e.target.value; + + if (newBaseUrl && newBaseUrl.trim().length === 0) { + newBaseUrl = undefined; + } + + updateProviderSettings(provider.name, { ...provider.settings, baseUrl: newBaseUrl }); + logStore.logProvider(`Base URL updated for ${provider.name}`, { + provider: provider.name, + baseUrl: newBaseUrl, + }); + }} + placeholder={`Enter ${provider.name} base URL`} + className="w-full bg-white dark:bg-bolt-elements-background-depth-4 relative px-2 py-1.5 rounded-md focus:outline-none placeholder-bolt-elements-textTertiary text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary border border-bolt-elements-borderColor" + /> +
+ )} +
+ ); + })}
); } diff --git a/app/lib/.server/llm/api-key.ts b/app/lib/.server/llm/api-key.ts index d21f070..83b4646 100644 --- a/app/lib/.server/llm/api-key.ts +++ b/app/lib/.server/llm/api-key.ts @@ -4,6 +4,7 @@ */ import { env } from 'node:process'; import type { IProviderSetting } from '~/types/model'; +import { getProviderBaseUrlAndKey } from '~/utils/constants'; export function getAPIKey(cloudflareEnv: Env, provider: string, userApiKeys?: Record) { /** @@ -16,7 +17,20 @@ export function getAPIKey(cloudflareEnv: Env, provider: string, userApiKeys?: Re return userApiKeys[provider]; } - // Fall back to environment variables + const { apiKey } = getProviderBaseUrlAndKey({ + provider, + apiKeys: userApiKeys, + providerSettings: undefined, + serverEnv: cloudflareEnv as any, + defaultBaseUrlKey: '', + defaultApiTokenKey: '', + }); + + if (apiKey) { + return apiKey; + } + + // Fall back to hardcoded environment variables names switch (provider) { case 'Anthropic': return env.ANTHROPIC_API_KEY || cloudflareEnv.ANTHROPIC_API_KEY; @@ -52,6 +66,19 @@ export function getAPIKey(cloudflareEnv: Env, provider: string, userApiKeys?: Re } export function getBaseURL(cloudflareEnv: Env, provider: string, providerSettings?: Record) { + const { baseUrl } = getProviderBaseUrlAndKey({ + provider, + apiKeys: {}, + providerSettings, + serverEnv: cloudflareEnv as any, + defaultBaseUrlKey: '', + defaultApiTokenKey: '', + }); + + if (baseUrl) { + return baseUrl; + } + let settingBaseUrl = providerSettings?.[provider].baseUrl; if (settingBaseUrl && settingBaseUrl.length == 0) { diff --git a/app/types/model.ts b/app/types/model.ts index a747a3f..b449363 100644 --- a/app/types/model.ts +++ b/app/types/model.ts @@ -4,6 +4,7 @@ export type ProviderInfo = { staticModels: ModelInfo[]; name: string; getDynamicModels?: ( + providerName: string, apiKeys?: Record, providerSettings?: IProviderSetting, serverEnv?: Record, diff --git a/app/utils/constants.ts b/app/utils/constants.ts index 6595d9c..c4eb0ae 100644 --- a/app/utils/constants.ts +++ b/app/utils/constants.ts @@ -318,6 +318,83 @@ const PROVIDER_LIST: ProviderInfo[] = [ }, ]; +export const providerBaseUrlEnvKeys: Record = { + Anthropic: { + apiTokenKey: 'ANTHROPIC_API_KEY', + }, + OpenAI: { + apiTokenKey: 'OPENAI_API_KEY', + }, + Groq: { + apiTokenKey: 'GROQ_API_KEY', + }, + HuggingFace: { + apiTokenKey: 'HuggingFace_API_KEY', + }, + OpenRouter: { + apiTokenKey: 'OPEN_ROUTER_API_KEY', + }, + Google: { + apiTokenKey: 'GOOGLE_GENERATIVE_AI_API_KEY', + }, + OpenAILike: { + baseUrlKey: 'OPENAI_LIKE_API_BASE_URL', + apiTokenKey: 'OPENAI_LIKE_API_KEY', + }, + Together: { + baseUrlKey: 'TOGETHER_API_BASE_URL', + apiTokenKey: 'TOGETHER_API_KEY', + }, + Deepseek: { + apiTokenKey: 'DEEPSEEK_API_KEY', + }, + Mistral: { + apiTokenKey: 'MISTRAL_API_KEY', + }, + LMStudio: { + baseUrlKey: 'LMSTUDIO_API_BASE_URL', + }, + xAI: { + apiTokenKey: 'XAI_API_KEY', + }, + Cohere: { + apiTokenKey: 'COHERE_API_KEY', + }, + Perplexity: { + apiTokenKey: 'PERPLEXITY_API_KEY', + }, + Ollama: { + baseUrlKey: 'OLLAMA_API_BASE_URL', + }, +}; + +export const getProviderBaseUrlAndKey = (options: { + provider: string; + apiKeys?: Record; + providerSettings?: IProviderSetting; + serverEnv?: Record; + defaultBaseUrlKey: string; + defaultApiTokenKey: string; +}) => { + const { provider, apiKeys, providerSettings, serverEnv, defaultBaseUrlKey, defaultApiTokenKey } = options; + let settingsBaseUrl = providerSettings?.baseUrl; + + if (settingsBaseUrl && settingsBaseUrl.length == 0) { + settingsBaseUrl = undefined; + } + + const baseUrlKey = providerBaseUrlEnvKeys[provider]?.baseUrlKey || defaultBaseUrlKey; + const baseUrl = settingsBaseUrl || serverEnv?.[baseUrlKey] || process.env[baseUrlKey] || import.meta.env[baseUrlKey]; + + const apiTokenKey = providerBaseUrlEnvKeys[provider]?.apiTokenKey || defaultApiTokenKey; + const apiKey = + apiKeys?.[provider] || serverEnv?.[apiTokenKey] || process.env[apiTokenKey] || import.meta.env[apiTokenKey]; + + return { + baseUrl, + apiKey, + }; +}; export const DEFAULT_PROVIDER = PROVIDER_LIST[0]; const staticModels: ModelInfo[] = PROVIDER_LIST.map((p) => p.staticModels).flat(); @@ -337,7 +414,7 @@ export async function getModelList(options: { await Promise.all( PROVIDER_LIST.filter( (p): p is ProviderInfo & { getDynamicModels: () => Promise } => !!p.getDynamicModels, - ).map((p) => p.getDynamicModels(apiKeys, providerSettings?.[p.name], serverEnv)), + ).map((p) => p.getDynamicModels(p.name, apiKeys, providerSettings?.[p.name], serverEnv)), ) ).flat(), ...staticModels, @@ -347,35 +424,26 @@ export async function getModelList(options: { } async function getTogetherModels( + name: string, apiKeys?: Record, settings?: IProviderSetting, serverEnv: Record = {}, ): Promise { try { - let settingsBaseUrl = settings?.baseUrl; - - if (settingsBaseUrl && settingsBaseUrl.length == 0) { - settingsBaseUrl = undefined; - } - - const baseUrl = - settingsBaseUrl || - serverEnv?.TOGETHER_API_BASE_URL || - process.env.TOGETHER_API_BASE_URL || - import.meta.env.TOGETHER_API_BASE_URL || - ''; - const provider = 'Together'; + const { baseUrl, apiKey } = getProviderBaseUrlAndKey({ + provider: name, + apiKeys, + providerSettings: settings, + serverEnv, + defaultBaseUrlKey: 'TOGETHER_API_BASE_URL', + defaultApiTokenKey: 'TOGETHER_API_KEY', + }); + console.log({ baseUrl, apiKey }); if (!baseUrl) { return []; } - let apiKey = import.meta.env.OPENAI_LIKE_API_KEY ?? ''; - - if (apiKeys && apiKeys[provider]) { - apiKey = apiKeys[provider]; - } - if (!apiKey) { return []; } @@ -393,7 +461,7 @@ async function getTogetherModels( label: `${m.display_name} - in:$${m.pricing.input.toFixed( 2, )} out:$${m.pricing.output.toFixed(2)} - context ${Math.floor(m.context_length / 1000)}k`, - provider, + provider: name, maxTokenAllowed: 8000, })); } catch (e) { @@ -402,39 +470,40 @@ async function getTogetherModels( } } -const getOllamaBaseUrl = (settings?: IProviderSetting, serverEnv: Record = {}) => { - let settingsBaseUrl = settings?.baseUrl; - - if (settingsBaseUrl && settingsBaseUrl.length == 0) { - settingsBaseUrl = undefined; - } - - const defaultBaseUrl = - settings?.baseUrl || - serverEnv?.OLLAMA_API_BASE_URL || - process.env.OLLAMA_API_BASE_URL || - import.meta.env.OLLAMA_API_BASE_URL || - 'http://localhost:11434'; +const getOllamaBaseUrl = (name: string, settings?: IProviderSetting, serverEnv: Record = {}) => { + const { baseUrl } = getProviderBaseUrlAndKey({ + provider: name, + providerSettings: settings, + serverEnv, + defaultBaseUrlKey: 'OLLAMA_API_BASE_URL', + defaultApiTokenKey: '', + }); // Check if we're in the browser if (typeof window !== 'undefined') { // Frontend always uses localhost - return defaultBaseUrl; + return baseUrl; } // Backend: Check if we're running in Docker const isDocker = process.env.RUNNING_IN_DOCKER === 'true'; - return isDocker ? defaultBaseUrl.replace('localhost', 'host.docker.internal') : defaultBaseUrl; + return isDocker ? baseUrl.replace('localhost', 'host.docker.internal') : baseUrl; }; async function getOllamaModels( - apiKeys?: Record, + name: string, + _apiKeys?: Record, settings?: IProviderSetting, serverEnv: Record = {}, ): Promise { try { - const baseUrl = getOllamaBaseUrl(settings, serverEnv); + const baseUrl = getOllamaBaseUrl(name, settings, serverEnv); + + if (!baseUrl) { + return []; + } + const response = await fetch(`${baseUrl}/api/tags`); const data = (await response.json()) as OllamaApiResponse; @@ -453,34 +522,25 @@ async function getOllamaModels( } async function getOpenAILikeModels( + name: string, apiKeys?: Record, settings?: IProviderSetting, serverEnv: Record = {}, ): Promise { try { - let settingsBaseUrl = settings?.baseUrl; - - if (settingsBaseUrl && settingsBaseUrl.length == 0) { - settingsBaseUrl = undefined; - } - - const baseUrl = - settingsBaseUrl || - serverEnv.OPENAI_LIKE_API_BASE_URL || - process.env.OPENAI_LIKE_API_BASE_URL || - import.meta.env.OPENAI_LIKE_API_BASE_URL || - ''; + const { baseUrl, apiKey } = getProviderBaseUrlAndKey({ + provider: name, + apiKeys, + providerSettings: settings, + serverEnv, + defaultBaseUrlKey: 'OPENAI_LIKE_API_BASE_URL', + defaultApiTokenKey: 'OPENAI_LIKE_API_KEY', + }); if (!baseUrl) { return []; } - let apiKey = ''; - - if (apiKeys && apiKeys.OpenAILike) { - apiKey = apiKeys.OpenAILike; - } - const response = await fetch(`${baseUrl}/models`, { headers: { Authorization: `Bearer ${apiKey}`, @@ -491,7 +551,7 @@ async function getOpenAILikeModels( return res.data.map((model: any) => ({ name: model.id, label: model.id, - provider: 'OpenAILike', + provider: name, })); } catch (e) { console.error('Error getting OpenAILike models:', e); @@ -533,23 +593,25 @@ async function getOpenRouterModels(): Promise { } async function getLMStudioModels( - _apiKeys?: Record, + name: string, + apiKeys?: Record, settings?: IProviderSetting, serverEnv: Record = {}, ): Promise { try { - let settingsBaseUrl = settings?.baseUrl; + const { baseUrl } = getProviderBaseUrlAndKey({ + provider: name, + apiKeys, + providerSettings: settings, + serverEnv, + defaultBaseUrlKey: 'LMSTUDIO_API_BASE_URL', + defaultApiTokenKey: '', + }); - if (settingsBaseUrl && settingsBaseUrl.length == 0) { - settingsBaseUrl = undefined; + if (!baseUrl) { + return []; } - const baseUrl = - settingsBaseUrl || - serverEnv.LMSTUDIO_API_BASE_URL || - process.env.LMSTUDIO_API_BASE_URL || - import.meta.env.LMSTUDIO_API_BASE_URL || - 'http://localhost:1234'; const response = await fetch(`${baseUrl}/v1/models`); const data = (await response.json()) as any; @@ -594,7 +656,7 @@ async function initializeModelList(options: { await Promise.all( PROVIDER_LIST.filter( (p): p is ProviderInfo & { getDynamicModels: () => Promise } => !!p.getDynamicModels, - ).map((p) => p.getDynamicModels(apiKeys, providerSettings?.[p.name], env)), + ).map((p) => p.getDynamicModels(p.name, apiKeys, providerSettings?.[p.name], env)), ) ).flat(), ...staticModels, diff --git a/vite.config.ts b/vite.config.ts index f18b8b9..d96f704 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -28,7 +28,7 @@ export default defineConfig((config) => { chrome129IssuePlugin(), config.mode === 'production' && optimizeCssModules({ apply: 'build' }), ], - envPrefix: ["VITE_", "OPENAI_LIKE_API_", "OLLAMA_API_BASE_URL", "LMSTUDIO_API_BASE_URL","TOGETHER_API_BASE_URL"], + envPrefix: ["VITE_", "OPENAI_LIKE_API_", "OLLAMA_API_BASE_URL", "LMSTUDIO_API_BASE_URL","TOGETHER_API_"], css: { preprocessorOptions: { scss: { From 90c9c9c760d672d6c9417f56ee1ffb130c6b32f0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 18 Dec 2024 15:12:36 +0000 Subject: [PATCH 11/24] chore: update commit hash to 6458211bed379396e797e6da2944f6627a428c40 --- app/commit.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/commit.json b/app/commit.json index 1386a05..f60c148 100644 --- a/app/commit.json +++ b/app/commit.json @@ -1 +1 @@ -{ "commit": "a9309161e95a8ed015f2f71b622fb63afdb74877", "version": "0.0.3" } +{ "commit": "6458211bed379396e797e6da2944f6627a428c40", "version": "0.0.3" } From 6975083fb9565b94b7e73064097f35c3f11527dc Mon Sep 17 00:00:00 2001 From: Anirban Kar Date: Wed, 18 Dec 2024 21:31:50 +0530 Subject: [PATCH 12/24] stopped apikeys set to envfile being exposed to UI --- app/commit.json | 2 +- vite.config.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/commit.json b/app/commit.json index 393abd3..f8c2532 100644 --- a/app/commit.json +++ b/app/commit.json @@ -1 +1 @@ -{ "commit": "e74d6cafb53f6eb2bb80c32014b27ac0aa56e7fe" } +{ "commit": "26a3bcf9b6401e606b5063830550cd6022f73899" } diff --git a/vite.config.ts b/vite.config.ts index d96f704..f292c7b 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -28,7 +28,7 @@ export default defineConfig((config) => { chrome129IssuePlugin(), config.mode === 'production' && optimizeCssModules({ apply: 'build' }), ], - envPrefix: ["VITE_", "OPENAI_LIKE_API_", "OLLAMA_API_BASE_URL", "LMSTUDIO_API_BASE_URL","TOGETHER_API_"], + envPrefix: ["VITE_", "OLLAMA_API_BASE_URL", "LMSTUDIO_API_BASE_URL","TOGETHER_API_BASE_URL"], css: { preprocessorOptions: { scss: { From d37c3736d5e73b0305f19d1bbc7c47a6dfbf7656 Mon Sep 17 00:00:00 2001 From: Anirban Kar Date: Wed, 18 Dec 2024 21:34:18 +0530 Subject: [PATCH 13/24] removed logs --- app/commit.json | 2 +- app/lib/.server/llm/model.ts | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/app/commit.json b/app/commit.json index f8c2532..cd2195b 100644 --- a/app/commit.json +++ b/app/commit.json @@ -1 +1 @@ -{ "commit": "26a3bcf9b6401e606b5063830550cd6022f73899" } +{ "commit": "6975083fb9565b94b7e73064097f35c3f11527dc" } diff --git a/app/lib/.server/llm/model.ts b/app/lib/.server/llm/model.ts index 1feb499..308e27d 100644 --- a/app/lib/.server/llm/model.ts +++ b/app/lib/.server/llm/model.ts @@ -84,8 +84,6 @@ export function getHuggingFaceModel(apiKey: OptionalApiKey, model: string) { } export function getOllamaModel(baseURL: string, model: string) { - console.log({ baseURL, model }); - const ollamaInstance = ollama(model, { numCtx: DEFAULT_NUM_CTX, }) as LanguageModelV1 & { config: any }; From b892d708ec5ec5396b810bad48cf3dedb2b05769 Mon Sep 17 00:00:00 2001 From: Anirban Kar Date: Wed, 18 Dec 2024 21:55:17 +0530 Subject: [PATCH 14/24] removed logs and openAI fix --- app/commit.json | 2 +- app/utils/constants.ts | 2 -- vite.config.ts | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/app/commit.json b/app/commit.json index cd2195b..432fff8 100644 --- a/app/commit.json +++ b/app/commit.json @@ -1 +1 @@ -{ "commit": "6975083fb9565b94b7e73064097f35c3f11527dc" } +{ "commit": "d37c3736d5e73b0305f19d1bbc7c47a6dfbf7656" } diff --git a/app/utils/constants.ts b/app/utils/constants.ts index c4eb0ae..b80b3c8 100644 --- a/app/utils/constants.ts +++ b/app/utils/constants.ts @@ -408,7 +408,6 @@ export async function getModelList(options: { }) { const { apiKeys, providerSettings, serverEnv } = options; - // console.log({ providerSettings, serverEnv,env:process.env }); MODEL_LIST = [ ...( await Promise.all( @@ -438,7 +437,6 @@ async function getTogetherModels( defaultBaseUrlKey: 'TOGETHER_API_BASE_URL', defaultApiTokenKey: 'TOGETHER_API_KEY', }); - console.log({ baseUrl, apiKey }); if (!baseUrl) { return []; diff --git a/vite.config.ts b/vite.config.ts index f292c7b..b2f795d 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -28,7 +28,7 @@ export default defineConfig((config) => { chrome129IssuePlugin(), config.mode === 'production' && optimizeCssModules({ apply: 'build' }), ], - envPrefix: ["VITE_", "OLLAMA_API_BASE_URL", "LMSTUDIO_API_BASE_URL","TOGETHER_API_BASE_URL"], + envPrefix: ["VITE_","OPENAI_LIKE_API_BASE_URL", "OLLAMA_API_BASE_URL", "LMSTUDIO_API_BASE_URL","TOGETHER_API_BASE_URL"], css: { preprocessorOptions: { scss: { From 296e5d9f779a818210caed974b75ca8e5644ea8c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 18 Dec 2024 16:28:44 +0000 Subject: [PATCH 15/24] chore: update commit hash to 50e677878446f622531123b19912f38e8246afbd --- app/commit.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/commit.json b/app/commit.json index f60c148..71f2fed 100644 --- a/app/commit.json +++ b/app/commit.json @@ -1 +1 @@ -{ "commit": "6458211bed379396e797e6da2944f6627a428c40", "version": "0.0.3" } +{ "commit": "50e677878446f622531123b19912f38e8246afbd", "version": "0.0.3" } From 906cc38e7932de05d96e369665f2c9c3d52b0e74 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 19 Dec 2024 09:20:48 +0000 Subject: [PATCH 16/24] chore: update commit hash to 95dbcf1eb76b308435eae3e58ce47acef3e5fc44 --- app/commit.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/commit.json b/app/commit.json index 71f2fed..5dc1da4 100644 --- a/app/commit.json +++ b/app/commit.json @@ -1 +1 @@ -{ "commit": "50e677878446f622531123b19912f38e8246afbd", "version": "0.0.3" } +{ "commit": "95dbcf1eb76b308435eae3e58ce47acef3e5fc44", "version": "0.0.3" } From 1d716c09254bed51d0a2ab773a27b370e28c9a6b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 19 Dec 2024 11:25:23 +0000 Subject: [PATCH 17/24] chore: update commit hash to de640076978bd15f3a62f235f14332b08b76b98a --- app/commit.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/commit.json b/app/commit.json index 5dc1da4..7d25e1e 100644 --- a/app/commit.json +++ b/app/commit.json @@ -1 +1 @@ -{ "commit": "95dbcf1eb76b308435eae3e58ce47acef3e5fc44", "version": "0.0.3" } +{ "commit": "de640076978bd15f3a62f235f14332b08b76b98a", "version": "0.0.3" } From 7e344aaa5c201a6f208e5a9adcfbcc76c0ef556a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 19 Dec 2024 12:33:56 +0000 Subject: [PATCH 18/24] chore: update commit hash to 381d490f1f8cf87a0262f9e35df388b1e9a134f2 --- app/commit.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/commit.json b/app/commit.json index 7d25e1e..4f6763b 100644 --- a/app/commit.json +++ b/app/commit.json @@ -1 +1 @@ -{ "commit": "de640076978bd15f3a62f235f14332b08b76b98a", "version": "0.0.3" } +{ "commit": "381d490f1f8cf87a0262f9e35df388b1e9a134f2", "version": "0.0.3" } From d4400a57046357fbb260932ce63ffe7a58b2d703 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 19 Dec 2024 15:53:04 +0000 Subject: [PATCH 19/24] chore: update commit hash to a53b10ff399c591e898182e4b3934c26db19b6d6 --- app/commit.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/commit.json b/app/commit.json index 4f6763b..f2fdb14 100644 --- a/app/commit.json +++ b/app/commit.json @@ -1 +1 @@ -{ "commit": "381d490f1f8cf87a0262f9e35df388b1e9a134f2", "version": "0.0.3" } +{ "commit": "a53b10ff399c591e898182e4b3934c26db19b6d6", "version": "0.0.3" } From a7551ecfcca491600a720751b5af778bd1b77143 Mon Sep 17 00:00:00 2001 From: Dustin Loring Date: Thu, 19 Dec 2024 12:57:59 -0500 Subject: [PATCH 20/24] Update ProvidersTab.tsx --- .../settings/providers/ProvidersTab.tsx | 226 +++++++----------- 1 file changed, 89 insertions(+), 137 deletions(-) diff --git a/app/components/settings/providers/ProvidersTab.tsx b/app/components/settings/providers/ProvidersTab.tsx index d52d42b..c9dc27c 100644 --- a/app/components/settings/providers/ProvidersTab.tsx +++ b/app/components/settings/providers/ProvidersTab.tsx @@ -6,17 +6,14 @@ import type { IProviderConfig } from '~/types/model'; import { logStore } from '~/lib/stores/logs'; // Import a default fallback icon -import DefaultIcon from '/icons/Default.svg'; -const ADVANCED_PROVIDERS = ['Ollama', 'OpenAILike', 'LMStudio']; +import DefaultIcon from '/icons/Default.svg'; // Adjust the path as necessary import { providerBaseUrlEnvKeys } from '~/utils/constants'; - export default function ProvidersTab() { const { providers, updateProviderSettings, isLocalModel } = useSettings(); const [filteredProviders, setFilteredProviders] = useState([]); - const [advancedProviders, setAdvancedProviders] = useState([]); - const [regularProviders, setRegularProviders] = useState([]); + // Load base URLs from cookies const [searchTerm, setSearchTerm] = useState(''); useEffect(() => { @@ -35,76 +32,88 @@ export default function ProvidersTab() { newFilteredProviders = newFilteredProviders.filter((provider) => !LOCAL_PROVIDERS.includes(provider.name)); } - // Split providers into regular and advanced - const regular = newFilteredProviders.filter( - (provider) => !ADVANCED_PROVIDERS.includes(provider.name) - ); - const advanced = newFilteredProviders.filter( - (provider) => ADVANCED_PROVIDERS.includes(provider.name) - ); + newFilteredProviders.sort((a, b) => a.name.localeCompare(b.name)); - // Sort advanced providers in specific order - OpenAILike at the end - const advancedOrder = ['Ollama', 'LMStudio', 'OpenAILike']; - advanced.sort((a, b) => { - return advancedOrder.indexOf(a.name) - advancedOrder.indexOf(b.name); - }); + // Split providers into regular and URL-configurable + const regular = newFilteredProviders.filter(p => !URL_CONFIGURABLE_PROVIDERS.includes(p.name)); + const urlConfigurable = newFilteredProviders.filter(p => URL_CONFIGURABLE_PROVIDERS.includes(p.name)); - // Sort regular providers alphabetically - regular.sort((a, b) => a.name.localeCompare(b.name)); - - setRegularProviders(regular); - setAdvancedProviders(advanced); - setFilteredProviders(newFilteredProviders); + setFilteredProviders([...regular, ...urlConfigurable]); }, [providers, searchTerm, isLocalModel]); - const ProviderCard = ({ provider }: { provider: IProviderConfig }) => ( -
-
-
- { - e.currentTarget.src = DefaultIcon; + const renderProviderCard = (provider: IProviderConfig) => { + const envBaseUrlKey = providerBaseUrlEnvKeys[provider.name].baseUrlKey; + const envBaseUrl = envBaseUrlKey ? import.meta.env[envBaseUrlKey] : undefined; + const isUrlConfigurable = URL_CONFIGURABLE_PROVIDERS.includes(provider.name); + + return ( +
+
+
+ { + e.currentTarget.src = DefaultIcon; + }} + alt={`${provider.name} icon`} + className="w-6 h-6 dark:invert" + /> + {provider.name} +
+ { + updateProviderSettings(provider.name, { ...provider.settings, enabled }); + + if (enabled) { + logStore.logProvider(`Provider ${provider.name} enabled`, { provider: provider.name }); + } else { + logStore.logProvider(`Provider ${provider.name} disabled`, { provider: provider.name }); + } }} - alt={`${provider.name} icon`} - className="w-6 h-6 dark:invert" /> - {provider.name}
- { - updateProviderSettings(provider.name, { ...provider.settings, enabled }); - if (enabled) { - logStore.logProvider(`Provider ${provider.name} enabled`, { provider: provider.name }); - } else { - logStore.logProvider(`Provider ${provider.name} disabled`, { provider: provider.name }); - } - }} - /> + {isUrlConfigurable && provider.settings.enabled && ( +
+ {envBaseUrl && ( + + )} + + { + let newBaseUrl: string | undefined = e.target.value; + + if (newBaseUrl && newBaseUrl.trim().length === 0) { + newBaseUrl = undefined; + } + + updateProviderSettings(provider.name, { ...provider.settings, baseUrl: newBaseUrl }); + logStore.logProvider(`Base URL updated for ${provider.name}`, { + provider: provider.name, + baseUrl: newBaseUrl, + }); + }} + placeholder={`Enter ${provider.name} base URL`} + className="w-full bg-white dark:bg-bolt-elements-background-depth-4 relative px-2 py-1.5 rounded-md focus:outline-none placeholder-bolt-elements-textTertiary text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary border border-bolt-elements-borderColor" + /> +
+ )}
- {URL_CONFIGURABLE_PROVIDERS.includes(provider.name) && provider.settings.enabled && ( -
- - { - const newBaseUrl = e.target.value; - updateProviderSettings(provider.name, { ...provider.settings, baseUrl: newBaseUrl }); - logStore.logProvider(`Base URL updated for ${provider.name}`, { - provider: provider.name, - baseUrl: newBaseUrl, - }); - }} - placeholder={`Enter ${provider.name} base URL`} - className="w-full bg-white dark:bg-bolt-elements-background-depth-4 relative px-2 py-1.5 rounded-md focus:outline-none placeholder-bolt-elements-textTertiary text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary border border-bolt-elements-borderColor" - /> -
- )} -
- ); + ); + }; + + const regularProviders = filteredProviders.filter(p => !URL_CONFIGURABLE_PROVIDERS.includes(p.name)); + const urlConfigurableProviders = filteredProviders.filter(p => URL_CONFIGURABLE_PROVIDERS.includes(p.name)); return (
@@ -118,77 +127,20 @@ export default function ProvidersTab() { />
- {filteredProviders.map((provider) => { - const envBaseUrlKey = providerBaseUrlEnvKeys[provider.name].baseUrlKey; - const envBaseUrl = envBaseUrlKey ? import.meta.env[envBaseUrlKey] : undefined; + {/* Regular Providers Grid */} +
+ {regularProviders.map(renderProviderCard)} +
- return ( -
-
-
- { - // Fallback to default icon on error - e.currentTarget.src = DefaultIcon; - }} - alt={`${provider.name} icon`} - className="w-6 h-6 dark:invert" - /> - {provider.name} -
- { - updateProviderSettings(provider.name, { ...provider.settings, enabled }); - - if (enabled) { - logStore.logProvider(`Provider ${provider.name} enabled`, { provider: provider.name }); - } else { - logStore.logProvider(`Provider ${provider.name} disabled`, { provider: provider.name }); - } - }} - /> -
- {/* Base URL input for configurable providers */} - {URL_CONFIGURABLE_PROVIDERS.includes(provider.name) && provider.settings.enabled && ( -
- {envBaseUrl && ( - - )} - - { - let newBaseUrl: string | undefined = e.target.value; - - if (newBaseUrl && newBaseUrl.trim().length === 0) { - newBaseUrl = undefined; - } - - updateProviderSettings(provider.name, { ...provider.settings, baseUrl: newBaseUrl }); - logStore.logProvider(`Base URL updated for ${provider.name}`, { - provider: provider.name, - baseUrl: newBaseUrl, - }); - }} - placeholder={`Enter ${provider.name} base URL`} - className="w-full bg-white dark:bg-bolt-elements-background-depth-4 relative px-2 py-1.5 rounded-md focus:outline-none placeholder-bolt-elements-textTertiary text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary border border-bolt-elements-borderColor" - /> -
- )} + {/* URL Configurable Providers Section */} + {urlConfigurableProviders.length > 0 && ( +
+

Providers with Custom Base URL

+
+ {urlConfigurableProviders.map(renderProviderCard)}
- ); - })} +
+ )}
); -} +} \ No newline at end of file From bbd9a17d3b1bb4010b75d88162aa03eedb5646f5 Mon Sep 17 00:00:00 2001 From: Dustin Loring Date: Thu, 19 Dec 2024 13:04:26 -0500 Subject: [PATCH 21/24] Update ProvidersTab.tsx --- app/components/settings/providers/ProvidersTab.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/components/settings/providers/ProvidersTab.tsx b/app/components/settings/providers/ProvidersTab.tsx index c9dc27c..58c8dac 100644 --- a/app/components/settings/providers/ProvidersTab.tsx +++ b/app/components/settings/providers/ProvidersTab.tsx @@ -135,7 +135,10 @@ export default function ProvidersTab() { {/* URL Configurable Providers Section */} {urlConfigurableProviders.length > 0 && (
-

Providers with Custom Base URL

+

Experimental Providers

+

+ These providers are experimental and allow you to run AI models locally or connect to your own infrastructure. They require additional setup but offer more flexibility. +

{urlConfigurableProviders.map(renderProviderCard)}
From 01599caf38ad960d80ad10d45ec39fc2f0a3e56b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 19 Dec 2024 18:08:12 +0000 Subject: [PATCH 22/24] chore: update commit hash to 75ec49beb8cb40561a493f0822455b8339b200f9 --- app/commit.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/commit.json b/app/commit.json index f2fdb14..74c23b6 100644 --- a/app/commit.json +++ b/app/commit.json @@ -1 +1 @@ -{ "commit": "a53b10ff399c591e898182e4b3934c26db19b6d6", "version": "0.0.3" } +{ "commit": "75ec49beb8cb40561a493f0822455b8339b200f9", "version": "0.0.3" } From 0733813af8ba0129ee22e4b656fb61b2e1edf452 Mon Sep 17 00:00:00 2001 From: Dustin Loring Date: Thu, 19 Dec 2024 15:12:21 -0500 Subject: [PATCH 23/24] docs: updated style in faq updated style in FAQ docs to be an accordion like style added a TOC to the index page in the docs --- docs/docs/FAQ.md | 57 +++++++++++++++++++--------------------------- docs/docs/index.md | 15 ++++++++++++ 2 files changed, 39 insertions(+), 33 deletions(-) diff --git a/docs/docs/FAQ.md b/docs/docs/FAQ.md index 1b645d3..a09fae8 100644 --- a/docs/docs/FAQ.md +++ b/docs/docs/FAQ.md @@ -1,6 +1,7 @@ # Frequently Asked Questions (FAQ) -## What are the best models for bolt.diy? +
+What are the best models for bolt.diy? For the best experience with bolt.diy, we recommend using the following models: @@ -11,10 +12,10 @@ For the best experience with bolt.diy, we recommend using the following models: - **Qwen 2.5 Coder 32b**: Best model for self-hosting with reasonable hardware requirements **Note**: Models with less than 7b parameters typically lack the capability to properly interact with bolt! +
---- - -## How do I get the best results with bolt.diy? +
+How do I get the best results with bolt.diy? - **Be specific about your stack**: Mention the frameworks or libraries you want to use (e.g., Astro, Tailwind, ShadCN) in your initial prompt. This ensures that bolt.diy scaffolds the project according to your preferences. @@ -28,72 +29,62 @@ For the best experience with bolt.diy, we recommend using the following models: - **Batch simple instructions**: Combine simple tasks into a single prompt to save time and reduce API credit consumption. For example: *"Change the color scheme, add mobile responsiveness, and restart the dev server."* +
---- - -## How do I contribute to bolt.diy? +
+How do I contribute to bolt.diy? Check out our [Contribution Guide](CONTRIBUTING.md) for more details on how to get involved! +
---- - -## What are the future plans for bolt.diy? +
+What are the future plans for bolt.diy? Visit our [Roadmap](https://roadmap.sh/r/ottodev-roadmap-2ovzo) for the latest updates. New features and improvements are on the way! +
---- - -## Why are there so many open issues/pull requests? +
+Why are there so many open issues/pull requests? bolt.diy began as a small showcase project on @ColeMedin's YouTube channel to explore editing open-source projects with local LLMs. However, it quickly grew into a massive community effort! -We’re forming a team of maintainers to manage demand and streamline issue resolution. The maintainers are rockstars, and we’re also exploring partnerships to help the project thrive. +We're forming a team of maintainers to manage demand and streamline issue resolution. The maintainers are rockstars, and we're also exploring partnerships to help the project thrive. +
---- - -## How do local LLMs compare to larger models like Claude 3.5 Sonnet for bolt.diy? +
+How do local LLMs compare to larger models like Claude 3.5 Sonnet for bolt.diy? While local LLMs are improving rapidly, larger models like GPT-4o, Claude 3.5 Sonnet, and DeepSeek Coder V2 236b still offer the best results for complex applications. Our ongoing focus is to improve prompts, agents, and the platform to better support smaller local LLMs. +
---- - -## Common Errors and Troubleshooting +
+Common Errors and Troubleshooting ### **"There was an error processing this request"** This generic error message means something went wrong. Check both: - The terminal (if you started the app with Docker or `pnpm`). - The developer console in your browser (press `F12` or right-click > *Inspect*, then go to the *Console* tab). ---- - ### **"x-api-key header missing"** This error is sometimes resolved by restarting the Docker container. -If that doesn’t work, try switching from Docker to `pnpm` or vice versa. We’re actively investigating this issue. - ---- +If that doesn't work, try switching from Docker to `pnpm` or vice versa. We're actively investigating this issue. ### **Blank preview when running the app** A blank preview often occurs due to hallucinated bad code or incorrect commands. To troubleshoot: - Check the developer console for errors. -- Remember, previews are core functionality, so the app isn’t broken! We’re working on making these errors more transparent. - ---- +- Remember, previews are core functionality, so the app isn't broken! We're working on making these errors more transparent. ### **"Everything works, but the results are bad"** Local LLMs like Qwen-2.5-Coder are powerful for small applications but still experimental for larger projects. For better results, consider using larger models like GPT-4o, Claude 3.5 Sonnet, or DeepSeek Coder V2 236b. ---- - ### **"Received structured exception #0xc0000005: access violation"** - If you are getting this, you are probably on Windows. The fix is generally to update the [Visual C++ Redistributable](https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist?view=msvc-170) ---- - ### **"Miniflare or Wrangler errors in Windows"** You will need to make sure you have the latest version of Visual Studio C++ installed (14.40.33816), more information here https://github.com/stackblitz-labs/bolt.diy/issues/19. +
--- diff --git a/docs/docs/index.md b/docs/docs/index.md index 389e74f..ef08de6 100644 --- a/docs/docs/index.md +++ b/docs/docs/index.md @@ -1,6 +1,21 @@ # Welcome to bolt diy bolt.diy 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, or Groq models - 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. +## Table of Contents +- [Join the community!](#join-the-community) +- [What's bolt.diy](#whats-boltdiy) +- [What Makes bolt.diy Different](#what-makes-boltdiy-different) +- [Setup](#setup) +- [Run with Docker](#run-with-docker) + - [Using Helper Scripts](#1a-using-helper-scripts) + - [Direct Docker Build Commands](#1b-direct-docker-build-commands-alternative-to-using-npm-scripts) + - [Docker Compose with Profiles](#2-docker-compose-with-profiles-to-run-the-container) +- [Run Without Docker](#run-without-docker) +- [Adding New LLMs](#adding-new-llms) +- [Available Scripts](#available-scripts) +- [Development](#development) +- [Tips and Tricks](#tips-and-tricks) + --- ## Join the community! From 2638c1a704118b411f942e1b17b6765abce46721 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 19 Dec 2024 20:17:35 +0000 Subject: [PATCH 24/24] chore: update commit hash to 636f87f568a368dadc5cf3c077284710951e2488 --- app/commit.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/commit.json b/app/commit.json index 74c23b6..0b3f44a 100644 --- a/app/commit.json +++ b/app/commit.json @@ -1 +1 @@ -{ "commit": "75ec49beb8cb40561a493f0822455b8339b200f9", "version": "0.0.3" } +{ "commit": "636f87f568a368dadc5cf3c077284710951e2488", "version": "0.0.3" }