mirror of
https://github.com/stackblitz-labs/bolt.diy
synced 2025-05-09 06:29:43 +00:00
Merge remote-tracking branch 'upstream/main'
This commit is contained in:
commit
4b236e934e
@ -5,6 +5,12 @@
|
|||||||
# You only need this environment variable set if you want to use Groq models
|
# You only need this environment variable set if you want to use Groq models
|
||||||
GROQ_API_KEY=
|
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 -
|
# 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
|
# 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
|
# You only need this environment variable set if you want to use GPT models
|
||||||
|
@ -1,7 +0,0 @@
|
|||||||
#!/usr/bin/env sh
|
|
||||||
|
|
||||||
. "$(dirname "$0")/_/husky.sh"
|
|
||||||
|
|
||||||
npx commitlint --edit $1
|
|
||||||
|
|
||||||
exit 0
|
|
@ -72,6 +72,7 @@ pnpm install
|
|||||||
- Add your LLM API keys (only set the ones you plan to use):
|
- Add your LLM API keys (only set the ones you plan to use):
|
||||||
```bash
|
```bash
|
||||||
GROQ_API_KEY=XXX
|
GROQ_API_KEY=XXX
|
||||||
|
HuggingFace_API_KEY=XXX
|
||||||
OPENAI_API_KEY=XXX
|
OPENAI_API_KEY=XXX
|
||||||
ANTHROPIC_API_KEY=XXX
|
ANTHROPIC_API_KEY=XXX
|
||||||
...
|
...
|
||||||
|
@ -19,6 +19,7 @@ FROM base AS bolt-ai-production
|
|||||||
|
|
||||||
# Define environment variables with default values or let them be overridden
|
# Define environment variables with default values or let them be overridden
|
||||||
ARG GROQ_API_KEY
|
ARG GROQ_API_KEY
|
||||||
|
ARG HuggingFace_API_KEY
|
||||||
ARG OPENAI_API_KEY
|
ARG OPENAI_API_KEY
|
||||||
ARG ANTHROPIC_API_KEY
|
ARG ANTHROPIC_API_KEY
|
||||||
ARG OPEN_ROUTER_API_KEY
|
ARG OPEN_ROUTER_API_KEY
|
||||||
@ -28,6 +29,7 @@ ARG VITE_LOG_LEVEL=debug
|
|||||||
|
|
||||||
ENV WRANGLER_SEND_METRICS=false \
|
ENV WRANGLER_SEND_METRICS=false \
|
||||||
GROQ_API_KEY=${GROQ_API_KEY} \
|
GROQ_API_KEY=${GROQ_API_KEY} \
|
||||||
|
HuggingFace_KEY=${HuggingFace_API_KEY} \
|
||||||
OPENAI_API_KEY=${OPENAI_API_KEY} \
|
OPENAI_API_KEY=${OPENAI_API_KEY} \
|
||||||
ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY} \
|
ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY} \
|
||||||
OPEN_ROUTER_API_KEY=${OPEN_ROUTER_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
|
# Define the same environment variables for development
|
||||||
ARG GROQ_API_KEY
|
ARG GROQ_API_KEY
|
||||||
|
ARG HuggingFace
|
||||||
ARG OPENAI_API_KEY
|
ARG OPENAI_API_KEY
|
||||||
ARG ANTHROPIC_API_KEY
|
ARG ANTHROPIC_API_KEY
|
||||||
ARG OPEN_ROUTER_API_KEY
|
ARG OPEN_ROUTER_API_KEY
|
||||||
@ -56,6 +59,7 @@ ARG OLLAMA_API_BASE_URL
|
|||||||
ARG VITE_LOG_LEVEL=debug
|
ARG VITE_LOG_LEVEL=debug
|
||||||
|
|
||||||
ENV GROQ_API_KEY=${GROQ_API_KEY} \
|
ENV GROQ_API_KEY=${GROQ_API_KEY} \
|
||||||
|
HuggingFace_API_KEY=${HuggingFace_API_KEY} \
|
||||||
OPENAI_API_KEY=${OPENAI_API_KEY} \
|
OPENAI_API_KEY=${OPENAI_API_KEY} \
|
||||||
ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY} \
|
ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY} \
|
||||||
OPEN_ROUTER_API_KEY=${OPEN_ROUTER_API_KEY} \
|
OPEN_ROUTER_API_KEY=${OPEN_ROUTER_API_KEY} \
|
||||||
|
@ -7,6 +7,7 @@ import type { ActionState } from '~/lib/runtime/action-runner';
|
|||||||
import { workbenchStore } from '~/lib/stores/workbench';
|
import { workbenchStore } from '~/lib/stores/workbench';
|
||||||
import { classNames } from '~/utils/classNames';
|
import { classNames } from '~/utils/classNames';
|
||||||
import { cubicEasingFn } from '~/utils/easings';
|
import { cubicEasingFn } from '~/utils/easings';
|
||||||
|
import { WORK_DIR } from '~/utils/constants';
|
||||||
|
|
||||||
const highlighterOptions = {
|
const highlighterOptions = {
|
||||||
langs: ['shell'],
|
langs: ['shell'],
|
||||||
@ -129,6 +130,14 @@ const actionVariants = {
|
|||||||
visible: { opacity: 1, y: 0 },
|
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) => {
|
const ActionList = memo(({ actions }: ActionListProps) => {
|
||||||
return (
|
return (
|
||||||
<motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} transition={{ duration: 0.15 }}>
|
<motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} transition={{ duration: 0.15 }}>
|
||||||
@ -169,7 +178,10 @@ const ActionList = memo(({ actions }: ActionListProps) => {
|
|||||||
{type === 'file' ? (
|
{type === 'file' ? (
|
||||||
<div>
|
<div>
|
||||||
Create{' '}
|
Create{' '}
|
||||||
<code className="bg-bolt-elements-artifacts-inlineCode-background text-bolt-elements-artifacts-inlineCode-text px-1.5 py-1 rounded-md">
|
<code
|
||||||
|
className="bg-bolt-elements-artifacts-inlineCode-background text-bolt-elements-artifacts-inlineCode-text px-1.5 py-1 rounded-md text-bolt-elements-item-contentAccent hover:underline cursor-pointer"
|
||||||
|
onClick={() => openArtifactInWorkbench(action.filePath)}
|
||||||
|
>
|
||||||
{action.filePath}
|
{action.filePath}
|
||||||
</code>
|
</code>
|
||||||
</div>
|
</div>
|
||||||
|
@ -3,6 +3,11 @@ import React from 'react';
|
|||||||
import { classNames } from '~/utils/classNames';
|
import { classNames } from '~/utils/classNames';
|
||||||
import { AssistantMessage } from './AssistantMessage';
|
import { AssistantMessage } from './AssistantMessage';
|
||||||
import { UserMessage } from './UserMessage';
|
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 {
|
interface MessagesProps {
|
||||||
id?: string;
|
id?: string;
|
||||||
@ -13,41 +18,112 @@ interface MessagesProps {
|
|||||||
|
|
||||||
export const Messages = React.forwardRef<HTMLDivElement, MessagesProps>((props: MessagesProps, ref) => {
|
export const Messages = React.forwardRef<HTMLDivElement, MessagesProps>((props: MessagesProps, ref) => {
|
||||||
const { id, isStreaming = false, messages = [] } = props;
|
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 (
|
return (
|
||||||
<div id={id} ref={ref} className={props.className}>
|
<Tooltip.Provider delayDuration={200}>
|
||||||
{messages.length > 0
|
<div id={id} ref={ref} className={props.className}>
|
||||||
? messages.map((message, index) => {
|
{messages.length > 0
|
||||||
const { role, content } = message;
|
? messages.map((message, index) => {
|
||||||
const isUserMessage = role === 'user';
|
const { role, content, id: messageId } = message;
|
||||||
const isFirst = index === 0;
|
const isUserMessage = role === 'user';
|
||||||
const isLast = index === messages.length - 1;
|
const isFirst = index === 0;
|
||||||
|
const isLast = index === messages.length - 1;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
className={classNames('flex gap-4 p-6 w-full rounded-[calc(0.75rem-1px)]', {
|
className={classNames('flex gap-4 p-6 w-full rounded-[calc(0.75rem-1px)]', {
|
||||||
'bg-bolt-elements-messages-background': isUserMessage || !isStreaming || (isStreaming && !isLast),
|
'bg-bolt-elements-messages-background': isUserMessage || !isStreaming || (isStreaming && !isLast),
|
||||||
'bg-gradient-to-b from-bolt-elements-messages-background from-30% to-transparent':
|
'bg-gradient-to-b from-bolt-elements-messages-background from-30% to-transparent':
|
||||||
isStreaming && isLast,
|
isStreaming && isLast,
|
||||||
'mt-4': !isFirst,
|
'mt-4': !isFirst,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{isUserMessage && (
|
{isUserMessage && (
|
||||||
<div className="flex items-center justify-center w-[34px] h-[34px] overflow-hidden bg-white text-gray-600 rounded-full shrink-0 self-start">
|
<div className="flex items-center justify-center w-[34px] h-[34px] overflow-hidden bg-white text-gray-600 rounded-full shrink-0 self-start">
|
||||||
<div className="i-ph:user-fill text-xl"></div>
|
<div className="i-ph:user-fill text-xl"></div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="grid grid-col-1 w-full">
|
||||||
|
{isUserMessage ? <UserMessage content={content} /> : <AssistantMessage content={content} />}
|
||||||
</div>
|
</div>
|
||||||
)}
|
{!isUserMessage && (<div className="flex gap-2">
|
||||||
<div className="grid grid-col-1 w-full">
|
<Tooltip.Root>
|
||||||
{isUserMessage ? <UserMessage content={content} /> : <AssistantMessage content={content} />}
|
<Tooltip.Trigger asChild>
|
||||||
|
{messageId && (<button
|
||||||
|
onClick={() => handleRewind(messageId)}
|
||||||
|
key='i-ph:arrow-u-up-left'
|
||||||
|
className={classNames(
|
||||||
|
'i-ph:arrow-u-up-left',
|
||||||
|
'text-xl text-bolt-elements-textSecondary hover:text-bolt-elements-textPrimary transition-colors'
|
||||||
|
)}
|
||||||
|
/>)}
|
||||||
|
</Tooltip.Trigger>
|
||||||
|
<Tooltip.Portal>
|
||||||
|
<Tooltip.Content
|
||||||
|
className="bg-bolt-elements-tooltip-background text-bolt-elements-textPrimary px-3 py-2 rounded-lg text-sm shadow-lg"
|
||||||
|
sideOffset={5}
|
||||||
|
style={{zIndex: 1000}}
|
||||||
|
>
|
||||||
|
Revert to this message
|
||||||
|
<Tooltip.Arrow className="fill-bolt-elements-tooltip-background" />
|
||||||
|
</Tooltip.Content>
|
||||||
|
</Tooltip.Portal>
|
||||||
|
</Tooltip.Root>
|
||||||
|
|
||||||
|
<Tooltip.Root>
|
||||||
|
<Tooltip.Trigger asChild>
|
||||||
|
<button
|
||||||
|
onClick={() => handleFork(messageId)}
|
||||||
|
key='i-ph:git-fork'
|
||||||
|
className={classNames(
|
||||||
|
'i-ph:git-fork',
|
||||||
|
'text-xl text-bolt-elements-textSecondary hover:text-bolt-elements-textPrimary transition-colors'
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Tooltip.Trigger>
|
||||||
|
<Tooltip.Portal>
|
||||||
|
<Tooltip.Content
|
||||||
|
className="bg-bolt-elements-tooltip-background text-bolt-elements-textPrimary px-3 py-2 rounded-lg text-sm shadow-lg"
|
||||||
|
sideOffset={5}
|
||||||
|
style={{zIndex: 1000}}
|
||||||
|
>
|
||||||
|
Fork chat from this message
|
||||||
|
<Tooltip.Arrow className="fill-bolt-elements-tooltip-background" />
|
||||||
|
</Tooltip.Content>
|
||||||
|
</Tooltip.Portal>
|
||||||
|
</Tooltip.Root>
|
||||||
|
</div>)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
);
|
||||||
);
|
})
|
||||||
})
|
: null}
|
||||||
: null}
|
{isStreaming && (
|
||||||
{isStreaming && (
|
<div className="text-center w-full text-bolt-elements-textSecondary i-svg-spinners:3-dots-fade text-4xl mt-4"></div>
|
||||||
<div className="text-center w-full text-bolt-elements-textSecondary i-svg-spinners:3-dots-fade text-4xl mt-4"></div>
|
)}
|
||||||
)}
|
</div>
|
||||||
</div>
|
</Tooltip.Provider>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -5,9 +5,10 @@ import { type ChatHistoryItem } from '~/lib/persistence';
|
|||||||
interface HistoryItemProps {
|
interface HistoryItemProps {
|
||||||
item: ChatHistoryItem;
|
item: ChatHistoryItem;
|
||||||
onDelete?: (event: React.UIEvent) => void;
|
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 [hovering, setHovering] = useState(false);
|
||||||
const hoverRef = useRef<HTMLDivElement>(null);
|
const hoverRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
@ -44,7 +45,14 @@ export function HistoryItem({ item, onDelete }: HistoryItemProps) {
|
|||||||
{item.description}
|
{item.description}
|
||||||
<div className="absolute right-0 z-1 top-0 bottom-0 bg-gradient-to-l from-bolt-elements-background-depth-2 group-hover:from-bolt-elements-background-depth-3 to-transparent w-10 flex justify-end group-hover:w-15 group-hover:from-45%">
|
<div className="absolute right-0 z-1 top-0 bottom-0 bg-gradient-to-l from-bolt-elements-background-depth-2 group-hover:from-bolt-elements-background-depth-3 to-transparent w-10 flex justify-end group-hover:w-15 group-hover:from-45%">
|
||||||
{hovering && (
|
{hovering && (
|
||||||
<div className="flex items-center p-1 text-bolt-elements-textSecondary hover:text-bolt-elements-item-contentDanger">
|
<div className="flex items-center p-1 text-bolt-elements-textSecondary">
|
||||||
|
{onDuplicate && (
|
||||||
|
<button
|
||||||
|
className="i-ph:copy scale-110 mr-2"
|
||||||
|
onClick={() => onDuplicate?.(item.id)}
|
||||||
|
title="Duplicate chat"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<Dialog.Trigger asChild>
|
<Dialog.Trigger asChild>
|
||||||
<button
|
<button
|
||||||
className="i-ph:trash scale-110"
|
className="i-ph:trash scale-110"
|
||||||
|
@ -4,7 +4,7 @@ import { toast } from 'react-toastify';
|
|||||||
import { Dialog, DialogButton, DialogDescription, DialogRoot, DialogTitle } from '~/components/ui/Dialog';
|
import { Dialog, DialogButton, DialogDescription, DialogRoot, DialogTitle } from '~/components/ui/Dialog';
|
||||||
import { IconButton } from '~/components/ui/IconButton';
|
import { IconButton } from '~/components/ui/IconButton';
|
||||||
import { ThemeSwitch } from '~/components/ui/ThemeSwitch';
|
import { ThemeSwitch } from '~/components/ui/ThemeSwitch';
|
||||||
import { db, deleteById, getAll, chatId, type ChatHistoryItem } from '~/lib/persistence';
|
import { db, deleteById, getAll, chatId, type ChatHistoryItem, useChatHistory } from '~/lib/persistence';
|
||||||
import { cubicEasingFn } from '~/utils/easings';
|
import { cubicEasingFn } from '~/utils/easings';
|
||||||
import { logger } from '~/utils/logger';
|
import { logger } from '~/utils/logger';
|
||||||
import { HistoryItem } from './HistoryItem';
|
import { HistoryItem } from './HistoryItem';
|
||||||
@ -34,6 +34,7 @@ const menuVariants = {
|
|||||||
type DialogContent = { type: 'delete'; item: ChatHistoryItem } | null;
|
type DialogContent = { type: 'delete'; item: ChatHistoryItem } | null;
|
||||||
|
|
||||||
export const Menu = () => {
|
export const Menu = () => {
|
||||||
|
const { duplicateCurrentChat } = useChatHistory();
|
||||||
const menuRef = useRef<HTMLDivElement>(null);
|
const menuRef = useRef<HTMLDivElement>(null);
|
||||||
const [list, setList] = useState<ChatHistoryItem[]>([]);
|
const [list, setList] = useState<ChatHistoryItem[]>([]);
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
@ -99,6 +100,17 @@ export const Menu = () => {
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const handleDeleteClick = (event: React.UIEvent, item: ChatHistoryItem) => {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
setDialogContent({ type: 'delete', item });
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDuplicate = async (id: string) => {
|
||||||
|
await duplicateCurrentChat(id);
|
||||||
|
loadEntries(); // Reload the list after duplication
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<motion.div
|
<motion.div
|
||||||
ref={menuRef}
|
ref={menuRef}
|
||||||
@ -128,7 +140,12 @@ export const Menu = () => {
|
|||||||
{category}
|
{category}
|
||||||
</div>
|
</div>
|
||||||
{items.map((item) => (
|
{items.map((item) => (
|
||||||
<HistoryItem key={item.id} item={item} onDelete={() => setDialogContent({ type: 'delete', item })} />
|
<HistoryItem
|
||||||
|
key={item.id}
|
||||||
|
item={item}
|
||||||
|
onDelete={(event) => handleDeleteClick(event, item)}
|
||||||
|
onDuplicate={() => handleDuplicate(item.id)}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
@ -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;
|
return env.GOOGLE_GENERATIVE_AI_API_KEY || cloudflareEnv.GOOGLE_GENERATIVE_AI_API_KEY;
|
||||||
case 'Groq':
|
case 'Groq':
|
||||||
return env.GROQ_API_KEY || cloudflareEnv.GROQ_API_KEY;
|
return env.GROQ_API_KEY || cloudflareEnv.GROQ_API_KEY;
|
||||||
|
case 'HuggingFace':
|
||||||
|
return env.HuggingFace_API_KEY || cloudflareEnv.HuggingFace_API_KEY;
|
||||||
case 'OpenRouter':
|
case 'OpenRouter':
|
||||||
return env.OPEN_ROUTER_API_KEY || cloudflareEnv.OPEN_ROUTER_API_KEY;
|
return env.OPEN_ROUTER_API_KEY || cloudflareEnv.OPEN_ROUTER_API_KEY;
|
||||||
case 'Deepseek':
|
case 'Deepseek':
|
||||||
|
@ -56,6 +56,15 @@ export function getGroqModel(apiKey: string, model: string) {
|
|||||||
return openai(model);
|
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) {
|
export function getOllamaModel(baseURL: string, model: string) {
|
||||||
let Ollama = ollama(model, {
|
let Ollama = ollama(model, {
|
||||||
numCtx: 32768,
|
numCtx: 32768,
|
||||||
@ -110,6 +119,8 @@ export function getModel(provider: string, model: string, env: Env, apiKeys?: Re
|
|||||||
return getOpenAIModel(apiKey, model);
|
return getOpenAIModel(apiKey, model);
|
||||||
case 'Groq':
|
case 'Groq':
|
||||||
return getGroqModel(apiKey, model);
|
return getGroqModel(apiKey, model);
|
||||||
|
case 'HuggingFace':
|
||||||
|
return getHuggingFaceModel(apiKey, model);
|
||||||
case 'OpenRouter':
|
case 'OpenRouter':
|
||||||
return getOpenRouterModel(apiKey, model);
|
return getOpenRouterModel(apiKey, model);
|
||||||
case 'Google':
|
case 'Google':
|
||||||
|
@ -88,7 +88,7 @@ You are Bolt, an expert AI assistant and exceptional senior software developer w
|
|||||||
Example:
|
Example:
|
||||||
|
|
||||||
<${MODIFICATIONS_TAG_NAME}>
|
<${MODIFICATIONS_TAG_NAME}>
|
||||||
<diff path="/home/project/src/main.js">
|
<diff path="${WORK_DIR}/src/main.js">
|
||||||
@@ -2,7 +2,10 @@
|
@@ -2,7 +2,10 @@
|
||||||
return a + b;
|
return a + b;
|
||||||
}
|
}
|
||||||
@ -103,7 +103,7 @@ You are Bolt, an expert AI assistant and exceptional senior software developer w
|
|||||||
+
|
+
|
||||||
+console.log('The End');
|
+console.log('The End');
|
||||||
</diff>
|
</diff>
|
||||||
<file path="/home/project/package.json">
|
<file path="${WORK_DIR}/package.json">
|
||||||
// full file content here
|
// full file content here
|
||||||
</file>
|
</file>
|
||||||
</${MODIFICATIONS_TAG_NAME}>
|
</${MODIFICATIONS_TAG_NAME}>
|
||||||
|
@ -158,3 +158,50 @@ async function getUrlIds(db: IDBDatabase): Promise<string[]> {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function forkChat(db: IDBDatabase, chatId: string, messageId: string): Promise<string> {
|
||||||
|
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<string> {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
@ -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 { useState, useEffect } from 'react';
|
||||||
import { atom } from 'nanostores';
|
import { atom } from 'nanostores';
|
||||||
import type { Message } from 'ai';
|
import type { Message } from 'ai';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
import { workbenchStore } from '~/lib/stores/workbench';
|
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 {
|
export interface ChatHistoryItem {
|
||||||
id: string;
|
id: string;
|
||||||
@ -24,6 +24,7 @@ export const description = atom<string | undefined>(undefined);
|
|||||||
export function useChatHistory() {
|
export function useChatHistory() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { id: mixedId } = useLoaderData<{ id?: string }>();
|
const { id: mixedId } = useLoaderData<{ id?: string }>();
|
||||||
|
const [searchParams] = useSearchParams();
|
||||||
|
|
||||||
const [initialMessages, setInitialMessages] = useState<Message[]>([]);
|
const [initialMessages, setInitialMessages] = useState<Message[]>([]);
|
||||||
const [ready, setReady] = useState<boolean>(false);
|
const [ready, setReady] = useState<boolean>(false);
|
||||||
@ -44,7 +45,12 @@ export function useChatHistory() {
|
|||||||
getMessages(db, mixedId)
|
getMessages(db, mixedId)
|
||||||
.then((storedMessages) => {
|
.then((storedMessages) => {
|
||||||
if (storedMessages && storedMessages.messages.length > 0) {
|
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);
|
setUrlId(storedMessages.urlId);
|
||||||
description.set(storedMessages.description);
|
description.set(storedMessages.description);
|
||||||
chatId.set(storedMessages.id);
|
chatId.set(storedMessages.id);
|
||||||
@ -93,6 +99,19 @@ export function useChatHistory() {
|
|||||||
|
|
||||||
await setMessages(db, chatId.get() as string, messages, urlId, description.get());
|
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');
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,7 +94,7 @@ export class ActionRunner {
|
|||||||
|
|
||||||
this.#updateAction(actionId, { ...action, ...data.action, executed: !isStreaming });
|
this.#updateAction(actionId, { ...action, ...data.action, executed: !isStreaming });
|
||||||
|
|
||||||
this.#currentExecutionPromise = this.#currentExecutionPromise
|
return this.#currentExecutionPromise = this.#currentExecutionPromise
|
||||||
.then(() => {
|
.then(() => {
|
||||||
return this.#executeAction(actionId, isStreaming);
|
return this.#executeAction(actionId, isStreaming);
|
||||||
})
|
})
|
||||||
@ -119,7 +119,14 @@ export class ActionRunner {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'start': {
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ import { saveAs } from 'file-saver';
|
|||||||
import { Octokit, type RestEndpointMethodTypes } from "@octokit/rest";
|
import { Octokit, type RestEndpointMethodTypes } from "@octokit/rest";
|
||||||
import * as nodePath from 'node:path';
|
import * as nodePath from 'node:path';
|
||||||
import type { WebContainerProcess } from '@webcontainer/api';
|
import type { WebContainerProcess } from '@webcontainer/api';
|
||||||
|
import { extractRelativePath } from '~/utils/diff';
|
||||||
|
|
||||||
export interface ArtifactState {
|
export interface ArtifactState {
|
||||||
id: string;
|
id: string;
|
||||||
@ -42,7 +43,7 @@ export class WorkbenchStore {
|
|||||||
modifiedFiles = new Set<string>();
|
modifiedFiles = new Set<string>();
|
||||||
artifactIdList: string[] = [];
|
artifactIdList: string[] = [];
|
||||||
#boltTerminal: { terminal: ITerminal; process: WebContainerProcess } | undefined;
|
#boltTerminal: { terminal: ITerminal; process: WebContainerProcess } | undefined;
|
||||||
|
#globalExecutionQueue=Promise.resolve();
|
||||||
constructor() {
|
constructor() {
|
||||||
if (import.meta.hot) {
|
if (import.meta.hot) {
|
||||||
import.meta.hot.data.artifacts = this.artifacts;
|
import.meta.hot.data.artifacts = this.artifacts;
|
||||||
@ -52,6 +53,10 @@ export class WorkbenchStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addToExecutionQueue(callback: () => Promise<void>) {
|
||||||
|
this.#globalExecutionQueue=this.#globalExecutionQueue.then(()=>callback())
|
||||||
|
}
|
||||||
|
|
||||||
get previews() {
|
get previews() {
|
||||||
return this.#previewsStore.previews;
|
return this.#previewsStore.previews;
|
||||||
}
|
}
|
||||||
@ -255,8 +260,11 @@ export class WorkbenchStore {
|
|||||||
|
|
||||||
this.artifacts.setKey(messageId, { ...artifact, ...state });
|
this.artifacts.setKey(messageId, { ...artifact, ...state });
|
||||||
}
|
}
|
||||||
|
addAction(data: ActionCallbackData) {
|
||||||
async addAction(data: ActionCallbackData) {
|
this._addAction(data)
|
||||||
|
// this.addToExecutionQueue(()=>this._addAction(data))
|
||||||
|
}
|
||||||
|
async _addAction(data: ActionCallbackData) {
|
||||||
const { messageId } = data;
|
const { messageId } = data;
|
||||||
|
|
||||||
const artifact = this.#getArtifact(messageId);
|
const artifact = this.#getArtifact(messageId);
|
||||||
@ -265,10 +273,18 @@ export class WorkbenchStore {
|
|||||||
unreachable('Artifact not found');
|
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 { messageId } = data;
|
||||||
|
|
||||||
const artifact = this.#getArtifact(messageId);
|
const artifact = this.#getArtifact(messageId);
|
||||||
@ -293,11 +309,11 @@ export class WorkbenchStore {
|
|||||||
this.#editorStore.updateFile(fullPath, data.action.content);
|
this.#editorStore.updateFile(fullPath, data.action.content);
|
||||||
|
|
||||||
if (!isStreaming) {
|
if (!isStreaming) {
|
||||||
this.resetCurrentDocument();
|
|
||||||
await artifact.runner.runAction(data);
|
await artifact.runner.runAction(data);
|
||||||
|
this.resetAllFileModifications();
|
||||||
}
|
}
|
||||||
} else {
|
} 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)) {
|
for (const [filePath, dirent] of Object.entries(files)) {
|
||||||
if (dirent?.type === 'file' && !dirent.isBinary) {
|
if (dirent?.type === 'file' && !dirent.isBinary) {
|
||||||
// remove '/home/project/' from the beginning of the path
|
const relativePath = extractRelativePath(filePath);
|
||||||
const relativePath = filePath.replace(/^\/home\/project\//, '');
|
|
||||||
|
|
||||||
// split the path into segments
|
// split the path into segments
|
||||||
const pathSegments = relativePath.split('/');
|
const pathSegments = relativePath.split('/');
|
||||||
@ -343,7 +358,7 @@ export class WorkbenchStore {
|
|||||||
|
|
||||||
for (const [filePath, dirent] of Object.entries(files)) {
|
for (const [filePath, dirent] of Object.entries(files)) {
|
||||||
if (dirent?.type === 'file' && !dirent.isBinary) {
|
if (dirent?.type === 'file' && !dirent.isBinary) {
|
||||||
const relativePath = filePath.replace(/^\/home\/project\//, '');
|
const relativePath = extractRelativePath(filePath);
|
||||||
const pathSegments = relativePath.split('/');
|
const pathSegments = relativePath.split('/');
|
||||||
let currentHandle = targetHandle;
|
let currentHandle = targetHandle;
|
||||||
|
|
||||||
@ -417,7 +432,7 @@ export class WorkbenchStore {
|
|||||||
content: Buffer.from(dirent.content).toString('base64'),
|
content: Buffer.from(dirent.content).toString('base64'),
|
||||||
encoding: 'base64',
|
encoding: 'base64',
|
||||||
});
|
});
|
||||||
return { path: filePath.replace(/^\/home\/project\//, ''), sha: blob.sha };
|
return { path: extractRelativePath(filePath), sha: blob.sha };
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
@ -71,7 +71,19 @@ const PROVIDER_LIST: ProviderInfo[] = [
|
|||||||
{ name: 'llama-3.2-1b-preview', label: 'Llama 3.2 1b (Groq)', provider: 'Groq' }
|
{ name: 'llama-3.2-1b-preview', label: 'Llama 3.2 1b (Groq)', provider: 'Groq' }
|
||||||
],
|
],
|
||||||
getApiKeyLink: 'https://console.groq.com/keys'
|
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',
|
name: 'OpenAI',
|
||||||
staticModels: [
|
staticModels: [
|
||||||
{ name: 'gpt-4o-mini', label: 'GPT-4o Mini', provider: 'OpenAI' },
|
{ name: 'gpt-4o-mini', label: 'GPT-4o Mini', provider: 'OpenAI' },
|
||||||
|
11
app/utils/diff.spec.ts
Normal file
11
app/utils/diff.spec.ts
Normal file
@ -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');
|
||||||
|
});
|
||||||
|
});
|
@ -1,6 +1,6 @@
|
|||||||
import { createTwoFilesPatch } from 'diff';
|
import { createTwoFilesPatch } from 'diff';
|
||||||
import type { FileMap } from '~/lib/stores/files';
|
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(
|
export const modificationsRegex = new RegExp(
|
||||||
`^<${MODIFICATIONS_TAG_NAME}>[\\s\\S]*?<\\/${MODIFICATIONS_TAG_NAME}>\\s+`,
|
`^<${MODIFICATIONS_TAG_NAME}>[\\s\\S]*?<\\/${MODIFICATIONS_TAG_NAME}>\\s+`,
|
||||||
@ -75,6 +75,15 @@ export function diffFiles(fileName: string, oldFileContent: string, newFileConte
|
|||||||
return unifiedDiff;
|
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.
|
* Converts the unified diff to HTML.
|
||||||
*
|
*
|
||||||
|
@ -14,6 +14,7 @@ services:
|
|||||||
# No strictly neded but serving as hints for Coolify
|
# No strictly neded but serving as hints for Coolify
|
||||||
- PORT=5173
|
- PORT=5173
|
||||||
- GROQ_API_KEY=${GROQ_API_KEY}
|
- GROQ_API_KEY=${GROQ_API_KEY}
|
||||||
|
- HuggingFace_API_KEY=${HuggingFace_API_KEY}
|
||||||
- OPENAI_API_KEY=${OPENAI_API_KEY}
|
- OPENAI_API_KEY=${OPENAI_API_KEY}
|
||||||
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
|
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
|
||||||
- OPEN_ROUTER_API_KEY=${OPEN_ROUTER_API_KEY}
|
- OPEN_ROUTER_API_KEY=${OPEN_ROUTER_API_KEY}
|
||||||
@ -40,6 +41,7 @@ services:
|
|||||||
- WATCHPACK_POLLING=true
|
- WATCHPACK_POLLING=true
|
||||||
- PORT=5173
|
- PORT=5173
|
||||||
- GROQ_API_KEY=${GROQ_API_KEY}
|
- GROQ_API_KEY=${GROQ_API_KEY}
|
||||||
|
- HuggingFace_API_KEY=${HuggingFace_API_KEY}
|
||||||
- OPENAI_API_KEY=${OPENAI_API_KEY}
|
- OPENAI_API_KEY=${OPENAI_API_KEY}
|
||||||
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
|
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
|
||||||
- OPEN_ROUTER_API_KEY=${OPEN_ROUTER_API_KEY}
|
- OPEN_ROUTER_API_KEY=${OPEN_ROUTER_API_KEY}
|
||||||
|
@ -4,7 +4,7 @@ import { getNamingConventionRule, tsFileExtensions } from '@blitz/eslint-plugin/
|
|||||||
|
|
||||||
export default [
|
export default [
|
||||||
{
|
{
|
||||||
ignores: ['**/dist', '**/node_modules', '**/.wrangler', '**/bolt/build'],
|
ignores: ['**/dist', '**/node_modules', '**/.wrangler', '**/bolt/build', '**/.history'],
|
||||||
},
|
},
|
||||||
...blitzPlugin.configs.recommended(),
|
...blitzPlugin.configs.recommended(),
|
||||||
{
|
{
|
||||||
|
@ -54,6 +54,7 @@
|
|||||||
"@openrouter/ai-sdk-provider": "^0.0.5",
|
"@openrouter/ai-sdk-provider": "^0.0.5",
|
||||||
"@radix-ui/react-dialog": "^1.1.1",
|
"@radix-ui/react-dialog": "^1.1.1",
|
||||||
"@radix-ui/react-dropdown-menu": "^2.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": "^2.10.2",
|
||||||
"@remix-run/cloudflare-pages": "^2.10.2",
|
"@remix-run/cloudflare-pages": "^2.10.2",
|
||||||
"@remix-run/react": "^2.10.2",
|
"@remix-run/react": "^2.10.2",
|
||||||
|
145
pnpm-lock.yaml
145
pnpm-lock.yaml
@ -95,6 +95,9 @@ importers:
|
|||||||
'@radix-ui/react-dropdown-menu':
|
'@radix-ui/react-dropdown-menu':
|
||||||
specifier: ^2.1.1
|
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)
|
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':
|
'@remix-run/cloudflare':
|
||||||
specifier: ^2.10.2
|
specifier: ^2.10.2
|
||||||
version: 2.10.2(@cloudflare/workers-types@4.20240620.0)(typescript@5.5.2)
|
version: 2.10.2(@cloudflare/workers-types@4.20240620.0)(typescript@5.5.2)
|
||||||
@ -1380,6 +1383,15 @@ packages:
|
|||||||
'@types/react':
|
'@types/react':
|
||||||
optional: true
|
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':
|
'@radix-ui/react-dialog@1.1.1':
|
||||||
resolution: {integrity: sha512-zysS+iU4YP3STKNS6USvFVqI4qqx8EpiwmT5TuCApVEBca+eRCbONi4EgzfNSuVnOXvC5UPHHMjs8RXO6DH9Bg==}
|
resolution: {integrity: sha512-zysS+iU4YP3STKNS6USvFVqI4qqx8EpiwmT5TuCApVEBca+eRCbONi4EgzfNSuVnOXvC5UPHHMjs8RXO6DH9Bg==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -1415,6 +1427,19 @@ packages:
|
|||||||
'@types/react-dom':
|
'@types/react-dom':
|
||||||
optional: true
|
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':
|
'@radix-ui/react-dropdown-menu@2.1.1':
|
||||||
resolution: {integrity: sha512-y8E+x9fBq9qvteD2Zwa4397pUVhYsh9iq44b5RD5qu1GMJWBCBuVg1hMyItbc6+zH00TxGRqd9Iot4wzf3OoBQ==}
|
resolution: {integrity: sha512-y8E+x9fBq9qvteD2Zwa4397pUVhYsh9iq44b5RD5qu1GMJWBCBuVg1hMyItbc6+zH00TxGRqd9Iot4wzf3OoBQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -1498,6 +1523,19 @@ packages:
|
|||||||
'@types/react-dom':
|
'@types/react-dom':
|
||||||
optional: true
|
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':
|
'@radix-ui/react-presence@1.1.0':
|
||||||
resolution: {integrity: sha512-Gq6wuRN/asf9H/E/VzdKoUtT8GC9PQc9z40/vEr0VCJ4u5XvvhWIrSsCB6vD2/cH7ugTdSfYq9fLJCcM00acrQ==}
|
resolution: {integrity: sha512-Gq6wuRN/asf9H/E/VzdKoUtT8GC9PQc9z40/vEr0VCJ4u5XvvhWIrSsCB6vD2/cH7ugTdSfYq9fLJCcM00acrQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -1511,6 +1549,19 @@ packages:
|
|||||||
'@types/react-dom':
|
'@types/react-dom':
|
||||||
optional: true
|
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':
|
'@radix-ui/react-primitive@2.0.0':
|
||||||
resolution: {integrity: sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==}
|
resolution: {integrity: sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -1546,6 +1597,19 @@ packages:
|
|||||||
'@types/react':
|
'@types/react':
|
||||||
optional: true
|
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':
|
'@radix-ui/react-use-callback-ref@1.1.0':
|
||||||
resolution: {integrity: sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==}
|
resolution: {integrity: sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -1600,6 +1664,19 @@ packages:
|
|||||||
'@types/react':
|
'@types/react':
|
||||||
optional: true
|
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':
|
'@radix-ui/rect@1.1.0':
|
||||||
resolution: {integrity: sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==}
|
resolution: {integrity: sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==}
|
||||||
|
|
||||||
@ -6720,6 +6797,12 @@ snapshots:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@types/react': 18.3.3
|
'@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)':
|
'@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:
|
dependencies:
|
||||||
'@radix-ui/primitive': 1.1.0
|
'@radix-ui/primitive': 1.1.0
|
||||||
@ -6761,6 +6844,19 @@ snapshots:
|
|||||||
'@types/react': 18.3.3
|
'@types/react': 18.3.3
|
||||||
'@types/react-dom': 18.3.0
|
'@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)':
|
'@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:
|
dependencies:
|
||||||
'@radix-ui/primitive': 1.1.0
|
'@radix-ui/primitive': 1.1.0
|
||||||
@ -6854,6 +6950,16 @@ snapshots:
|
|||||||
'@types/react': 18.3.3
|
'@types/react': 18.3.3
|
||||||
'@types/react-dom': 18.3.0
|
'@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)':
|
'@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:
|
dependencies:
|
||||||
'@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.3)(react@18.3.1)
|
'@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': 18.3.3
|
||||||
'@types/react-dom': 18.3.0
|
'@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)':
|
'@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:
|
dependencies:
|
||||||
'@radix-ui/react-slot': 1.1.0(@types/react@18.3.3)(react@18.3.1)
|
'@radix-ui/react-slot': 1.1.0(@types/react@18.3.3)(react@18.3.1)
|
||||||
@ -6897,6 +7013,26 @@ snapshots:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@types/react': 18.3.3
|
'@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)':
|
'@radix-ui/react-use-callback-ref@1.1.0(@types/react@18.3.3)(react@18.3.1)':
|
||||||
dependencies:
|
dependencies:
|
||||||
react: 18.3.1
|
react: 18.3.1
|
||||||
@ -6937,6 +7073,15 @@ snapshots:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@types/react': 18.3.3
|
'@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': {}
|
'@radix-ui/rect@1.1.0': {}
|
||||||
|
|
||||||
'@remix-run/cloudflare-pages@2.10.2(@cloudflare/workers-types@4.20240620.0)(typescript@5.5.2)':
|
'@remix-run/cloudflare-pages@2.10.2(@cloudflare/workers-types@4.20240620.0)(typescript@5.5.2)':
|
||||||
|
1
worker-configuration.d.ts
vendored
1
worker-configuration.d.ts
vendored
@ -2,6 +2,7 @@ interface Env {
|
|||||||
ANTHROPIC_API_KEY: string;
|
ANTHROPIC_API_KEY: string;
|
||||||
OPENAI_API_KEY: string;
|
OPENAI_API_KEY: string;
|
||||||
GROQ_API_KEY: string;
|
GROQ_API_KEY: string;
|
||||||
|
HuggingFace_API_KEY: string;
|
||||||
OPEN_ROUTER_API_KEY: string;
|
OPEN_ROUTER_API_KEY: string;
|
||||||
OLLAMA_API_BASE_URL: string;
|
OLLAMA_API_BASE_URL: string;
|
||||||
OPENAI_LIKE_API_KEY: string;
|
OPENAI_LIKE_API_KEY: string;
|
||||||
|
Loading…
Reference in New Issue
Block a user