diff --git a/.env.example b/.env.example index 46a21e89..00a0fd94 100644 --- a/.env.example +++ b/.env.example @@ -5,6 +5,12 @@ # You only need this environment variable set if you want to use Groq models GROQ_API_KEY= +# Get your HuggingFace API Key here - +# https://huggingface.co/settings/tokens +# You only need this environment variable set if you want to use HuggingFace models +HuggingFace_API_KEY= + + # Get your Open AI API Key by following these instructions - # https://help.openai.com/en/articles/4936850-where-do-i-find-my-openai-api-key # You only need this environment variable set if you want to use GPT models @@ -55,4 +61,4 @@ LMSTUDIO_API_BASE_URL= XAI_API_KEY= # Include this environment variable if you want more logging for debugging locally -VITE_LOG_LEVEL=debug +VITE_LOG_LEVEL=debug \ No newline at end of file diff --git a/.husky/commit-msg b/.husky/commit-msg deleted file mode 100644 index d821bbc5..00000000 --- a/.husky/commit-msg +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env sh - -. "$(dirname "$0")/_/husky.sh" - -npx commitlint --edit $1 - -exit 0 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1bf3bfb7..5bb82b8f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -72,6 +72,7 @@ pnpm install - Add your LLM API keys (only set the ones you plan to use): ```bash GROQ_API_KEY=XXX +HuggingFace_API_KEY=XXX OPENAI_API_KEY=XXX ANTHROPIC_API_KEY=XXX ... diff --git a/Dockerfile b/Dockerfile index 3b5a74cd..82c07da0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -19,6 +19,7 @@ FROM base AS bolt-ai-production # Define environment variables with default values or let them be overridden ARG GROQ_API_KEY +ARG HuggingFace_API_KEY ARG OPENAI_API_KEY ARG ANTHROPIC_API_KEY ARG OPEN_ROUTER_API_KEY @@ -28,6 +29,7 @@ ARG VITE_LOG_LEVEL=debug ENV WRANGLER_SEND_METRICS=false \ GROQ_API_KEY=${GROQ_API_KEY} \ + HuggingFace_KEY=${HuggingFace_API_KEY} \ OPENAI_API_KEY=${OPENAI_API_KEY} \ ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY} \ OPEN_ROUTER_API_KEY=${OPEN_ROUTER_API_KEY} \ @@ -48,6 +50,7 @@ FROM base AS bolt-ai-development # Define the same environment variables for development ARG GROQ_API_KEY +ARG HuggingFace ARG OPENAI_API_KEY ARG ANTHROPIC_API_KEY ARG OPEN_ROUTER_API_KEY @@ -56,6 +59,7 @@ ARG OLLAMA_API_BASE_URL ARG VITE_LOG_LEVEL=debug ENV GROQ_API_KEY=${GROQ_API_KEY} \ + HuggingFace_API_KEY=${HuggingFace_API_KEY} \ OPENAI_API_KEY=${OPENAI_API_KEY} \ ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY} \ OPEN_ROUTER_API_KEY=${OPEN_ROUTER_API_KEY} \ diff --git a/app/components/chat/Artifact.tsx b/app/components/chat/Artifact.tsx index 62020fd8..682a4c76 100644 --- a/app/components/chat/Artifact.tsx +++ b/app/components/chat/Artifact.tsx @@ -7,6 +7,7 @@ import type { ActionState } from '~/lib/runtime/action-runner'; import { workbenchStore } from '~/lib/stores/workbench'; import { classNames } from '~/utils/classNames'; import { cubicEasingFn } from '~/utils/easings'; +import { WORK_DIR } from '~/utils/constants'; const highlighterOptions = { langs: ['shell'], @@ -129,6 +130,14 @@ const actionVariants = { visible: { opacity: 1, y: 0 }, }; +function openArtifactInWorkbench(filePath: any) { + if (workbenchStore.currentView.get() !== 'code') { + workbenchStore.currentView.set('code'); + } + + workbenchStore.setSelectedFile(`${WORK_DIR}/${filePath}`); +} + const ActionList = memo(({ actions }: ActionListProps) => { return ( @@ -169,7 +178,10 @@ const ActionList = memo(({ actions }: ActionListProps) => { {type === 'file' ? (
Create{' '} - + openArtifactInWorkbench(action.filePath)} + > {action.filePath}
diff --git a/app/components/chat/Messages.client.tsx b/app/components/chat/Messages.client.tsx index 2f35f49d..fca6fbd8 100644 --- a/app/components/chat/Messages.client.tsx +++ b/app/components/chat/Messages.client.tsx @@ -3,6 +3,11 @@ import React from 'react'; import { classNames } from '~/utils/classNames'; import { AssistantMessage } from './AssistantMessage'; import { UserMessage } from './UserMessage'; +import * as Tooltip from '@radix-ui/react-tooltip'; +import { useLocation, useNavigate } from '@remix-run/react'; +import { db, chatId } from '~/lib/persistence/useChatHistory'; +import { forkChat } from '~/lib/persistence/db'; +import { toast } from 'react-toastify'; interface MessagesProps { id?: string; @@ -13,41 +18,112 @@ interface MessagesProps { export const Messages = React.forwardRef((props: MessagesProps, ref) => { const { id, isStreaming = false, messages = [] } = props; + const location = useLocation(); + const navigate = useNavigate(); + + const handleRewind = (messageId: string) => { + const searchParams = new URLSearchParams(location.search); + searchParams.set('rewindTo', messageId); + window.location.search = searchParams.toString(); + }; + + const handleFork = async (messageId: string) => { + try { + if (!db || !chatId.get()) { + toast.error('Chat persistence is not available'); + return; + } + + const urlId = await forkChat(db, chatId.get()!, messageId); + window.location.href = `/chat/${urlId}`; + } catch (error) { + toast.error('Failed to fork chat: ' + (error as Error).message); + } + }; return ( -
- {messages.length > 0 - ? messages.map((message, index) => { - const { role, content } = message; - const isUserMessage = role === 'user'; - const isFirst = index === 0; - const isLast = index === messages.length - 1; + +
+ {messages.length > 0 + ? messages.map((message, index) => { + const { role, content, id: messageId } = message; + const isUserMessage = role === 'user'; + const isFirst = index === 0; + const isLast = index === messages.length - 1; - return ( -
- {isUserMessage && ( -
-
+ return ( +
+ {isUserMessage && ( +
+
+
+ )} +
+ {isUserMessage ? : }
- )} -
- {isUserMessage ? : } + {!isUserMessage && (
+ + + {messageId && (
)}
-
- ); - }) - : null} - {isStreaming && ( -
- )} -
+ ); + }) + : null} + {isStreaming && ( +
+ )} +
+ ); }); diff --git a/app/components/sidebar/HistoryItem.tsx b/app/components/sidebar/HistoryItem.tsx index 8022e4d2..df270c8c 100644 --- a/app/components/sidebar/HistoryItem.tsx +++ b/app/components/sidebar/HistoryItem.tsx @@ -5,9 +5,10 @@ import { type ChatHistoryItem } from '~/lib/persistence'; interface HistoryItemProps { item: ChatHistoryItem; onDelete?: (event: React.UIEvent) => void; + onDuplicate?: (id: string) => void; } -export function HistoryItem({ item, onDelete }: HistoryItemProps) { +export function HistoryItem({ item, onDelete, onDuplicate }: HistoryItemProps) { const [hovering, setHovering] = useState(false); const hoverRef = useRef(null); @@ -44,7 +45,14 @@ export function HistoryItem({ item, onDelete }: HistoryItemProps) { {item.description}
{hovering && ( -
+
+ {onDuplicate && ( +
{items.map((item) => ( - setDialogContent({ type: 'delete', item })} /> + handleDeleteClick(event, item)} + onDuplicate={() => handleDuplicate(item.id)} + /> ))}
))} diff --git a/app/lib/.server/llm/api-key.ts b/app/lib/.server/llm/api-key.ts index 464e334d..f282aa80 100644 --- a/app/lib/.server/llm/api-key.ts +++ b/app/lib/.server/llm/api-key.ts @@ -23,6 +23,8 @@ export function getAPIKey(cloudflareEnv: Env, provider: string, userApiKeys?: Re return env.GOOGLE_GENERATIVE_AI_API_KEY || cloudflareEnv.GOOGLE_GENERATIVE_AI_API_KEY; case 'Groq': return env.GROQ_API_KEY || cloudflareEnv.GROQ_API_KEY; + case 'HuggingFace': + return env.HuggingFace_API_KEY || cloudflareEnv.HuggingFace_API_KEY; case 'OpenRouter': return env.OPEN_ROUTER_API_KEY || cloudflareEnv.OPEN_ROUTER_API_KEY; case 'Deepseek': diff --git a/app/lib/.server/llm/model.ts b/app/lib/.server/llm/model.ts index 6be9d117..e07f2bbc 100644 --- a/app/lib/.server/llm/model.ts +++ b/app/lib/.server/llm/model.ts @@ -56,6 +56,15 @@ export function getGroqModel(apiKey: string, model: string) { return openai(model); } +export function getHuggingFaceModel(apiKey: string, model: string) { + const openai = createOpenAI({ + baseURL: 'https://api-inference.huggingface.co/v1/', + apiKey, + }); + + return openai(model); +} + export function getOllamaModel(baseURL: string, model: string) { let Ollama = ollama(model, { numCtx: 32768, @@ -110,6 +119,8 @@ export function getModel(provider: string, model: string, env: Env, apiKeys?: Re return getOpenAIModel(apiKey, model); case 'Groq': return getGroqModel(apiKey, model); + case 'HuggingFace': + return getHuggingFaceModel(apiKey, model); case 'OpenRouter': return getOpenRouterModel(apiKey, model); case 'Google': diff --git a/app/lib/.server/llm/prompts.ts b/app/lib/.server/llm/prompts.ts index 4eac2ecc..c0dc1dcf 100644 --- a/app/lib/.server/llm/prompts.ts +++ b/app/lib/.server/llm/prompts.ts @@ -88,7 +88,7 @@ You are Bolt, an expert AI assistant and exceptional senior software developer w Example: <${MODIFICATIONS_TAG_NAME}> - + @@ -2,7 +2,10 @@ return a + b; } @@ -103,7 +103,7 @@ You are Bolt, an expert AI assistant and exceptional senior software developer w + +console.log('The End'); - + // full file content here diff --git a/app/lib/persistence/db.ts b/app/lib/persistence/db.ts index 7a952e34..3aa2004a 100644 --- a/app/lib/persistence/db.ts +++ b/app/lib/persistence/db.ts @@ -158,3 +158,50 @@ async function getUrlIds(db: IDBDatabase): Promise { }; }); } + +export async function forkChat(db: IDBDatabase, chatId: string, messageId: string): Promise { + const chat = await getMessages(db, chatId); + if (!chat) throw new Error('Chat not found'); + + // Find the index of the message to fork at + const messageIndex = chat.messages.findIndex(msg => msg.id === messageId); + if (messageIndex === -1) throw new Error('Message not found'); + + // Get messages up to and including the selected message + const messages = chat.messages.slice(0, messageIndex + 1); + + // Generate new IDs + const newId = await getNextId(db); + const urlId = await getUrlId(db, newId); + + // Create the forked chat + await setMessages( + db, + newId, + messages, + urlId, + chat.description ? `${chat.description} (fork)` : 'Forked chat' + ); + + return urlId; +} + +export async function duplicateChat(db: IDBDatabase, id: string): Promise { + const chat = await getMessages(db, id); + if (!chat) { + throw new Error('Chat not found'); + } + + const newId = await getNextId(db); + const newUrlId = await getUrlId(db, newId); // Get a new urlId for the duplicated chat + + await setMessages( + db, + newId, + chat.messages, + newUrlId, // Use the new urlId + `${chat.description || 'Chat'} (copy)` + ); + + return newUrlId; // Return the urlId instead of id for navigation +} diff --git a/app/lib/persistence/useChatHistory.ts b/app/lib/persistence/useChatHistory.ts index e5627532..f5e8138e 100644 --- a/app/lib/persistence/useChatHistory.ts +++ b/app/lib/persistence/useChatHistory.ts @@ -1,10 +1,10 @@ -import { useLoaderData, useNavigate } from '@remix-run/react'; +import { useLoaderData, useNavigate, useSearchParams } from '@remix-run/react'; import { useState, useEffect } from 'react'; import { atom } from 'nanostores'; import type { Message } from 'ai'; import { toast } from 'react-toastify'; import { workbenchStore } from '~/lib/stores/workbench'; -import { getMessages, getNextId, getUrlId, openDatabase, setMessages } from './db'; +import { getMessages, getNextId, getUrlId, openDatabase, setMessages, duplicateChat } from './db'; export interface ChatHistoryItem { id: string; @@ -24,6 +24,7 @@ export const description = atom(undefined); export function useChatHistory() { const navigate = useNavigate(); const { id: mixedId } = useLoaderData<{ id?: string }>(); + const [searchParams] = useSearchParams(); const [initialMessages, setInitialMessages] = useState([]); const [ready, setReady] = useState(false); @@ -44,7 +45,12 @@ export function useChatHistory() { getMessages(db, mixedId) .then((storedMessages) => { if (storedMessages && storedMessages.messages.length > 0) { - setInitialMessages(storedMessages.messages); + const rewindId = searchParams.get('rewindTo'); + const filteredMessages = rewindId + ? storedMessages.messages.slice(0, storedMessages.messages.findIndex((m) => m.id === rewindId) + 1) + : storedMessages.messages; + + setInitialMessages(filteredMessages); setUrlId(storedMessages.urlId); description.set(storedMessages.description); chatId.set(storedMessages.id); @@ -93,6 +99,19 @@ export function useChatHistory() { await setMessages(db, chatId.get() as string, messages, urlId, description.get()); }, + duplicateCurrentChat: async (listItemId:string) => { + if (!db || (!mixedId && !listItemId)) { + return; + } + + try { + const newId = await duplicateChat(db, mixedId || listItemId); + navigate(`/chat/${newId}`); + toast.success('Chat duplicated successfully'); + } catch (error) { + toast.error('Failed to duplicate chat'); + } + } }; } diff --git a/app/lib/runtime/action-runner.ts b/app/lib/runtime/action-runner.ts index f94390be..e38a8ce9 100644 --- a/app/lib/runtime/action-runner.ts +++ b/app/lib/runtime/action-runner.ts @@ -94,7 +94,7 @@ export class ActionRunner { this.#updateAction(actionId, { ...action, ...data.action, executed: !isStreaming }); - this.#currentExecutionPromise = this.#currentExecutionPromise + return this.#currentExecutionPromise = this.#currentExecutionPromise .then(() => { return this.#executeAction(actionId, isStreaming); }) @@ -119,7 +119,14 @@ export class ActionRunner { break; } case 'start': { - await this.#runStartAction(action) + // making the start app non blocking + + this.#runStartAction(action).then(()=>this.#updateAction(actionId, { status: 'complete' })) + .catch(()=>this.#updateAction(actionId, { status: 'failed', error: 'Action failed' })) + // adding a delay to avoid any race condition between 2 start actions + // i am up for a better approch + await new Promise(resolve=>setTimeout(resolve,2000)) + return break; } } diff --git a/app/lib/stores/workbench.ts b/app/lib/stores/workbench.ts index 8589391c..4db14e7b 100644 --- a/app/lib/stores/workbench.ts +++ b/app/lib/stores/workbench.ts @@ -14,6 +14,7 @@ import { saveAs } from 'file-saver'; import { Octokit, type RestEndpointMethodTypes } from "@octokit/rest"; import * as nodePath from 'node:path'; import type { WebContainerProcess } from '@webcontainer/api'; +import { extractRelativePath } from '~/utils/diff'; export interface ArtifactState { id: string; @@ -42,7 +43,7 @@ export class WorkbenchStore { modifiedFiles = new Set(); artifactIdList: string[] = []; #boltTerminal: { terminal: ITerminal; process: WebContainerProcess } | undefined; - + #globalExecutionQueue=Promise.resolve(); constructor() { if (import.meta.hot) { import.meta.hot.data.artifacts = this.artifacts; @@ -52,6 +53,10 @@ export class WorkbenchStore { } } + addToExecutionQueue(callback: () => Promise) { + this.#globalExecutionQueue=this.#globalExecutionQueue.then(()=>callback()) + } + get previews() { return this.#previewsStore.previews; } @@ -255,8 +260,11 @@ export class WorkbenchStore { this.artifacts.setKey(messageId, { ...artifact, ...state }); } - - async addAction(data: ActionCallbackData) { + addAction(data: ActionCallbackData) { + this._addAction(data) + // this.addToExecutionQueue(()=>this._addAction(data)) + } + async _addAction(data: ActionCallbackData) { const { messageId } = data; const artifact = this.#getArtifact(messageId); @@ -265,10 +273,18 @@ export class WorkbenchStore { unreachable('Artifact not found'); } - artifact.runner.addAction(data); + return artifact.runner.addAction(data); } - async runAction(data: ActionCallbackData, isStreaming: boolean = false) { + runAction(data: ActionCallbackData, isStreaming: boolean = false) { + if(isStreaming) { + this._runAction(data, isStreaming) + } + else{ + this.addToExecutionQueue(()=>this._runAction(data, isStreaming)) + } + } + async _runAction(data: ActionCallbackData, isStreaming: boolean = false) { const { messageId } = data; const artifact = this.#getArtifact(messageId); @@ -293,11 +309,11 @@ export class WorkbenchStore { this.#editorStore.updateFile(fullPath, data.action.content); if (!isStreaming) { - this.resetCurrentDocument(); await artifact.runner.runAction(data); + this.resetAllFileModifications(); } } else { - artifact.runner.runAction(data); + await artifact.runner.runAction(data); } } @@ -312,8 +328,7 @@ export class WorkbenchStore { for (const [filePath, dirent] of Object.entries(files)) { if (dirent?.type === 'file' && !dirent.isBinary) { - // remove '/home/project/' from the beginning of the path - const relativePath = filePath.replace(/^\/home\/project\//, ''); + const relativePath = extractRelativePath(filePath); // split the path into segments const pathSegments = relativePath.split('/'); @@ -343,7 +358,7 @@ export class WorkbenchStore { for (const [filePath, dirent] of Object.entries(files)) { if (dirent?.type === 'file' && !dirent.isBinary) { - const relativePath = filePath.replace(/^\/home\/project\//, ''); + const relativePath = extractRelativePath(filePath); const pathSegments = relativePath.split('/'); let currentHandle = targetHandle; @@ -417,7 +432,7 @@ export class WorkbenchStore { content: Buffer.from(dirent.content).toString('base64'), encoding: 'base64', }); - return { path: filePath.replace(/^\/home\/project\//, ''), sha: blob.sha }; + return { path: extractRelativePath(filePath), sha: blob.sha }; } }) ); diff --git a/app/utils/constants.ts b/app/utils/constants.ts index fbcf226e..308832b9 100644 --- a/app/utils/constants.ts +++ b/app/utils/constants.ts @@ -71,7 +71,19 @@ const PROVIDER_LIST: ProviderInfo[] = [ { name: 'llama-3.2-1b-preview', label: 'Llama 3.2 1b (Groq)', provider: 'Groq' } ], getApiKeyLink: 'https://console.groq.com/keys' - }, { + }, + { + name: 'HuggingFace', + staticModels: [ + { name: 'Qwen/Qwen2.5-Coder-32B-Instruct', label: 'Qwen2.5-Coder-32B-Instruct (HuggingFace)', provider: 'HuggingFace' }, + { name: '01-ai/Yi-1.5-34B-Chat', label: 'Yi-1.5-34B-Chat (HuggingFace)', provider: 'HuggingFace' }, + { name: 'codellama/CodeLlama-34b-Instruct-hf', label: 'CodeLlama-34b-Instruct (HuggingFace)', provider: 'HuggingFace' }, + { name: 'NousResearch/Hermes-3-Llama-3.1-8B', label: 'Hermes-3-Llama-3.1-8B (HuggingFace)', provider: 'HuggingFace' } + ], + getApiKeyLink: 'https://huggingface.co/settings/tokens' + }, + + { name: 'OpenAI', staticModels: [ { name: 'gpt-4o-mini', label: 'GPT-4o Mini', provider: 'OpenAI' }, diff --git a/app/utils/diff.spec.ts b/app/utils/diff.spec.ts new file mode 100644 index 00000000..ee270f2a --- /dev/null +++ b/app/utils/diff.spec.ts @@ -0,0 +1,11 @@ +import { describe, expect, it } from 'vitest'; +import { extractRelativePath } from './diff'; +import { WORK_DIR } from './constants'; + +describe('Diff', () => { + it('should strip out Work_dir', () => { + const filePath = `${WORK_DIR}/index.js`; + const result = extractRelativePath(filePath); + expect(result).toBe('index.js'); + }); +}); diff --git a/app/utils/diff.ts b/app/utils/diff.ts index 66c0e155..25cde26f 100644 --- a/app/utils/diff.ts +++ b/app/utils/diff.ts @@ -1,6 +1,6 @@ import { createTwoFilesPatch } from 'diff'; import type { FileMap } from '~/lib/stores/files'; -import { MODIFICATIONS_TAG_NAME } from './constants'; +import { MODIFICATIONS_TAG_NAME, WORK_DIR } from './constants'; export const modificationsRegex = new RegExp( `^<${MODIFICATIONS_TAG_NAME}>[\\s\\S]*?<\\/${MODIFICATIONS_TAG_NAME}>\\s+`, @@ -75,6 +75,15 @@ export function diffFiles(fileName: string, oldFileContent: string, newFileConte return unifiedDiff; } +const regex = new RegExp(`^${WORK_DIR}\/`); + +/** + * Strips out the work directory from the file path. + */ +export function extractRelativePath(filePath: string) { + return filePath.replace(regex, ''); +} + /** * Converts the unified diff to HTML. * diff --git a/docker-compose.yaml b/docker-compose.yaml index c391dd73..7c03531d 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -14,6 +14,7 @@ services: # No strictly neded but serving as hints for Coolify - PORT=5173 - GROQ_API_KEY=${GROQ_API_KEY} + - HuggingFace_API_KEY=${HuggingFace_API_KEY} - OPENAI_API_KEY=${OPENAI_API_KEY} - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY} - OPEN_ROUTER_API_KEY=${OPEN_ROUTER_API_KEY} @@ -40,6 +41,7 @@ services: - WATCHPACK_POLLING=true - PORT=5173 - GROQ_API_KEY=${GROQ_API_KEY} + - HuggingFace_API_KEY=${HuggingFace_API_KEY} - OPENAI_API_KEY=${OPENAI_API_KEY} - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY} - OPEN_ROUTER_API_KEY=${OPEN_ROUTER_API_KEY} diff --git a/eslint.config.mjs b/eslint.config.mjs index 95df41ef..123aaf1f 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -4,7 +4,7 @@ import { getNamingConventionRule, tsFileExtensions } from '@blitz/eslint-plugin/ export default [ { - ignores: ['**/dist', '**/node_modules', '**/.wrangler', '**/bolt/build'], + ignores: ['**/dist', '**/node_modules', '**/.wrangler', '**/bolt/build', '**/.history'], }, ...blitzPlugin.configs.recommended(), { diff --git a/package.json b/package.json index e828e005..56a9e720 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ "@openrouter/ai-sdk-provider": "^0.0.5", "@radix-ui/react-dialog": "^1.1.1", "@radix-ui/react-dropdown-menu": "^2.1.1", + "@radix-ui/react-tooltip": "^1.1.4", "@remix-run/cloudflare": "^2.10.2", "@remix-run/cloudflare-pages": "^2.10.2", "@remix-run/react": "^2.10.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d507a018..ead147e9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -95,6 +95,9 @@ importers: '@radix-ui/react-dropdown-menu': specifier: ^2.1.1 version: 2.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-tooltip': + specifier: ^1.1.4 + version: 1.1.4(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@remix-run/cloudflare': specifier: ^2.10.2 version: 2.10.2(@cloudflare/workers-types@4.20240620.0)(typescript@5.5.2) @@ -1380,6 +1383,15 @@ packages: '@types/react': optional: true + '@radix-ui/react-context@1.1.1': + resolution: {integrity: sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-dialog@1.1.1': resolution: {integrity: sha512-zysS+iU4YP3STKNS6USvFVqI4qqx8EpiwmT5TuCApVEBca+eRCbONi4EgzfNSuVnOXvC5UPHHMjs8RXO6DH9Bg==} peerDependencies: @@ -1415,6 +1427,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-dismissable-layer@1.1.1': + resolution: {integrity: sha512-QSxg29lfr/xcev6kSz7MAlmDnzbP1eI/Dwn3Tp1ip0KT5CUELsxkekFEMVBEoykI3oV39hKT4TKZzBNMbcTZYQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-dropdown-menu@2.1.1': resolution: {integrity: sha512-y8E+x9fBq9qvteD2Zwa4397pUVhYsh9iq44b5RD5qu1GMJWBCBuVg1hMyItbc6+zH00TxGRqd9Iot4wzf3OoBQ==} peerDependencies: @@ -1498,6 +1523,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-portal@1.1.2': + resolution: {integrity: sha512-WeDYLGPxJb/5EGBoedyJbT0MpoULmwnIPMJMSldkuiMsBAv7N1cRdsTWZWht9vpPOiN3qyiGAtbK2is47/uMFg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-presence@1.1.0': resolution: {integrity: sha512-Gq6wuRN/asf9H/E/VzdKoUtT8GC9PQc9z40/vEr0VCJ4u5XvvhWIrSsCB6vD2/cH7ugTdSfYq9fLJCcM00acrQ==} peerDependencies: @@ -1511,6 +1549,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-presence@1.1.1': + resolution: {integrity: sha512-IeFXVi4YS1K0wVZzXNrbaaUvIJ3qdY+/Ih4eHFhWA9SwGR9UDX7Ck8abvL57C4cv3wwMvUE0OG69Qc3NCcTe/A==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-primitive@2.0.0': resolution: {integrity: sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==} peerDependencies: @@ -1546,6 +1597,19 @@ packages: '@types/react': optional: true + '@radix-ui/react-tooltip@1.1.4': + resolution: {integrity: sha512-QpObUH/ZlpaO4YgHSaYzrLO2VuO+ZBFFgGzjMUPwtiYnAzzNNDPJeEGRrT7qNOrWm/Jr08M1vlp+vTHtnSQ0Uw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-use-callback-ref@1.1.0': resolution: {integrity: sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==} peerDependencies: @@ -1600,6 +1664,19 @@ packages: '@types/react': optional: true + '@radix-ui/react-visually-hidden@1.1.0': + resolution: {integrity: sha512-N8MDZqtgCgG5S3aV60INAB475osJousYpZ4cTJ2cFbMpdHS5Y6loLTH8LPtkj2QN0x93J30HT/M3qJXM0+lyeQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/rect@1.1.0': resolution: {integrity: sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==} @@ -6720,6 +6797,12 @@ snapshots: optionalDependencies: '@types/react': 18.3.3 + '@radix-ui/react-context@1.1.1(@types/react@18.3.3)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.3 + '@radix-ui/react-dialog@1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@radix-ui/primitive': 1.1.0 @@ -6761,6 +6844,19 @@ snapshots: '@types/react': 18.3.3 '@types/react-dom': 18.3.0 + '@radix-ui/react-dismissable-layer@1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.0 + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.3)(react@18.3.1) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.3)(react@18.3.1) + '@radix-ui/react-use-escape-keydown': 1.1.0(@types/react@18.3.3)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.3 + '@types/react-dom': 18.3.0 + '@radix-ui/react-dropdown-menu@2.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@radix-ui/primitive': 1.1.0 @@ -6854,6 +6950,16 @@ snapshots: '@types/react': 18.3.3 '@types/react-dom': 18.3.0 + '@radix-ui/react-portal@1.1.2(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.3)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.3 + '@types/react-dom': 18.3.0 + '@radix-ui/react-presence@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.3)(react@18.3.1) @@ -6864,6 +6970,16 @@ snapshots: '@types/react': 18.3.3 '@types/react-dom': 18.3.0 + '@radix-ui/react-presence@1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.3)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.3)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.3 + '@types/react-dom': 18.3.0 + '@radix-ui/react-primitive@2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@radix-ui/react-slot': 1.1.0(@types/react@18.3.3)(react@18.3.1) @@ -6897,6 +7013,26 @@ snapshots: optionalDependencies: '@types/react': 18.3.3 + '@radix-ui/react-tooltip@1.1.4(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.0 + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.3)(react@18.3.1) + '@radix-ui/react-context': 1.1.1(@types/react@18.3.3)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.1.0(@types/react@18.3.3)(react@18.3.1) + '@radix-ui/react-popper': 1.2.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-portal': 1.1.2(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-presence': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.1.0(@types/react@18.3.3)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.3)(react@18.3.1) + '@radix-ui/react-visually-hidden': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.3 + '@types/react-dom': 18.3.0 + '@radix-ui/react-use-callback-ref@1.1.0(@types/react@18.3.3)(react@18.3.1)': dependencies: react: 18.3.1 @@ -6937,6 +7073,15 @@ snapshots: optionalDependencies: '@types/react': 18.3.3 + '@radix-ui/react-visually-hidden@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.3 + '@types/react-dom': 18.3.0 + '@radix-ui/rect@1.1.0': {} '@remix-run/cloudflare-pages@2.10.2(@cloudflare/workers-types@4.20240620.0)(typescript@5.5.2)': diff --git a/worker-configuration.d.ts b/worker-configuration.d.ts index 6db1aa60..1d8993bc 100644 --- a/worker-configuration.d.ts +++ b/worker-configuration.d.ts @@ -2,6 +2,7 @@ interface Env { ANTHROPIC_API_KEY: string; OPENAI_API_KEY: string; GROQ_API_KEY: string; + HuggingFace_API_KEY: string; OPEN_ROUTER_API_KEY: string; OLLAMA_API_BASE_URL: string; OPENAI_LIKE_API_KEY: string;