From 25f725f01e329b0508e813d8ad8f7daa2e588159 Mon Sep 17 00:00:00 2001 From: Anirban Kar Date: Tue, 10 Dec 2024 18:37:05 +0530 Subject: [PATCH 01/14] refactor: settinge menu refactored with useSettings hook --- .../settings/chat-history/ChatHistoryTab.tsx | 105 ++++++++++++++++++ .../settings/connections/ConnectionsTab.tsx | 48 ++++++++ app/components/settings/debug/DebugTab.tsx | 69 ++++++++++++ .../settings/features/FeaturesTab.tsx | 29 +++++ .../settings/providers/ProvidersTab.tsx | 77 +++++++++++++ app/lib/hooks/useSettings.tsx | 103 +++++++++++++++++ 6 files changed, 431 insertions(+) create mode 100644 app/components/settings/chat-history/ChatHistoryTab.tsx create mode 100644 app/components/settings/connections/ConnectionsTab.tsx create mode 100644 app/components/settings/debug/DebugTab.tsx create mode 100644 app/components/settings/features/FeaturesTab.tsx create mode 100644 app/components/settings/providers/ProvidersTab.tsx create mode 100644 app/lib/hooks/useSettings.tsx diff --git a/app/components/settings/chat-history/ChatHistoryTab.tsx b/app/components/settings/chat-history/ChatHistoryTab.tsx new file mode 100644 index 000000000..e96f0d8ef --- /dev/null +++ b/app/components/settings/chat-history/ChatHistoryTab.tsx @@ -0,0 +1,105 @@ +import { useNavigate } from '@remix-run/react'; +import React, { useState } from 'react'; +import { toast } from 'react-toastify'; +import { db, deleteById, getAll } from '~/lib/persistence'; +import { classNames } from '~/utils/classNames'; +import styles from '~/components/settings/Settings.module.scss'; + +export default function ChatHistoryTab() { + const navigate = useNavigate(); + const [isDeleting, setIsDeleting] = useState(false); + const downloadAsJson = (data: any, filename: string) => { + const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' }); + const url = URL.createObjectURL(blob); + const link = document.createElement('a'); + link.href = url; + link.download = filename; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + URL.revokeObjectURL(url); + }; + + const handleDeleteAllChats = async () => { + if (!db) { + toast.error('Database is not available'); + return; + } + + try { + setIsDeleting(true); + + const allChats = await getAll(db); + + // Delete all chats one by one + await Promise.all(allChats.map((chat) => deleteById(db!, chat.id))); + + toast.success('All chats deleted successfully'); + navigate('/', { replace: true }); + } catch (error) { + toast.error('Failed to delete chats'); + console.error(error); + } finally { + setIsDeleting(false); + } + }; + + const handleExportAllChats = async () => { + if (!db) { + toast.error('Database is not available'); + return; + } + + try { + const allChats = await getAll(db); + const exportData = { + chats: allChats, + exportDate: new Date().toISOString(), + }; + + downloadAsJson(exportData, `all-chats-${new Date().toISOString()}.json`); + toast.success('Chats exported successfully'); + } catch (error) { + toast.error('Failed to export chats'); + console.error(error); + } + }; + + return ( + <> +
+

Chat History

+ + +
+

Danger Area

+

This action cannot be undone!

+ +
+
+ + ); +} diff --git a/app/components/settings/connections/ConnectionsTab.tsx b/app/components/settings/connections/ConnectionsTab.tsx new file mode 100644 index 000000000..32d0fa09b --- /dev/null +++ b/app/components/settings/connections/ConnectionsTab.tsx @@ -0,0 +1,48 @@ +import React, { useState } from 'react'; +import { toast } from 'react-toastify'; +import Cookies from 'js-cookie'; + +export default function ConnectionsTab() { + const [githubUsername, setGithubUsername] = useState(Cookies.get('githubUsername') || ''); + const [githubToken, setGithubToken] = useState(Cookies.get('githubToken') || ''); + + const handleSaveConnection = () => { + Cookies.set('githubUsername', githubUsername); + Cookies.set('githubToken', githubToken); + toast.success('GitHub credentials saved successfully!'); + }; + + return ( +
+

GitHub Connection

+
+
+ + setGithubUsername(e.target.value)} + 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" + /> +
+
+ + setGithubToken(e.target.value)} + 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/components/settings/debug/DebugTab.tsx b/app/components/settings/debug/DebugTab.tsx new file mode 100644 index 000000000..7a84ec16c --- /dev/null +++ b/app/components/settings/debug/DebugTab.tsx @@ -0,0 +1,69 @@ +import React, { useCallback, useEffect, useState } from 'react'; +import { useSettings } from '~/lib/hooks/useSettings'; +import commit from '~/commit.json'; + +const versionHash = commit.commit; // Get the version hash from commit.json + +export default function DebugTab() { + const { providers } = useSettings(); + const [activeProviders, setActiveProviders] = useState([]); + useEffect(() => { + setActiveProviders( + Object.entries(providers) + .filter(([_key, provider]) => provider.settings.enabled) + .map(([_key, provider]) => provider.name), + ); + }, [providers]); + + const handleCopyToClipboard = useCallback(() => { + const debugInfo = { + OS: navigator.platform, + Browser: navigator.userAgent, + ActiveFeatures: activeProviders, + BaseURLs: { + Ollama: process.env.REACT_APP_OLLAMA_URL, + OpenAI: process.env.REACT_APP_OPENAI_URL, + LMStudio: process.env.REACT_APP_LM_STUDIO_URL, + }, + Version: versionHash, + }; + navigator.clipboard.writeText(JSON.stringify(debugInfo, null, 2)).then(() => { + alert('Debug information copied to clipboard!'); + }); + }, [providers]); + + return ( +
+

Debug Tab

+ + +

System Information

+

OS: {navigator.platform}

+

Browser: {navigator.userAgent}

+ +

Active Features

+
    + {activeProviders.map((name) => ( +
  • + {name} +
  • + ))} +
+ +

Base URLs

+
    +
  • Ollama: {process.env.REACT_APP_OLLAMA_URL}
  • +
  • OpenAI: {process.env.REACT_APP_OPENAI_URL}
  • +
  • LM Studio: {process.env.REACT_APP_LM_STUDIO_URL}
  • +
+ +

Version Information

+

Version Hash: {versionHash}

+
+ ); +} diff --git a/app/components/settings/features/FeaturesTab.tsx b/app/components/settings/features/FeaturesTab.tsx new file mode 100644 index 000000000..0b4fa755a --- /dev/null +++ b/app/components/settings/features/FeaturesTab.tsx @@ -0,0 +1,29 @@ +import React from 'react'; +import { Switch } from '~/components/ui/Switch'; +import { useSettings } from '~/lib/hooks/useSettings'; + +export default function FeaturesTab() { + const { debug, enableDebugMode, isLocalModel, enableLocalModels } = useSettings(); + return ( +
+
+

Optional Features

+
+ Debug Info + +
+
+ +
+

Experimental Features

+

+ Disclaimer: Experimental features may be unstable and are subject to change. +

+
+ Enable Local Models + +
+
+
+ ); +} diff --git a/app/components/settings/providers/ProvidersTab.tsx b/app/components/settings/providers/ProvidersTab.tsx new file mode 100644 index 000000000..0b87959a7 --- /dev/null +++ b/app/components/settings/providers/ProvidersTab.tsx @@ -0,0 +1,77 @@ +import React, { useEffect, useState } from 'react'; +import { Switch } from '~/components/ui/Switch'; +import { useSettings } from '~/lib/hooks/useSettings'; +import { LOCAL_PROVIDERS, URL_CONFIGURABLE_PROVIDERS, type IProviderConfig } from '~/lib/stores/settings'; + +export default function ProvidersTab() { + const { providers, updateProviderSettings, isLocalModel } = useSettings(); + const [filteredProviders, setFilteredProviders] = useState([]); + + // Load base URLs from cookies + const [searchTerm, setSearchTerm] = useState(''); + + useEffect(() => { + let newFilteredProviders: IProviderConfig[] = Object.entries(providers).map(([key, value]) => ({ + ...value, + name: key, + })); + + if (searchTerm && searchTerm.length > 0) { + newFilteredProviders = newFilteredProviders.filter((provider) => + provider.name.toLowerCase().includes(searchTerm.toLowerCase()), + ); + } + + if (!isLocalModel) { + newFilteredProviders = newFilteredProviders.filter((provider) => !LOCAL_PROVIDERS.includes(provider.name)); + } + + newFilteredProviders.sort((a, b) => a.name.localeCompare(b.name)); + + setFilteredProviders(newFilteredProviders); + }, [providers, searchTerm, isLocalModel]); + + return ( +
+
+ setSearchTerm(e.target.value)} + 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) => ( +
+
+ {provider.name} + updateProviderSettings(provider.name, { ...provider.settings, enabled })} + /> +
+ {/* Base URL input for configurable providers */} + {URL_CONFIGURABLE_PROVIDERS.includes(provider.name) && provider.settings.enabled && ( +
+ + + updateProviderSettings(provider.name, { ...provider.settings, baseUrl: e.target.value }) + } + 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/hooks/useSettings.tsx b/app/lib/hooks/useSettings.tsx new file mode 100644 index 000000000..9b63430f4 --- /dev/null +++ b/app/lib/hooks/useSettings.tsx @@ -0,0 +1,103 @@ +import { useStore } from '@nanostores/react'; +import { + isDebugMode, + isLocalModelsEnabled, + LOCAL_PROVIDERS, + providersStore, + type IProviderSetting, +} from '~/lib/stores/settings'; +import { useCallback, useEffect, useState } from 'react'; +import Cookies from 'js-cookie'; +import type { ProviderInfo } from '~/utils/types'; + +export function useSettings() { + const providers = useStore(providersStore); + const debug = useStore(isDebugMode); + const isLocalModel = useStore(isLocalModelsEnabled); + const [activeProviders, setActiveProviders] = useState([]); + + // reading values from cookies on mount + useEffect(() => { + const savedProviders = Cookies.get('providers'); + + if (savedProviders) { + try { + const parsedProviders: Record = JSON.parse(savedProviders); + Object.keys(parsedProviders).forEach((provider) => { + const currentProvider = providers[provider]; + providersStore.setKey(provider, { + ...currentProvider, + settings: { + ...parsedProviders[provider], + enabled: parsedProviders[provider].enabled || true, + }, + }); + }); + } catch (error) { + console.error('Failed to parse providers from cookies:', error); + } + } + + // load debug mode from cookies + const savedDebugMode = Cookies.get('isDebugEnabled'); + + if (savedDebugMode) { + isDebugMode.set(savedDebugMode === 'true'); + } + + // load local models from cookies + const savedLocalModels = Cookies.get('isLocalModelsEnabled'); + + if (savedLocalModels) { + isLocalModelsEnabled.set(savedLocalModels === 'true'); + } + }, []); + + // writing values to cookies on change + useEffect(() => { + const providers = providersStore.get(); + const providerSetting: Record = {}; + Object.keys(providers).forEach((provider) => { + providerSetting[provider] = providers[provider].settings; + }); + Cookies.set('providers', JSON.stringify(providerSetting)); + }, [providers]); + + useEffect(() => { + let active = Object.entries(providers) + .filter(([_key, provider]) => provider.settings.enabled) + .map(([_k, p]) => p); + + if (!isLocalModel) { + active = active.filter((p) => !LOCAL_PROVIDERS.includes(p.name)); + } + + setActiveProviders(active); + }, [providers, isLocalModel]); + + // helper function to update settings + const updateProviderSettings = useCallback((provider: string, config: IProviderSetting) => { + const settings = providers[provider].settings; + providersStore.setKey(provider, { ...providers[provider], settings: { ...settings, ...config } }); + }, []); + + const enableDebugMode = useCallback((enabled: boolean) => { + isDebugMode.set(enabled); + Cookies.set('isDebugEnabled', String(enabled)); + }, []); + + const enableLocalModels = useCallback((enabled: boolean) => { + isLocalModelsEnabled.set(enabled); + Cookies.set('isLocalModelsEnabled', String(enabled)); + }, []); + + return { + providers, + activeProviders, + updateProviderSettings, + debug, + enableDebugMode, + isLocalModel, + enableLocalModels, + }; +} From b4d05971209e5d91013f665990e8399c331d0c32 Mon Sep 17 00:00:00 2001 From: Anirban Kar Date: Tue, 10 Dec 2024 18:37:23 +0530 Subject: [PATCH 02/14] remaining changes --- app/components/chat/BaseChat.tsx | 48 +-- app/components/chat/Chat.client.tsx | 3 + app/components/settings/SettingsWindow.tsx | 411 ++------------------- app/lib/stores/settings.ts | 57 ++- 4 files changed, 57 insertions(+), 462 deletions(-) diff --git a/app/components/chat/BaseChat.tsx b/app/components/chat/BaseChat.tsx index 411da94b8..57a153d3c 100644 --- a/app/components/chat/BaseChat.tsx +++ b/app/components/chat/BaseChat.tsx @@ -45,6 +45,7 @@ interface BaseChatProps { setModel?: (model: string) => void; provider?: ProviderInfo; setProvider?: (provider: ProviderInfo) => void; + providerList?: ProviderInfo[]; handleStop?: () => void; sendMessage?: (event: React.UIEvent, messageInput?: string) => void; handleInputChange?: (event: React.ChangeEvent) => void; @@ -70,6 +71,7 @@ export const BaseChat = React.forwardRef( setModel, provider, setProvider, + providerList, input = '', enhancingPrompt, handleInputChange, @@ -108,45 +110,7 @@ export const BaseChat = React.forwardRef( const [recognition, setRecognition] = useState(null); const [transcript, setTranscript] = useState(''); - // Load enabled providers from cookies - const [enabledProviders, setEnabledProviders] = useState(() => { - const savedProviders = Cookies.get('providers'); - - if (savedProviders) { - try { - const parsedProviders = JSON.parse(savedProviders); - return PROVIDER_LIST.filter((p) => parsedProviders[p.name]); - } catch (error) { - console.error('Failed to parse providers from cookies:', error); - return PROVIDER_LIST; - } - } - - return PROVIDER_LIST; - }); - // Update enabled providers when cookies change - useEffect(() => { - const updateProvidersFromCookies = () => { - const savedProviders = Cookies.get('providers'); - - if (savedProviders) { - try { - const parsedProviders = JSON.parse(savedProviders); - setEnabledProviders(PROVIDER_LIST.filter((p) => parsedProviders[p.name])); - } catch (error) { - console.error('Failed to parse providers from cookies:', error); - } - } - }; - - updateProvidersFromCookies(); - - const interval = setInterval(updateProvidersFromCookies, 1000); - - return () => clearInterval(interval); - }, [PROVIDER_LIST]); - console.log(transcript); useEffect(() => { // Load API keys from cookies on component mount @@ -377,10 +341,10 @@ export const BaseChat = React.forwardRef( modelList={modelList} provider={provider} setProvider={setProvider} - providerList={PROVIDER_LIST} + providerList={providerList || PROVIDER_LIST} apiKeys={apiKeys} /> - {enabledProviders.length > 0 && provider && ( + {(providerList || []).length > 0 && provider && ( ( 0 || isStreaming || uploadedFiles.length > 0} isStreaming={isStreaming} - disabled={enabledProviders.length === 0} + disabled={!providerList || providerList.length === 0} onClick={(event) => { if (isStreaming) { handleStop?.(); @@ -536,7 +500,7 @@ export const BaseChat = React.forwardRef( !isModelSettingsCollapsed, })} onClick={() => setIsModelSettingsCollapsed(!isModelSettingsCollapsed)} - disabled={enabledProviders.length === 0} + disabled={!providerList || providerList.length === 0} >
{isModelSettingsCollapsed ? {model} : } diff --git a/app/components/chat/Chat.client.tsx b/app/components/chat/Chat.client.tsx index c6a01eef4..7c67a750a 100644 --- a/app/components/chat/Chat.client.tsx +++ b/app/components/chat/Chat.client.tsx @@ -19,6 +19,7 @@ import { BaseChat } from './BaseChat'; import Cookies from 'js-cookie'; import type { ProviderInfo } from '~/utils/types'; import { debounce } from '~/utils/debounce'; +import { useSettings } from '~/lib/hooks/useSettings'; const toastAnimation = cssTransition({ enter: 'animated fadeInRight', @@ -91,6 +92,7 @@ export const ChatImpl = memo( const [chatStarted, setChatStarted] = useState(initialMessages.length > 0); const [uploadedFiles, setUploadedFiles] = useState([]); // Move here const [imageDataList, setImageDataList] = useState([]); // Move here + const { activeProviders } = useSettings(); const [model, setModel] = useState(() => { const savedModel = Cookies.get('selectedModel'); @@ -316,6 +318,7 @@ export const ChatImpl = memo( setModel={handleModelChange} provider={provider} setProvider={handleProviderChange} + providerList={activeProviders} messageRef={messageRef} scrollRef={scrollRef} handleInputChange={(e) => { diff --git a/app/components/settings/SettingsWindow.tsx b/app/components/settings/SettingsWindow.tsx index 0910859d6..8ae4a4842 100644 --- a/app/components/settings/SettingsWindow.tsx +++ b/app/components/settings/SettingsWindow.tsx @@ -1,17 +1,16 @@ import * as RadixDialog from '@radix-ui/react-dialog'; import { motion } from 'framer-motion'; -import { useState } from 'react'; +import { useState, type ReactElement } from 'react'; import { classNames } from '~/utils/classNames'; import { DialogTitle, dialogVariants, dialogBackdropVariants } from '~/components/ui/Dialog'; import { IconButton } from '~/components/ui/IconButton'; -import { providersList } from '~/lib/stores/settings'; -import { db, getAll, deleteById } from '~/lib/persistence'; -import { toast } from 'react-toastify'; -import { useNavigate } from '@remix-run/react'; -import commit from '~/commit.json'; -import Cookies from 'js-cookie'; import styles from './Settings.module.scss'; -import { Switch } from '~/components/ui/Switch'; +import ChatHistoryTab from './chat-history/ChatHistoryTab'; +import ProvidersTab from './providers/ProvidersTab'; +import { useSettings } from '~/lib/hooks/useSettings'; +import FeaturesTab from './features/FeaturesTab'; +import DebugTab from './debug/DebugTab'; +import ConnectionsTab from './connections/ConnectionsTab'; interface SettingsProps { open: boolean; @@ -21,206 +20,27 @@ interface SettingsProps { type TabType = 'chat-history' | 'providers' | 'features' | 'debug' | 'connection'; // Providers that support base URL configuration -const URL_CONFIGURABLE_PROVIDERS = ['Ollama', 'LMStudio', 'OpenAILike']; - export const SettingsWindow = ({ open, onClose }: SettingsProps) => { - const navigate = useNavigate(); + const { debug } = useSettings(); const [activeTab, setActiveTab] = useState('chat-history'); - const [isDebugEnabled, setIsDebugEnabled] = useState(() => { - const savedDebugState = Cookies.get('isDebugEnabled'); - return savedDebugState === 'true'; - }); - const [searchTerm, setSearchTerm] = useState(''); - const [isDeleting, setIsDeleting] = useState(false); - const [githubUsername, setGithubUsername] = useState(Cookies.get('githubUsername') || ''); - const [githubToken, setGithubToken] = useState(Cookies.get('githubToken') || ''); - const [isLocalModelsEnabled, setIsLocalModelsEnabled] = useState(() => { - const savedLocalModelsState = Cookies.get('isLocalModelsEnabled'); - return savedLocalModelsState === 'true'; - }); - - // Load base URLs from cookies - const [baseUrls, setBaseUrls] = useState(() => { - const savedUrls = Cookies.get('providerBaseUrls'); - - if (savedUrls) { - try { - return JSON.parse(savedUrls); - } catch (error) { - console.error('Failed to parse base URLs from cookies:', error); - return { - Ollama: 'http://localhost:11434', - LMStudio: 'http://localhost:1234', - OpenAILike: '', - }; - } - } - - return { - Ollama: 'http://localhost:11434', - LMStudio: 'http://localhost:1234', - OpenAILike: '', - }; - }); - - const handleBaseUrlChange = (provider: string, url: string) => { - setBaseUrls((prev: Record) => { - const newUrls = { ...prev, [provider]: url }; - Cookies.set('providerBaseUrls', JSON.stringify(newUrls)); - return newUrls; - }); - }; - - const tabs: { id: TabType; label: string; icon: string }[] = [ - { id: 'chat-history', label: 'Chat History', icon: 'i-ph:book' }, - { id: 'providers', label: 'Providers', icon: 'i-ph:key' }, - { id: 'features', label: 'Features', icon: 'i-ph:star' }, - { id: 'connection', label: 'Connection', icon: 'i-ph:link' }, - ...(isDebugEnabled ? [{ id: 'debug' as TabType, label: 'Debug Tab', icon: 'i-ph:bug' }] : []), + const tabs: { id: TabType; label: string; icon: string; component?: ReactElement }[] = [ + { id: 'chat-history', label: 'Chat History', icon: 'i-ph:book', component: }, + { id: 'providers', label: 'Providers', icon: 'i-ph:key', component: }, + { id: 'features', label: 'Features', icon: 'i-ph:star', component: }, + { id: 'connection', label: 'Connection', icon: 'i-ph:link', component: }, + ...(debug + ? [ + { + id: 'debug' as TabType, + label: 'Debug Tab', + icon: 'i-ph:bug', + component: , + }, + ] + : []), ]; - // Load providers from cookies on mount - const [providers, setProviders] = useState(() => { - const savedProviders = Cookies.get('providers'); - - if (savedProviders) { - try { - const parsedProviders = JSON.parse(savedProviders); - - // Merge saved enabled states with the base provider list - return providersList.map((provider) => ({ - ...provider, - isEnabled: parsedProviders[provider.name] || false, - })); - } catch (error) { - console.error('Failed to parse providers from cookies:', error); - } - } - - return providersList; - }); - - const handleToggleProvider = (providerName: string, enabled: boolean) => { - setProviders((prevProviders) => { - const newProviders = prevProviders.map((provider) => - provider.name === providerName ? { ...provider, isEnabled: enabled } : provider, - ); - - // Save to cookies - const enabledStates = newProviders.reduce( - (acc, provider) => ({ - ...acc, - [provider.name]: provider.isEnabled, - }), - {}, - ); - Cookies.set('providers', JSON.stringify(enabledStates)); - - return newProviders; - }); - }; - - const filteredProviders = providers - .filter((provider) => { - const isLocalModelProvider = ['OpenAILike', 'LMStudio', 'Ollama'].includes(provider.name); - return isLocalModelsEnabled || !isLocalModelProvider; - }) - .filter((provider) => provider.name.toLowerCase().includes(searchTerm.toLowerCase())) - .sort((a, b) => a.name.localeCompare(b.name)); - - const handleCopyToClipboard = () => { - const debugInfo = { - OS: navigator.platform, - Browser: navigator.userAgent, - ActiveFeatures: providers.filter((provider) => provider.isEnabled).map((provider) => provider.name), - BaseURLs: { - Ollama: process.env.REACT_APP_OLLAMA_URL, - OpenAI: process.env.REACT_APP_OPENAI_URL, - LMStudio: process.env.REACT_APP_LM_STUDIO_URL, - }, - Version: versionHash, - }; - navigator.clipboard.writeText(JSON.stringify(debugInfo, null, 2)).then(() => { - alert('Debug information copied to clipboard!'); - }); - }; - - const downloadAsJson = (data: any, filename: string) => { - const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' }); - const url = URL.createObjectURL(blob); - const link = document.createElement('a'); - link.href = url; - link.download = filename; - document.body.appendChild(link); - link.click(); - document.body.removeChild(link); - URL.revokeObjectURL(url); - }; - - const handleDeleteAllChats = async () => { - if (!db) { - toast.error('Database is not available'); - return; - } - - try { - setIsDeleting(true); - - const allChats = await getAll(db); - - // Delete all chats one by one - await Promise.all(allChats.map((chat) => deleteById(db!, chat.id))); - - toast.success('All chats deleted successfully'); - navigate('/', { replace: true }); - } catch (error) { - toast.error('Failed to delete chats'); - console.error(error); - } finally { - setIsDeleting(false); - } - }; - - const handleExportAllChats = async () => { - if (!db) { - toast.error('Database is not available'); - return; - } - - try { - const allChats = await getAll(db); - const exportData = { - chats: allChats, - exportDate: new Date().toISOString(), - }; - - downloadAsJson(exportData, `all-chats-${new Date().toISOString()}.json`); - toast.success('Chats exported successfully'); - } catch (error) { - toast.error('Failed to export chats'); - console.error(error); - } - }; - - const versionHash = commit.commit; // Get the version hash from commit.json - - const handleSaveConnection = () => { - Cookies.set('githubUsername', githubUsername); - Cookies.set('githubToken', githubToken); - toast.success('GitHub credentials saved successfully!'); - }; - - const handleToggleDebug = (enabled: boolean) => { - setIsDebugEnabled(enabled); - Cookies.set('isDebugEnabled', String(enabled)); - }; - - const handleToggleLocalModels = (enabled: boolean) => { - setIsLocalModelsEnabled(enabled); - Cookies.set('isLocalModelsEnabled', String(enabled)); - }; - return ( @@ -284,190 +104,7 @@ export const SettingsWindow = ({ open, onClose }: SettingsProps) => {
-
- {activeTab === 'chat-history' && ( -
-

Chat History

- - -
-

Danger Area

-

This action cannot be undone!

- -
-
- )} - {activeTab === 'providers' && ( -
-
- setSearchTerm(e.target.value)} - 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) => ( -
-
- {provider.name} - handleToggleProvider(provider.name, enabled)} - /> -
- {/* Base URL input for configurable providers */} - {URL_CONFIGURABLE_PROVIDERS.includes(provider.name) && provider.isEnabled && ( -
- - handleBaseUrlChange(provider.name, e.target.value)} - 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" - /> -
- )} -
- ))} -
- )} - {activeTab === 'features' && ( -
-
-

Optional Features

-
- Debug Info - -
-
- -
-

Experimental Features

-

- Disclaimer: Experimental features may be unstable and are subject to change. -

-
- Enable Local Models - -
-
-
- )} - {activeTab === 'debug' && isDebugEnabled && ( -
-

Debug Tab

- - -

System Information

-

OS: {navigator.platform}

-

Browser: {navigator.userAgent}

- -

Active Features

-
    - {providers - .filter((provider) => provider.isEnabled) - .map((provider) => ( -
  • - {provider.name} -
  • - ))} -
- -

Base URLs

-
    -
  • Ollama: {process.env.REACT_APP_OLLAMA_URL}
  • -
  • OpenAI: {process.env.REACT_APP_OPENAI_URL}
  • -
  • - LM Studio: {process.env.REACT_APP_LM_STUDIO_URL} -
  • -
- -

Version Information

-

Version Hash: {versionHash}

-
- )} - {activeTab === 'connection' && ( -
-

GitHub Connection

-
-
- - setGithubUsername(e.target.value)} - 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" - /> -
-
- - setGithubToken(e.target.value)} - 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" - /> -
-
-
- -
-
- )} -
+
{tabs.find((tab) => tab.id === activeTab)?.component}
diff --git a/app/lib/stores/settings.ts b/app/lib/stores/settings.ts index 7106cfb6b..b6dbc06a5 100644 --- a/app/lib/stores/settings.ts +++ b/app/lib/stores/settings.ts @@ -1,5 +1,7 @@ -import { map } from 'nanostores'; +import { atom, map } from 'nanostores'; import { workbenchStore } from './workbench'; +import type { ProviderInfo } from '~/utils/types'; +import { PROVIDER_LIST } from '~/utils/constants'; export interface Shortcut { key: string; @@ -15,32 +17,18 @@ export interface Shortcuts { toggleTerminal: Shortcut; } -export interface Provider { - name: string; - isEnabled: boolean; +export interface IProviderSetting { + enabled?: boolean; + baseUrl?: string; } +export type IProviderConfig = ProviderInfo & { + settings: IProviderSetting; +}; -export interface Settings { - shortcuts: Shortcuts; - providers: Provider[]; -} +export const URL_CONFIGURABLE_PROVIDERS = ['Ollama', 'LMStudio', 'OpenAILike']; +export const LOCAL_PROVIDERS = ['OpenAILike', 'LMStudio', 'Ollama']; -export const providersList: Provider[] = [ - { name: 'Groq', isEnabled: false }, - { name: 'HuggingFace', isEnabled: false }, - { name: 'OpenAI', isEnabled: false }, - { name: 'Anthropic', isEnabled: false }, - { name: 'OpenRouter', isEnabled: false }, - { name: 'Google', isEnabled: false }, - { name: 'Ollama', isEnabled: false }, - { name: 'OpenAILike', isEnabled: false }, - { name: 'Together', isEnabled: false }, - { name: 'Deepseek', isEnabled: false }, - { name: 'Mistral', isEnabled: false }, - { name: 'Cohere', isEnabled: false }, - { name: 'LMStudio', isEnabled: false }, - { name: 'xAI', isEnabled: false }, -]; +export type ProviderSetting = Record; export const shortcutsStore = map({ toggleTerminal: { @@ -50,14 +38,17 @@ export const shortcutsStore = map({ }, }); -export const settingsStore = map({ - shortcuts: shortcutsStore.get(), - providers: providersList, +const initialProviderSettings: ProviderSetting = {}; +PROVIDER_LIST.forEach((provider) => { + initialProviderSettings[provider.name] = { + ...provider, + settings: { + enabled: false, + }, + }; }); +export const providersStore = map(initialProviderSettings); -shortcutsStore.subscribe((shortcuts) => { - settingsStore.set({ - ...settingsStore.get(), - shortcuts, - }); -}); +export const isDebugMode = atom(false); + +export const isLocalModelsEnabled = atom(true); From 5d4b860c94aa2d20d544ad82388b09c180d82426 Mon Sep 17 00:00:00 2001 From: Anirban Kar Date: Wed, 11 Dec 2024 14:02:21 +0530 Subject: [PATCH 03/14] updated to adapth baseurl setup --- app/components/chat/BaseChat.tsx | 23 +++++++++- app/components/chat/Chat.client.tsx | 2 +- .../settings/providers/ProvidersTab.tsx | 3 +- app/lib/.server/llm/model.ts | 11 ++++- app/lib/.server/llm/stream-text.ts | 19 ++++---- app/lib/hooks/useSettings.tsx | 10 +---- app/lib/stores/settings.ts | 10 +---- app/routes/api.chat.ts | 8 +++- app/routes/api.enhancer.ts | 42 +++++++++++++++--- app/types/model.ts | 12 ++++- app/utils/constants.ts | 44 ++++++++++--------- app/utils/types.ts | 9 ---- 12 files changed, 122 insertions(+), 71 deletions(-) diff --git a/app/components/chat/BaseChat.tsx b/app/components/chat/BaseChat.tsx index 57a153d3c..b17baf958 100644 --- a/app/components/chat/BaseChat.tsx +++ b/app/components/chat/BaseChat.tsx @@ -17,7 +17,6 @@ import Cookies from 'js-cookie'; import * as Tooltip from '@radix-ui/react-tooltip'; import styles from './BaseChat.module.scss'; -import type { ProviderInfo } from '~/utils/types'; import { ExportChatButton } from '~/components/chat/chatExportAndImport/ExportChatButton'; import { ImportButtons } from '~/components/chat/chatExportAndImport/ImportButtons'; import { ExamplePrompts } from '~/components/chat/ExamplePrompts'; @@ -26,6 +25,7 @@ import GitCloneButton from './GitCloneButton'; import FilePreview from './FilePreview'; import { ModelSelector } from '~/components/chat/ModelSelector'; import { SpeechRecognitionButton } from '~/components/chat/SpeechRecognition'; +import type { IProviderSetting, ProviderInfo } from '~/types/model'; const TEXTAREA_MIN_HEIGHT = 76; @@ -131,7 +131,26 @@ export const BaseChat = React.forwardRef( Cookies.remove('apiKeys'); } - initializeModelList().then((modelList) => { + let providerSettings: Record | undefined = undefined; + + try { + const savedProviderSettings = Cookies.get('providers'); + + if (savedProviderSettings) { + const parsedProviderSettings = JSON.parse(savedProviderSettings); + + if (typeof parsedProviderSettings === 'object' && parsedProviderSettings !== null) { + providerSettings = parsedProviderSettings; + } + } + } catch (error) { + console.error('Error loading Provider Settings from cookies:', error); + + // Clear invalid cookie data + Cookies.remove('providers'); + } + + initializeModelList(providerSettings).then((modelList) => { setModelList(modelList); }); diff --git a/app/components/chat/Chat.client.tsx b/app/components/chat/Chat.client.tsx index 7c67a750a..cd651cb12 100644 --- a/app/components/chat/Chat.client.tsx +++ b/app/components/chat/Chat.client.tsx @@ -17,9 +17,9 @@ import { cubicEasingFn } from '~/utils/easings'; import { createScopedLogger, renderLogger } from '~/utils/logger'; import { BaseChat } from './BaseChat'; import Cookies from 'js-cookie'; -import type { ProviderInfo } from '~/utils/types'; import { debounce } from '~/utils/debounce'; import { useSettings } from '~/lib/hooks/useSettings'; +import type { ProviderInfo } from '~/types/model'; const toastAnimation = cssTransition({ enter: 'animated fadeInRight', diff --git a/app/components/settings/providers/ProvidersTab.tsx b/app/components/settings/providers/ProvidersTab.tsx index 0b87959a7..309afb861 100644 --- a/app/components/settings/providers/ProvidersTab.tsx +++ b/app/components/settings/providers/ProvidersTab.tsx @@ -1,7 +1,8 @@ import React, { useEffect, useState } from 'react'; import { Switch } from '~/components/ui/Switch'; import { useSettings } from '~/lib/hooks/useSettings'; -import { LOCAL_PROVIDERS, URL_CONFIGURABLE_PROVIDERS, type IProviderConfig } from '~/lib/stores/settings'; +import { LOCAL_PROVIDERS, URL_CONFIGURABLE_PROVIDERS } from '~/lib/stores/settings'; +import type { IProviderConfig } from '~/types/model'; export default function ProvidersTab() { const { providers, updateProviderSettings, isLocalModel } = useSettings(); diff --git a/app/lib/.server/llm/model.ts b/app/lib/.server/llm/model.ts index ecbcd6402..2588c2be9 100644 --- a/app/lib/.server/llm/model.ts +++ b/app/lib/.server/llm/model.ts @@ -11,6 +11,7 @@ import { createOpenRouter } from '@openrouter/ai-sdk-provider'; import { createMistral } from '@ai-sdk/mistral'; import { createCohere } from '@ai-sdk/cohere'; import type { LanguageModelV1 } from 'ai'; +import type { IProviderSetting } from '~/types/model'; export const DEFAULT_NUM_CTX = process.env.DEFAULT_NUM_CTX ? parseInt(process.env.DEFAULT_NUM_CTX, 10) : 32768; @@ -127,14 +128,20 @@ export function getXAIModel(apiKey: OptionalApiKey, model: string) { return openai(model); } -export function getModel(provider: string, model: string, env: Env, apiKeys?: Record) { +export function getModel( + provider: string, + model: string, + env: Env, + apiKeys?: Record, + providerSettings?: Record, +) { /* * let apiKey; // Declare first * let baseURL; */ const apiKey = getAPIKey(env, provider, apiKeys); // Then assign - const baseURL = getBaseURL(env, provider); + const baseURL = providerSettings?.[provider].baseUrl || getBaseURL(env, provider); switch (provider) { case 'Anthropic': diff --git a/app/lib/.server/llm/stream-text.ts b/app/lib/.server/llm/stream-text.ts index f408ba2a6..52271f07e 100644 --- a/app/lib/.server/llm/stream-text.ts +++ b/app/lib/.server/llm/stream-text.ts @@ -3,6 +3,7 @@ import { getModel } from '~/lib/.server/llm/model'; import { MAX_TOKENS } from './constants'; import { getSystemPrompt } from './prompts'; import { DEFAULT_MODEL, DEFAULT_PROVIDER, getModelList, MODEL_REGEX, PROVIDER_REGEX } from '~/utils/constants'; +import type { IProviderSetting } from '~/types/model'; interface ToolResult { toolCallId: string; @@ -58,15 +59,17 @@ function extractPropertiesFromMessage(message: Message): { model: string; provid return { model, provider, content: cleanedContent }; } -export async function streamText( - messages: Messages, - env: Env, - options?: StreamingOptions, - apiKeys?: Record, -) { +export async function streamText(props: { + messages: Messages; + env: Env; + options?: StreamingOptions; + apiKeys?: Record; + providerSettings?: Record; +}) { + const { messages, env, options, apiKeys, providerSettings } = props; let currentModel = DEFAULT_MODEL; let currentProvider = DEFAULT_PROVIDER.name; - const MODEL_LIST = await getModelList(apiKeys || {}); + const MODEL_LIST = await getModelList(apiKeys || {}, providerSettings); const processedMessages = messages.map((message) => { if (message.role === 'user') { const { model, provider, content } = extractPropertiesFromMessage(message); @@ -88,7 +91,7 @@ export async function streamText( const dynamicMaxTokens = modelDetails && modelDetails.maxTokenAllowed ? modelDetails.maxTokenAllowed : MAX_TOKENS; return _streamText({ - model: getModel(currentProvider, currentModel, env, apiKeys) as any, + model: getModel(currentProvider, currentModel, env, apiKeys, providerSettings) as any, system: getSystemPrompt(), maxTokens: dynamicMaxTokens, messages: convertToCoreMessages(processedMessages as any), diff --git a/app/lib/hooks/useSettings.tsx b/app/lib/hooks/useSettings.tsx index 9b63430f4..531e481ad 100644 --- a/app/lib/hooks/useSettings.tsx +++ b/app/lib/hooks/useSettings.tsx @@ -1,14 +1,8 @@ import { useStore } from '@nanostores/react'; -import { - isDebugMode, - isLocalModelsEnabled, - LOCAL_PROVIDERS, - providersStore, - type IProviderSetting, -} from '~/lib/stores/settings'; +import { isDebugMode, isLocalModelsEnabled, LOCAL_PROVIDERS, providersStore } from '~/lib/stores/settings'; import { useCallback, useEffect, useState } from 'react'; import Cookies from 'js-cookie'; -import type { ProviderInfo } from '~/utils/types'; +import type { IProviderSetting, ProviderInfo } from '~/types/model'; export function useSettings() { const providers = useStore(providersStore); diff --git a/app/lib/stores/settings.ts b/app/lib/stores/settings.ts index b6dbc06a5..31564e63a 100644 --- a/app/lib/stores/settings.ts +++ b/app/lib/stores/settings.ts @@ -1,7 +1,7 @@ import { atom, map } from 'nanostores'; import { workbenchStore } from './workbench'; -import type { ProviderInfo } from '~/utils/types'; import { PROVIDER_LIST } from '~/utils/constants'; +import type { IProviderConfig } from '~/types/model'; export interface Shortcut { key: string; @@ -17,14 +17,6 @@ export interface Shortcuts { toggleTerminal: Shortcut; } -export interface IProviderSetting { - enabled?: boolean; - baseUrl?: string; -} -export type IProviderConfig = ProviderInfo & { - settings: IProviderSetting; -}; - export const URL_CONFIGURABLE_PROVIDERS = ['Ollama', 'LMStudio', 'OpenAILike']; export const LOCAL_PROVIDERS = ['OpenAILike', 'LMStudio', 'Ollama']; diff --git a/app/routes/api.chat.ts b/app/routes/api.chat.ts index 007327454..9edf1af31 100644 --- a/app/routes/api.chat.ts +++ b/app/routes/api.chat.ts @@ -3,6 +3,7 @@ import { MAX_RESPONSE_SEGMENTS, MAX_TOKENS } from '~/lib/.server/llm/constants'; import { CONTINUE_PROMPT } from '~/lib/.server/llm/prompts'; import { streamText, type Messages, type StreamingOptions } from '~/lib/.server/llm/stream-text'; import SwitchableStream from '~/lib/.server/llm/switchable-stream'; +import type { IProviderSetting } from '~/types/model'; export async function action(args: ActionFunctionArgs) { return chatAction(args); @@ -38,6 +39,9 @@ async function chatAction({ context, request }: ActionFunctionArgs) { // Parse the cookie's value (returns an object or null if no cookie exists) const apiKeys = JSON.parse(parseCookies(cookieHeader || '').apiKeys || '{}'); + const providerSettings: Record = JSON.parse( + parseCookies(cookieHeader || '').providers || '{}', + ); const stream = new SwitchableStream(); @@ -60,13 +64,13 @@ async function chatAction({ context, request }: ActionFunctionArgs) { messages.push({ role: 'assistant', content }); messages.push({ role: 'user', content: CONTINUE_PROMPT }); - const result = await streamText(messages, context.cloudflare.env, options, apiKeys); + const result = await streamText({ messages, env: context.cloudflare.env, options, apiKeys, providerSettings }); return stream.switchSource(result.toAIStream()); }, }; - const result = await streamText(messages, context.cloudflare.env, options, apiKeys); + const result = await streamText({ messages, env: context.cloudflare.env, options, apiKeys, providerSettings }); stream.switchSource(result.toAIStream()); diff --git a/app/routes/api.enhancer.ts b/app/routes/api.enhancer.ts index 0738ae49f..cc51116ff 100644 --- a/app/routes/api.enhancer.ts +++ b/app/routes/api.enhancer.ts @@ -2,7 +2,7 @@ import { type ActionFunctionArgs } from '@remix-run/cloudflare'; import { StreamingTextResponse, parseStreamPart } from 'ai'; import { streamText } from '~/lib/.server/llm/stream-text'; import { stripIndents } from '~/utils/stripIndent'; -import type { ProviderInfo } from '~/types/model'; +import type { IProviderSetting, ProviderInfo } from '~/types/model'; const encoder = new TextEncoder(); const decoder = new TextDecoder(); @@ -11,8 +11,28 @@ export async function action(args: ActionFunctionArgs) { return enhancerAction(args); } +function parseCookies(cookieHeader: string) { + const cookies: any = {}; + + // Split the cookie string by semicolons and spaces + const items = cookieHeader.split(';').map((cookie) => cookie.trim()); + + items.forEach((item) => { + const [name, ...rest] = item.split('='); + + if (name && rest) { + // Decode the name and value, and join value parts in case it contains '=' + const decodedName = decodeURIComponent(name.trim()); + const decodedValue = decodeURIComponent(rest.join('=').trim()); + cookies[decodedName] = decodedValue; + } + }); + + return cookies; +} + async function enhancerAction({ context, request }: ActionFunctionArgs) { - const { message, model, provider, apiKeys } = await request.json<{ + const { message, model, provider } = await request.json<{ message: string; model: string; provider: ProviderInfo; @@ -36,9 +56,17 @@ async function enhancerAction({ context, request }: ActionFunctionArgs) { }); } + const cookieHeader = request.headers.get('Cookie'); + + // Parse the cookie's value (returns an object or null if no cookie exists) + const apiKeys = JSON.parse(parseCookies(cookieHeader || '').apiKeys || '{}'); + const providerSettings: Record = JSON.parse( + parseCookies(cookieHeader || '').providers || '{}', + ); + try { - const result = await streamText( - [ + const result = await streamText({ + messages: [ { role: 'user', content: @@ -73,10 +101,10 @@ async function enhancerAction({ context, request }: ActionFunctionArgs) { `, }, ], - context.cloudflare.env, - undefined, + env: context.cloudflare.env, apiKeys, - ); + providerSettings, + }); const transformStream = new TransformStream({ transform(chunk, controller) { diff --git a/app/types/model.ts b/app/types/model.ts index c6c58d75a..3bfbfde92 100644 --- a/app/types/model.ts +++ b/app/types/model.ts @@ -3,9 +3,17 @@ import type { ModelInfo } from '~/utils/types'; export type ProviderInfo = { staticModels: ModelInfo[]; name: string; - getDynamicModels?: (apiKeys?: Record) => Promise; + getDynamicModels?: (apiKeys?: Record, providerSettings?: IProviderSetting) => Promise; getApiKeyLink?: string; labelForGetApiKey?: string; icon?: string; - isEnabled?: boolean; +}; + +export interface IProviderSetting { + enabled?: boolean; + baseUrl?: string; +} + +export type IProviderConfig = ProviderInfo & { + settings: IProviderSetting; }; diff --git a/app/utils/constants.ts b/app/utils/constants.ts index eb90f29d0..ffedee6ca 100644 --- a/app/utils/constants.ts +++ b/app/utils/constants.ts @@ -1,6 +1,6 @@ import Cookies from 'js-cookie'; import type { ModelInfo, OllamaApiResponse, OllamaModel } from './types'; -import type { ProviderInfo } from '~/types/model'; +import type { ProviderInfo, IProviderSetting } from '~/types/model'; export const WORK_DIR_NAME = 'project'; export const WORK_DIR = `/home/${WORK_DIR_NAME}`; @@ -295,13 +295,16 @@ const staticModels: ModelInfo[] = PROVIDER_LIST.map((p) => p.staticModels).flat( export let MODEL_LIST: ModelInfo[] = [...staticModels]; -export async function getModelList(apiKeys: Record) { +export async function getModelList( + apiKeys: Record, + providerSettings?: Record, +) { MODEL_LIST = [ ...( await Promise.all( PROVIDER_LIST.filter( (p): p is ProviderInfo & { getDynamicModels: () => Promise } => !!p.getDynamicModels, - ).map((p) => p.getDynamicModels(apiKeys)), + ).map((p) => p.getDynamicModels(apiKeys, providerSettings?.[p.name])), ) ).flat(), ...staticModels, @@ -309,9 +312,9 @@ export async function getModelList(apiKeys: Record) { return MODEL_LIST; } -async function getTogetherModels(apiKeys?: Record): Promise { +async function getTogetherModels(apiKeys?: Record, settings?: IProviderSetting): Promise { try { - const baseUrl = import.meta.env.TOGETHER_API_BASE_URL || ''; + const baseUrl = settings?.baseUrl || import.meta.env.TOGETHER_API_BASE_URL || ''; const provider = 'Together'; if (!baseUrl) { @@ -350,8 +353,8 @@ async function getTogetherModels(apiKeys?: Record): Promise { - const defaultBaseUrl = import.meta.env.OLLAMA_API_BASE_URL || 'http://localhost:11434'; +const getOllamaBaseUrl = (settings?: IProviderSetting) => { + const defaultBaseUrl = settings?.baseUrl || import.meta.env.OLLAMA_API_BASE_URL || 'http://localhost:11434'; // Check if we're in the browser if (typeof window !== 'undefined') { @@ -365,7 +368,7 @@ const getOllamaBaseUrl = () => { return isDocker ? defaultBaseUrl.replace('localhost', 'host.docker.internal') : defaultBaseUrl; }; -async function getOllamaModels(): Promise { +async function getOllamaModels(apiKeys?: Record, settings?: IProviderSetting): Promise { /* * if (typeof window === 'undefined') { * return []; @@ -373,7 +376,7 @@ async function getOllamaModels(): Promise { */ try { - const baseUrl = getOllamaBaseUrl(); + const baseUrl = getOllamaBaseUrl(settings); const response = await fetch(`${baseUrl}/api/tags`); const data = (await response.json()) as OllamaApiResponse; @@ -389,20 +392,21 @@ async function getOllamaModels(): Promise { } } -async function getOpenAILikeModels(): Promise { +async function getOpenAILikeModels( + apiKeys?: Record, + settings?: IProviderSetting, +): Promise { try { - const baseUrl = import.meta.env.OPENAI_LIKE_API_BASE_URL || ''; + const baseUrl = settings?.baseUrl || import.meta.env.OPENAI_LIKE_API_BASE_URL || ''; if (!baseUrl) { return []; } - let apiKey = import.meta.env.OPENAI_LIKE_API_KEY ?? ''; - - const apikeys = JSON.parse(Cookies.get('apiKeys') || '{}'); + let apiKey = ''; - if (apikeys && apikeys.OpenAILike) { - apiKey = apikeys.OpenAILike; + if (apiKeys && apiKeys.OpenAILike) { + apiKey = apiKeys.OpenAILike; } const response = await fetch(`${baseUrl}/models`, { @@ -456,13 +460,13 @@ async function getOpenRouterModels(): Promise { })); } -async function getLMStudioModels(): Promise { +async function getLMStudioModels(_apiKeys?: Record, settings?: IProviderSetting): Promise { if (typeof window === 'undefined') { return []; } try { - const baseUrl = import.meta.env.LMSTUDIO_API_BASE_URL || 'http://localhost:1234'; + const baseUrl = settings?.baseUrl || import.meta.env.LMSTUDIO_API_BASE_URL || 'http://localhost:1234'; const response = await fetch(`${baseUrl}/v1/models`); const data = (await response.json()) as any; @@ -477,7 +481,7 @@ async function getLMStudioModels(): Promise { } } -async function initializeModelList(): Promise { +async function initializeModelList(providerSettings?: Record): Promise { let apiKeys: Record = {}; try { @@ -498,7 +502,7 @@ async function initializeModelList(): Promise { await Promise.all( PROVIDER_LIST.filter( (p): p is ProviderInfo & { getDynamicModels: () => Promise } => !!p.getDynamicModels, - ).map((p) => p.getDynamicModels(apiKeys)), + ).map((p) => p.getDynamicModels(apiKeys, providerSettings?.[p.name])), ) ).flat(), ...staticModels, diff --git a/app/utils/types.ts b/app/utils/types.ts index 874289107..1fa253fa4 100644 --- a/app/utils/types.ts +++ b/app/utils/types.ts @@ -26,12 +26,3 @@ export interface ModelInfo { provider: string; maxTokenAllowed: number; } - -export interface ProviderInfo { - staticModels: ModelInfo[]; - name: string; - getDynamicModels?: () => Promise; - getApiKeyLink?: string; - labelForGetApiKey?: string; - icon?: string; -} From 287c8adfaacdd1bda4d611dcc1eeb466b1b5f496 Mon Sep 17 00:00:00 2001 From: Anirban Kar Date: Wed, 11 Dec 2024 20:32:09 +0530 Subject: [PATCH 04/14] updated docs with new name --- docs/docs/CONTRIBUTING.md | 4 ++-- docs/docs/FAQ.md | 19 +++++++------------ docs/docs/index.md | 28 ++++++++++++++-------------- docs/mkdocs.yml | 10 +++++----- 4 files changed, 28 insertions(+), 33 deletions(-) diff --git a/docs/docs/CONTRIBUTING.md b/docs/docs/CONTRIBUTING.md index e1edd87da..b1232f93c 100644 --- a/docs/docs/CONTRIBUTING.md +++ b/docs/docs/CONTRIBUTING.md @@ -4,7 +4,7 @@ The `DEFAULT_NUM_CTX` environment variable can be used to limit the maximum number of context values used by the qwen2.5-coder model. For example, to limit the context to 24576 values (which uses 32GB of VRAM), set `DEFAULT_NUM_CTX=24576` in your `.env.local` file. -First off, thank you for considering contributing to Bolt.new! This fork aims to expand the capabilities of the original project by integrating multiple LLM providers and enhancing functionality. Every contribution helps make Bolt.new a better tool for developers worldwide. +First off, thank you for considering contributing to Bolt.diy! This fork aims to expand the capabilities of the original project by integrating multiple LLM providers and enhancing functionality. Every contribution helps make Bolt.diy a better tool for developers worldwide. ## 📋 Table of Contents - [Code of Conduct](#code-of-conduct) @@ -62,7 +62,7 @@ We're looking for dedicated contributors to help maintain and grow this project. ### 🔄 Initial Setup 1. Clone the repository: ```bash -git clone https://github.com/coleam00/bolt.new-any-llm.git +git clone https://github.com/stackblitz-labs/bolt.diy.git ``` 2. Install dependencies: diff --git a/docs/docs/FAQ.md b/docs/docs/FAQ.md index 8e57502ed..0c339c636 100644 --- a/docs/docs/FAQ.md +++ b/docs/docs/FAQ.md @@ -1,15 +1,15 @@ # Frequently Asked Questions (FAQ) -## How do I get the best results with oTToDev? +## 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 oTToDev scaffolds the project according to your preferences. + 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. - **Use the enhance prompt icon**: Before sending your prompt, click the *enhance* icon to let the AI refine your prompt. You can edit the suggested improvements before submitting. - **Scaffold the basics first, then add features**: - Ensure the foundational structure of your application is in place before introducing advanced functionality. This helps oTToDev establish a solid base to build on. + Ensure the foundational structure of your application is in place before introducing advanced functionality. This helps Bolt.diy establish a solid base to build on. - **Batch simple instructions**: Combine simple tasks into a single prompt to save time and reduce API credit consumption. For example: @@ -17,19 +17,14 @@ --- -## How do I contribute to oTToDev? +## How do I contribute to Bolt.diy? Check out our [Contribution Guide](CONTRIBUTING.md) for more details on how to get involved! --- -## Do you plan on merging oTToDev back into the official Bolt.new repo? -Stay tuned! We’ll share updates on this early next month. - ---- - -## What are the future plans for oTToDev? +## 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! @@ -38,13 +33,13 @@ New features and improvements are on the way! ## Why are there so many open issues/pull requests? -oTToDev 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! +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. --- -## How do local LLMs compare to larger models like Claude 3.5 Sonnet for oTToDev/Bolt.new? +## 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. diff --git a/docs/docs/index.md b/docs/docs/index.md index d9c953ed2..8a4d34122 100644 --- a/docs/docs/index.md +++ b/docs/docs/index.md @@ -1,28 +1,28 @@ -# Welcome to OTTO Dev -This fork of Bolt.new (oTToDev) 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. +# 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. -Join the community for oTToDev! +Join the community! https://thinktank.ottomator.ai -## Whats Bolt.new +## Whats Bolt.diy -Bolt.new is an AI-powered web development agent that allows you to prompt, run, edit, and deploy full-stack applications directly from your browser—no local setup required. If you're here to build your own AI-powered web dev agent using the Bolt open source codebase, [click here to get started!](./CONTRIBUTING.md) +Bolt.diy is an AI-powered web development agent that allows you to prompt, run, edit, and deploy full-stack applications directly from your browser—no local setup required. If you're here to build your own AI-powered web dev agent using the Bolt open source codebase, [click here to get started!](./CONTRIBUTING.md) -## What Makes Bolt.new Different +## What Makes Bolt.diy Different -Claude, v0, etc are incredible- but you can't install packages, run backends, or edit code. That’s where Bolt.new stands out: +Claude, v0, etc are incredible- but you can't install packages, run backends, or edit code. That’s where Bolt.diy stands out: -- **Full-Stack in the Browser**: Bolt.new integrates cutting-edge AI models with an in-browser development environment powered by **StackBlitz’s WebContainers**. This allows you to: +- **Full-Stack in the Browser**: Bolt.diy integrates cutting-edge AI models with an in-browser development environment powered by **StackBlitz’s WebContainers**. This allows you to: - Install and run npm tools and libraries (like Vite, Next.js, and more) - Run Node.js servers - Interact with third-party APIs - Deploy to production from chat - Share your work via a URL -- **AI with Environment Control**: Unlike traditional dev environments where the AI can only assist in code generation, Bolt.new gives AI models **complete control** over the entire environment including the filesystem, node server, package manager, terminal, and browser console. This empowers AI agents to handle the whole app lifecycle—from creation to deployment. +- **AI with Environment Control**: Unlike traditional dev environments where the AI can only assist in code generation, Bolt.diy gives AI models **complete control** over the entire environment including the filesystem, node server, package manager, terminal, and browser console. This empowers AI agents to handle the whole app lifecycle—from creation to deployment. -Whether you’re an experienced developer, a PM, or a designer, Bolt.new allows you to easily build production-grade full-stack applications. +Whether you’re an experienced developer, a PM, or a designer, Bolt.diy allows you to easily build production-grade full-stack applications. For developers interested in building their own AI-powered development tools with WebContainers, check out the open-source Bolt codebase in this repo! @@ -47,10 +47,10 @@ If you see usr/local/bin in the output then you're good to go. 3. Clone the repository (if you haven't already) by opening a Terminal window (or CMD with admin permissions) and then typing in this: ``` -git clone https://github.com/coleam00/bolt.new-any-llm.git +git clone https://github.com/stackblitz-labs/bolt.diy.git ``` -3. Rename .env.example to .env.local and add your LLM API keys. You will find this file on a Mac at "[your name]/bold.new-any-llm/.env.example". For Windows and Linux the path will be similar. +3. Rename .env.example to .env.local and add your LLM API keys. You will find this file on a Mac at "[your name]/bolt.diy/.env.example". For Windows and Linux the path will be similar. ![image](https://github.com/user-attachments/assets/7e6a532c-2268-401f-8310-e8d20c731328) @@ -150,7 +150,7 @@ pnpm run dev ## Adding New LLMs: -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. +To make new LLMs available to use in this version of Bolt.diy, 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! @@ -179,7 +179,7 @@ This will start the Remix Vite development server. You will need Google Chrome C ## Tips and Tricks -Here are some tips to get the most out of Bolt.new: +Here are some tips to get the most out of Bolt.diy: - **Be specific about your stack**: If you want to use specific frameworks or libraries (like Astro, Tailwind, ShadCN, or any other popular JavaScript framework), mention them in your initial prompt to ensure Bolt scaffolds the project accordingly. diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 045d77cac..50a132db1 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -31,19 +31,19 @@ theme: repo: fontawesome/brands/github # logo: assets/logo.png # favicon: assets/logo.png -repo_name: Bolt.Local -repo_url: https://github.com/coleam00/bolt.new-any-llm +repo_name: Bolt.diy +repo_url: https://github.com/stackblitz-labs/bolt.diy edit_uri: "" extra: generator: false social: - icon: fontawesome/brands/github - link: https://github.com/coleam00/bolt.new-any-llm - name: Bolt.Local + link: https://github.com/stackblitz-labs/bolt.diy + name: Bolt.diy - icon: fontawesome/brands/discourse link: https://thinktank.ottomator.ai/ - name: Bolt.Local Discourse + name: Bolt.diy Discourse markdown_extensions: From dbd10139d80fcb5e6b34525d972ec7ea26b33d07 Mon Sep 17 00:00:00 2001 From: Dustin Loring Date: Wed, 11 Dec 2024 10:02:14 -0500 Subject: [PATCH 05/14] Changed Docs URL Now that Docs are done building here is the change to the new URL --- app/components/settings/SettingsWindow.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/components/settings/SettingsWindow.tsx b/app/components/settings/SettingsWindow.tsx index 1c7271153..4391ff448 100644 --- a/app/components/settings/SettingsWindow.tsx +++ b/app/components/settings/SettingsWindow.tsx @@ -272,7 +272,7 @@ export const SettingsWindow = ({ open, onClose }: SettingsProps) => { GitHub Date: Wed, 11 Dec 2024 15:03:07 +0000 Subject: [PATCH 06/14] chore: update commit hash to 7d482ace3d20d62d73107777a51c4ccc375c5969 --- app/commit.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/commit.json b/app/commit.json index 208eefff9..3603e8e2f 100644 --- a/app/commit.json +++ b/app/commit.json @@ -1 +1 @@ -{ "commit": "154935cdeb054d2cc22dfb0c7e6cf084f02b95d0" } +{ "commit": "7d482ace3d20d62d73107777a51c4ccc375c5969" } From 70a15ac559d7446c19edd66b8e27e45ac80097d7 Mon Sep 17 00:00:00 2001 From: Anirban Kar Date: Wed, 11 Dec 2024 20:35:47 +0530 Subject: [PATCH 07/14] fix Title --- docs/mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 50a132db1..03148753c 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -1,4 +1,4 @@ -site_name: Bolt.Local Docs +site_name: Bolt.diy Docs site_dir: ../site theme: name: material From dcfb7fc45f390d6f61a8a5ae37e77c7bb60c76c1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 11 Dec 2024 15:06:49 +0000 Subject: [PATCH 08/14] chore: update commit hash to ab08f52aa0b13350cdfe0d0136b668af5e1cd108 --- app/commit.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/commit.json b/app/commit.json index 3603e8e2f..41f7e8072 100644 --- a/app/commit.json +++ b/app/commit.json @@ -1 +1 @@ -{ "commit": "7d482ace3d20d62d73107777a51c4ccc375c5969" } +{ "commit": "ab08f52aa0b13350cdfe0d0136b668af5e1cd108" } From 64eee11f58841d251f4df1368ba01ac687937d52 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 11 Dec 2024 15:44:01 +0000 Subject: [PATCH 09/14] chore: update commit hash to fd2c17c384a69ab5e7a40113342caa7de405b944 --- app/commit.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/commit.json b/app/commit.json index 41f7e8072..32c084215 100644 --- a/app/commit.json +++ b/app/commit.json @@ -1 +1 @@ -{ "commit": "ab08f52aa0b13350cdfe0d0136b668af5e1cd108" } +{ "commit": "fd2c17c384a69ab5e7a40113342caa7de405b944" } From 4739de47b41c700a770a7cd39d65e863daeebc23 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 11 Dec 2024 18:05:37 +0000 Subject: [PATCH 10/14] chore: update commit hash to c8a7ed9eb02a3626a6e1d591545102765bf762cb --- app/commit.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/commit.json b/app/commit.json index 32c084215..e1f933bc9 100644 --- a/app/commit.json +++ b/app/commit.json @@ -1 +1 @@ -{ "commit": "fd2c17c384a69ab5e7a40113342caa7de405b944" } +{ "commit": "c8a7ed9eb02a3626a6e1d591545102765bf762cb" } From 812b25dad7744d023dff8b6e0d3d86f0b4ee93bf Mon Sep 17 00:00:00 2001 From: eduardruzga Date: Wed, 11 Dec 2024 20:53:04 +0200 Subject: [PATCH 11/14] Remove other oTToDev mentions --- CONTRIBUTING.md | 2 +- FAQ.md | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 68215a289..304b140b8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,6 @@ # Contributing to oTToDev -First off, thank you for considering contributing to oTToDev! This fork aims to expand the capabilities of the original project by integrating multiple LLM providers and enhancing functionality. Every contribution helps make oTToDev a better tool for developers worldwide. +First off, thank you for considering contributing to Bolt.diy! This fork aims to expand the capabilities of the original project by integrating multiple LLM providers and enhancing functionality. Every contribution helps make Bolt.diy a better tool for developers worldwide. ## 📋 Table of Contents - [Code of Conduct](#code-of-conduct) diff --git a/FAQ.md b/FAQ.md index 3e267058c..c9467bbde 100644 --- a/FAQ.md +++ b/FAQ.md @@ -1,45 +1,45 @@ [![Bolt.new: AI-Powered Full-Stack Web Development in the Browser](./public/social_preview_index.jpg)](https://bolt.new) -# Bolt.new Fork by Cole Medin - oTToDev +# Bolt.new Fork by Cole Medin - Bolt.diy ## FAQ -### How do I get the best results with oTToDev? +### How do I get the best results with Bolt.diy? - **Be specific about your stack**: If you want to use specific frameworks or libraries (like Astro, Tailwind, ShadCN, or any other popular JavaScript framework), mention them in your initial prompt to ensure Bolt scaffolds the project accordingly. - **Use the enhance prompt icon**: Before sending your prompt, try clicking the 'enhance' icon to have the AI model help you refine your prompt, then edit the results before submitting. -- **Scaffold the basics first, then add features**: Make sure the basic structure of your application is in place before diving into more advanced functionality. This helps oTToDev understand the foundation of your project and ensure everything is wired up right before building out more advanced functionality. +- **Scaffold the basics first, then add features**: Make sure the basic structure of your application is in place before diving into more advanced functionality. This helps Bolt.diy understand the foundation of your project and ensure everything is wired up right before building out more advanced functionality. -- **Batch simple instructions**: Save time by combining simple instructions into one message. For example, you can ask oTToDev to change the color scheme, add mobile responsiveness, and restart the dev server, all in one go saving you time and reducing API credit consumption significantly. +- **Batch simple instructions**: Save time by combining simple instructions into one message. For example, you can ask Bolt.diy to change the color scheme, add mobile responsiveness, and restart the dev server, all in one go saving you time and reducing API credit consumption significantly. -### Do you plan on merging oTToDev back into the official Bolt.new repo? +### Do you plan on merging Bolt.diy back into the official Bolt.new repo? More news coming on this coming early next month - stay tuned! ### Why are there so many open issues/pull requests? -oTToDev was started simply to showcase how to edit an open source project and to do something cool with local LLMs on my (@ColeMedin) YouTube channel! However, it quickly +Bolt.diy was started simply to showcase how to edit an open source project and to do something cool with local LLMs on my (@ColeMedin) YouTube channel! However, it quickly grew into a massive community project that I am working hard to keep up with the demand of by forming a team of maintainers and getting as many people involved as I can. That effort is going well and all of our maintainers are ABSOLUTE rockstars, but it still takes time to organize everything so we can efficiently get through all the issues and PRs. But rest assured, we are working hard and even working on some partnerships behind the scenes to really help this project take off! -### How do local LLMs fair compared to larger models like Claude 3.5 Sonnet for oTToDev/Bolt.new? +### How do local LLMs fair compared to larger models like Claude 3.5 Sonnet for Bolt.diy/Bolt.new? As much as the gap is quickly closing between open source and massive close source models, you’re still going to get the best results with the very large models like GPT-4o, Claude 3.5 Sonnet, and DeepSeek Coder V2 236b. This is one of the big tasks we have at hand - figuring out how to prompt better, use agents, and improve the platform as a whole to make it work better for even the smaller local LLMs! ### I'm getting the error: "There was an error processing this request" -If you see this error within oTToDev, that is just the application telling you there is a problem at a high level, and this could mean a number of different things. To find the actual error, please check BOTH the terminal where you started the application (with Docker or pnpm) and the developer console in the browser. For most browsers, you can access the developer console by pressing F12 or right clicking anywhere in the browser and selecting “Inspect”. Then go to the “console” tab in the top right. +If you see this error within Bolt.diy, that is just the application telling you there is a problem at a high level, and this could mean a number of different things. To find the actual error, please check BOTH the terminal where you started the application (with Docker or pnpm) and the developer console in the browser. For most browsers, you can access the developer console by pressing F12 or right clicking anywhere in the browser and selecting “Inspect”. Then go to the “console” tab in the top right. ### I'm getting the error: "x-api-key header missing" -We have seen this error a couple times and for some reason just restarting the Docker container has fixed it. This seems to be Ollama specific. Another thing to try is try to run oTToDev with Docker or pnpm, whichever you didn’t run first. We are still on the hunt for why this happens once and a while! +We have seen this error a couple times and for some reason just restarting the Docker container has fixed it. This seems to be Ollama specific. Another thing to try is try to run Bolt.diy with Docker or pnpm, whichever you didn’t run first. We are still on the hunt for why this happens once and a while! -### I'm getting a blank preview when oTToDev runs my app! +### I'm getting a blank preview when Bolt.diy runs my app! -We promise you that we are constantly testing new PRs coming into oTToDev 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. +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: From 5b0a6db174bee6e544ef71a47fb43a7d92711f7e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 11 Dec 2024 18:56:21 +0000 Subject: [PATCH 12/14] chore: update commit hash to f682216515e6e594c6a55cf4520eb67d63939b60 --- app/commit.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/commit.json b/app/commit.json index e1f933bc9..a4093bba3 100644 --- a/app/commit.json +++ b/app/commit.json @@ -1 +1 @@ -{ "commit": "c8a7ed9eb02a3626a6e1d591545102765bf762cb" } +{ "commit": "f682216515e6e594c6a55cf4520eb67d63939b60" } From 46a90c246be6c6655c4c533a4545366c977ae42b Mon Sep 17 00:00:00 2001 From: eduardruzga Date: Wed, 11 Dec 2024 21:03:45 +0200 Subject: [PATCH 13/14] Add gemini flash 2.0 --- app/utils/constants.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/app/utils/constants.ts b/app/utils/constants.ts index 9d9877452..240b4b9d1 100644 --- a/app/utils/constants.ts +++ b/app/utils/constants.ts @@ -126,6 +126,7 @@ const PROVIDER_LIST: ProviderInfo[] = [ name: 'Google', staticModels: [ { name: 'gemini-1.5-flash-latest', label: 'Gemini 1.5 Flash', provider: 'Google', maxTokenAllowed: 8192 }, + { name: 'gemini-2.0-flash-exp', label: 'Gemini 2.0 Flash', provider: 'Google', maxTokenAllowed: 8192 }, { name: 'gemini-1.5-flash-002', label: 'Gemini 1.5 Flash-002', provider: 'Google', maxTokenAllowed: 8192 }, { name: 'gemini-1.5-flash-8b', label: 'Gemini 1.5 Flash-8b', provider: 'Google', maxTokenAllowed: 8192 }, { name: 'gemini-1.5-pro-latest', label: 'Gemini 1.5 Pro', provider: 'Google', maxTokenAllowed: 8192 }, From bfaa3f706517000bb9f421588a4d8c180174e5d0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 11 Dec 2024 19:04:37 +0000 Subject: [PATCH 14/14] chore: update commit hash to 8f3b4cd08249d26b14397e66241b9d099d3eb205 --- app/commit.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/commit.json b/app/commit.json index a4093bba3..8d738676d 100644 --- a/app/commit.json +++ b/app/commit.json @@ -1 +1 @@ -{ "commit": "f682216515e6e594c6a55cf4520eb67d63939b60" } +{ "commit": "8f3b4cd08249d26b14397e66241b9d099d3eb205" }