mirror of
https://github.com/Mail-0/Zero.git
synced 2026-07-01 08:16:28 +00:00
Implement prompts dialog and integrate prompt management in brain functionality. Add new prompts for summarization and enhance the brain router to fetch prompts based on connection ID. Clean up unused imports and refactor components for better structure.
This commit is contained in:
@@ -293,7 +293,7 @@ const AutoLabelingSettings = () => {
|
||||
</ScrollArea>
|
||||
<DialogFooter className="mt-4">
|
||||
<div className="flex w-full justify-between">
|
||||
<Button variant="outline" size="sm">
|
||||
<Button onClick={handleToggleAutolabeling} variant="outline" size="sm">
|
||||
{brainState?.enabled ? 'Disable' : 'Enable'}
|
||||
</Button>
|
||||
<div className="flex gap-2">
|
||||
|
||||
@@ -5,7 +5,6 @@ import {
|
||||
FileText,
|
||||
Expand,
|
||||
Plus,
|
||||
GitBranchPlus,
|
||||
Maximize2 as LucideMaximize2,
|
||||
Minimize2 as LucideMinimize2,
|
||||
} from 'lucide-react';
|
||||
@@ -27,8 +26,8 @@ import {
|
||||
useRef,
|
||||
} from 'react';
|
||||
import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from '@/components/ui/tooltip';
|
||||
import { ArrowsPointingIn, ArrowsPointingOut, PanelLeftOpen, Paper, Phone } from '../icons/icons';
|
||||
import { ResizablePanelGroup, ResizablePanel, ResizableHandle } from '@/components/ui/resizable';
|
||||
import { ArrowsPointingIn, ArrowsPointingOut, PanelLeftOpen, Phone } from '../icons/icons';
|
||||
import { AI_SIDEBAR_COOKIE_NAME, SIDEBAR_COOKIE_MAX_AGE } from '@/lib/constants';
|
||||
import { StyledEmailAssistantSystemPrompt, AiChatPrompt } from '@/lib/prompts';
|
||||
import { usePathname, useSearchParams, useParams } from 'next/navigation';
|
||||
@@ -39,6 +38,7 @@ import { useTRPC } from '@/providers/query-provider';
|
||||
import { Tools } from '../../../server/src/types';
|
||||
import { useBilling } from '@/hooks/use-billing';
|
||||
import { PricingDialog } from './pricing-dialog';
|
||||
import { PromptsDialog } from './prompts-dialog';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { useLabels } from '@/hooks/use-labels';
|
||||
@@ -54,6 +54,133 @@ import Image from 'next/image';
|
||||
import { toast } from 'sonner';
|
||||
import Link from 'next/link';
|
||||
|
||||
interface ChatHeaderProps {
|
||||
onClose: () => void;
|
||||
onToggleFullScreen: () => void;
|
||||
onToggleViewMode: () => void;
|
||||
isFullScreen: boolean;
|
||||
isPopup: boolean;
|
||||
chatMessages: { remaining: number };
|
||||
isPro: boolean;
|
||||
onUpgrade: () => void;
|
||||
onNewChat: () => void;
|
||||
}
|
||||
|
||||
function ChatHeader({
|
||||
onClose,
|
||||
onToggleFullScreen,
|
||||
onToggleViewMode,
|
||||
isFullScreen,
|
||||
isPopup,
|
||||
chatMessages,
|
||||
isPro,
|
||||
onUpgrade,
|
||||
onNewChat,
|
||||
}: ChatHeaderProps) {
|
||||
return (
|
||||
<div className="relative flex items-center justify-between border-b border-[#E7E7E7] px-2.5 pb-[10px] pt-[13px] dark:border-[#252525]">
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button onClick={onClose} variant="ghost" className="md:h-fit md:px-2">
|
||||
<X className="dark:text-iconDark text-iconLight" />
|
||||
<span className="sr-only">Close chat</span>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Close chat</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
{isFullScreen ? (
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
onClick={onToggleFullScreen}
|
||||
variant="ghost"
|
||||
className="hidden md:flex md:h-fit md:px-2"
|
||||
>
|
||||
<ArrowsPointingIn className="dark:fill-iconDark fill-iconLight" />
|
||||
<span className="sr-only">Toggle view mode</span>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Remove full screen</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
) : (
|
||||
<>
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
onClick={onToggleFullScreen}
|
||||
variant="ghost"
|
||||
className="hidden md:flex md:h-fit md:px-2 [&>svg]:size-2"
|
||||
>
|
||||
<Expand className="dark:text-iconDark text-iconLight" />
|
||||
<span className="sr-only">Toggle view mode</span>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Go to full screen</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button onClick={onToggleViewMode} variant="ghost" className="md:h-fit md:px-2">
|
||||
{isPopup ? (
|
||||
<PanelLeftOpen className="dark:fill-iconDark fill-iconLight" />
|
||||
) : (
|
||||
<Phone className="dark:fill-iconDark fill-iconLight" />
|
||||
)}
|
||||
<span className="sr-only"></span>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Go to {isPopup ? 'sidebar' : 'popup'}</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</>
|
||||
)}
|
||||
|
||||
{!isPro && (
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild className="md:h-fit md:px-2">
|
||||
<div>
|
||||
<Gauge value={50 - chatMessages.remaining!} size="small" showValue={true} />
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>You've used {50 - chatMessages.remaining!} out of 50 chat messages.</p>
|
||||
<p className="mb-2">Upgrade for unlimited messages!</p>
|
||||
<Button onClick={onUpgrade} className="h-8 w-full">
|
||||
Upgrade
|
||||
</Button>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
)}
|
||||
|
||||
<PromptsDialog />
|
||||
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button onClick={onNewChat} variant="ghost" className="md:h-fit md:px-2">
|
||||
<Plus className="dark:text-iconDark text-iconLight" />
|
||||
<span className="sr-only">New chat</span>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>New chat</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface AISidebarProps {
|
||||
className?: string;
|
||||
}
|
||||
@@ -62,14 +189,14 @@ type ViewMode = 'sidebar' | 'popup' | 'fullscreen';
|
||||
|
||||
export function useAIFullScreen() {
|
||||
const [isFullScreenQuery, setIsFullScreenQuery] = useQueryState('isFullScreen');
|
||||
|
||||
|
||||
// Initialize isFullScreen state from query parameter or localStorage
|
||||
const [isFullScreen, setIsFullScreenState] = useState<boolean>(() => {
|
||||
// First check query parameter
|
||||
if (isFullScreenQuery) {
|
||||
return isFullScreenQuery === 'true';
|
||||
}
|
||||
|
||||
|
||||
// Then check localStorage if on client
|
||||
if (typeof window !== 'undefined') {
|
||||
const savedFullScreen = localStorage.getItem('ai-fullscreen');
|
||||
@@ -77,23 +204,23 @@ export function useAIFullScreen() {
|
||||
return savedFullScreen === 'true';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
|
||||
// Update both query parameter and localStorage when fullscreen state changes
|
||||
const setIsFullScreen = useCallback(
|
||||
(value: boolean) => {
|
||||
// Immediately update local state for faster UI response
|
||||
setIsFullScreenState(value);
|
||||
|
||||
|
||||
// For exiting fullscreen, we need to be extra careful to ensure state is updated properly
|
||||
if (!value) {
|
||||
// Force immediate removal from localStorage for faster response
|
||||
if (typeof window !== 'undefined') {
|
||||
localStorage.removeItem('ai-fullscreen');
|
||||
}
|
||||
|
||||
|
||||
// Use setTimeout to ensure the state update happens in the next tick
|
||||
// This helps prevent the need for double-clicking
|
||||
setTimeout(() => {
|
||||
@@ -102,7 +229,7 @@ export function useAIFullScreen() {
|
||||
} else {
|
||||
// For entering fullscreen, we can use the normal flow
|
||||
setIsFullScreenQuery('true').catch(console.error);
|
||||
|
||||
|
||||
// Save to localStorage for persistence across sessions
|
||||
if (typeof window !== 'undefined') {
|
||||
localStorage.setItem('ai-fullscreen', 'true');
|
||||
@@ -111,7 +238,7 @@ export function useAIFullScreen() {
|
||||
},
|
||||
[setIsFullScreenQuery],
|
||||
);
|
||||
|
||||
|
||||
// Sync with query parameter on mount or when it changes
|
||||
useEffect(() => {
|
||||
const queryValue = isFullScreenQuery === 'true';
|
||||
@@ -119,7 +246,7 @@ export function useAIFullScreen() {
|
||||
setIsFullScreenState(queryValue);
|
||||
}
|
||||
}, [isFullScreenQuery, isFullScreen]);
|
||||
|
||||
|
||||
// Initialize from localStorage on mount if query parameter is not set
|
||||
useEffect(() => {
|
||||
if (typeof window !== 'undefined' && !isFullScreenQuery) {
|
||||
@@ -128,13 +255,13 @@ export function useAIFullScreen() {
|
||||
setIsFullScreenQuery('true');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Force a re-render when exiting fullscreen mode
|
||||
if (isFullScreenQuery === null && isFullScreen) {
|
||||
setIsFullScreenState(false);
|
||||
}
|
||||
}, [isFullScreenQuery, setIsFullScreenQuery, isFullScreen]);
|
||||
|
||||
|
||||
return {
|
||||
isFullScreen,
|
||||
setIsFullScreen,
|
||||
@@ -145,11 +272,11 @@ export function useAISidebar() {
|
||||
const [open, setOpenQuery] = useQueryState('aiSidebar');
|
||||
const [viewModeQuery, setViewModeQuery] = useQueryState('viewMode');
|
||||
const { isFullScreen, setIsFullScreen } = useAIFullScreen();
|
||||
|
||||
|
||||
// Initialize viewMode from query parameter, localStorage, or default to 'sidebar'
|
||||
const [viewMode, setViewModeState] = useState<ViewMode>(() => {
|
||||
if (viewModeQuery) return viewModeQuery as ViewMode;
|
||||
|
||||
|
||||
// Check localStorage for saved state if on client
|
||||
if (typeof window !== 'undefined') {
|
||||
const savedViewMode = localStorage.getItem('ai-viewmode');
|
||||
@@ -157,7 +284,7 @@ export function useAISidebar() {
|
||||
return savedViewMode as ViewMode;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return 'popup';
|
||||
});
|
||||
|
||||
@@ -166,15 +293,15 @@ export function useAISidebar() {
|
||||
async (mode: ViewMode) => {
|
||||
setViewModeState(mode);
|
||||
await setViewModeQuery(mode === 'popup' ? null : mode);
|
||||
|
||||
|
||||
// Save to localStorage for persistence across sessions
|
||||
if (typeof window !== 'undefined') {
|
||||
localStorage.setItem('ai-viewmode', mode);
|
||||
}
|
||||
},
|
||||
[setViewModeQuery]
|
||||
[setViewModeQuery],
|
||||
);
|
||||
|
||||
|
||||
// Function to set open state and save to localStorage
|
||||
const setOpen = useCallback(
|
||||
(openState: boolean) => {
|
||||
@@ -184,7 +311,7 @@ export function useAISidebar() {
|
||||
if (typeof window !== 'undefined') {
|
||||
localStorage.removeItem('ai-sidebar-open');
|
||||
}
|
||||
|
||||
|
||||
// Use setTimeout to ensure the query update happens in the next tick
|
||||
// This helps prevent the need for double-clicking
|
||||
setTimeout(() => {
|
||||
@@ -193,24 +320,21 @@ export function useAISidebar() {
|
||||
} else {
|
||||
// For opening, we can use the normal flow
|
||||
setOpenQuery('true').catch(console.error);
|
||||
|
||||
|
||||
// Save to localStorage
|
||||
if (typeof window !== 'undefined') {
|
||||
localStorage.setItem('ai-sidebar-open', 'true');
|
||||
}
|
||||
}
|
||||
},
|
||||
[setOpenQuery]
|
||||
[setOpenQuery],
|
||||
);
|
||||
|
||||
|
||||
// Toggle open state
|
||||
const toggleOpen = useCallback(
|
||||
() => {
|
||||
const newState = !(open === 'true');
|
||||
setOpen(newState);
|
||||
},
|
||||
[open, setOpen]
|
||||
);
|
||||
const toggleOpen = useCallback(() => {
|
||||
const newState = !(open === 'true');
|
||||
setOpen(newState);
|
||||
}, [open, setOpen]);
|
||||
|
||||
// Initialize from localStorage on mount
|
||||
useEffect(() => {
|
||||
@@ -245,16 +369,16 @@ export function useAISidebar() {
|
||||
}
|
||||
|
||||
function AISidebar({ className }: AISidebarProps) {
|
||||
const {
|
||||
open,
|
||||
setOpen,
|
||||
viewMode,
|
||||
const {
|
||||
open,
|
||||
setOpen,
|
||||
viewMode,
|
||||
setViewMode,
|
||||
isFullScreen,
|
||||
setIsFullScreen,
|
||||
isFullScreen,
|
||||
setIsFullScreen,
|
||||
toggleViewMode,
|
||||
isSidebar,
|
||||
isPopup
|
||||
isPopup,
|
||||
} = useAISidebar();
|
||||
const [resetKey, setResetKey] = useState(0);
|
||||
const [showPricing, setShowPricing] = useState(false);
|
||||
@@ -369,173 +493,22 @@ function AISidebar({ className }: AISidebarProps) {
|
||||
>
|
||||
<div className={cn('h-[calc(98vh+6px)]', 'flex flex-col', '', className)}>
|
||||
<div className="flex h-full flex-col">
|
||||
<div className="relative flex items-center justify-between border-b border-[#E7E7E7] px-2.5 pb-[10px] pt-[13px] dark:border-[#252525]">
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setOpen(false);
|
||||
setIsFullScreen(false);
|
||||
}}
|
||||
variant="ghost"
|
||||
className="md:h-fit md:px-2"
|
||||
>
|
||||
<X className="dark:text-iconDark text-iconLight" />
|
||||
<span className="sr-only">Close chat</span>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Close chat</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
onClick={() => setIsFullScreen(!isFullScreen)}
|
||||
// onClick={toggleViewMode}
|
||||
variant="ghost"
|
||||
className="hidden md:flex md:h-fit md:px-2 [&>svg]:size-2"
|
||||
>
|
||||
<Expand className="dark:text-iconDark text-iconLight" />
|
||||
<span className="sr-only">Toggle view mode</span>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Go to full screen</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
onClick={toggleViewMode}
|
||||
variant="ghost"
|
||||
className="md:h-fit md:px-2"
|
||||
>
|
||||
{isFullScreen ? (
|
||||
<LucideMinimize2 className="dark:fill-iconDark fill-iconLight" />
|
||||
) : (
|
||||
<Phone className="dark:fill-iconDark fill-iconLight" />
|
||||
)}
|
||||
<span className="sr-only"></span>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Go to popup</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
|
||||
{!isPro && (
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild className="md:h-fit md:px-2">
|
||||
<div>
|
||||
<Gauge
|
||||
value={50 - chatMessages.remaining!}
|
||||
size="small"
|
||||
showValue={true}
|
||||
/>
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>
|
||||
You've used {50 - chatMessages.remaining!} out of 50 chat
|
||||
messages.
|
||||
</p>
|
||||
<p className="mb-2">Upgrade for unlimited messages!</p>
|
||||
<Button onClick={handleUpgrade} className="h-8 w-full">
|
||||
Upgrade
|
||||
</Button>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
)}
|
||||
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<Dialog>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<DialogTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="md:h-fit md:px-2 [&>svg]:size-3"
|
||||
>
|
||||
<Paper className="dark:fill-iconDark fill-iconLight h-3.5 w-3.5" />
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Prompts</TooltipContent>
|
||||
</Tooltip>
|
||||
<DialogContent showOverlay={true}>
|
||||
<DialogHeader>
|
||||
<DialogTitle>AI System Prompts</DialogTitle>
|
||||
<DialogDescription>
|
||||
We believe in Open Source, so we're open sourcing our AI system
|
||||
prompts. Soon you will be able to customize them to your liking.
|
||||
For now, here are the default prompts:
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="text-muted-foreground mb-1 mt-4 flex gap-2 text-sm">
|
||||
<span>Zero Chat / System Prompt</span>
|
||||
<Link
|
||||
href={'https://github.com/Mail-0/Zero.git'}
|
||||
target="_blank"
|
||||
className="flex items-center gap-1 underline"
|
||||
>
|
||||
<span>Contribute</span>
|
||||
<GitBranchPlus className="h-4 w-4" />
|
||||
</Link>
|
||||
</div>
|
||||
<Textarea
|
||||
className="min-h-60"
|
||||
readOnly
|
||||
value={AiChatPrompt('', '', '')}
|
||||
/>
|
||||
<div className="text-muted-foreground mb-1 mt-4 flex gap-2 text-sm">
|
||||
<span>Zero Compose / System Prompt</span>
|
||||
<Link
|
||||
href={'https://github.com/Mail-0/Zero.git'}
|
||||
target="_blank"
|
||||
className="flex items-center gap-1 underline"
|
||||
>
|
||||
<span>Contribute</span>
|
||||
<GitBranchPlus className="h-4 w-4" />
|
||||
</Link>
|
||||
</div>
|
||||
<Textarea
|
||||
className="min-h-60"
|
||||
readOnly
|
||||
value={StyledEmailAssistantSystemPrompt().trim()}
|
||||
/>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</TooltipProvider>
|
||||
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
onClick={handleNewChat}
|
||||
variant="ghost"
|
||||
className="md:h-fit md:px-2"
|
||||
>
|
||||
<Plus className="dark:text-iconDark text-iconLight" />
|
||||
<span className="sr-only">New chat</span>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>New chat</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
</div>
|
||||
<div className="b relative flex-1 overflow-hidden">
|
||||
<AIChat
|
||||
key={resetKey}
|
||||
{...chatState}
|
||||
// Pass the chat state to preserve conversation when switching between desktop/mobile
|
||||
/>
|
||||
<ChatHeader
|
||||
onClose={() => {
|
||||
setOpen(false);
|
||||
setIsFullScreen(false);
|
||||
}}
|
||||
onToggleFullScreen={() => setIsFullScreen(!isFullScreen)}
|
||||
onToggleViewMode={toggleViewMode}
|
||||
isFullScreen={isFullScreen}
|
||||
isPopup={isPopup}
|
||||
chatMessages={chatMessages}
|
||||
isPro={isPro}
|
||||
onUpgrade={handleUpgrade}
|
||||
onNewChat={handleNewChat}
|
||||
/>
|
||||
<div className="relative flex-1 overflow-hidden">
|
||||
<AIChat key={resetKey} {...chatState} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -547,20 +520,17 @@ function AISidebar({ className }: AISidebarProps) {
|
||||
<div
|
||||
className={cn(
|
||||
'fixed inset-0 z-50 flex items-center justify-center bg-transparent p-4 opacity-40 backdrop-blur-sm transition-opacity duration-150 hover:opacity-100 sm:inset-auto sm:bottom-4 sm:right-4 sm:flex-col sm:items-end sm:justify-end sm:p-0',
|
||||
'md:hidden', // Hide on md+ screens by default
|
||||
isPopup && !isFullScreen && 'md:flex', // Show on md+ screens when in popup mode
|
||||
isFullScreen && '!inset-0 !flex !p-0 !opacity-100 !backdrop-blur-none', // Full screen mode
|
||||
'md:hidden',
|
||||
isPopup && !isFullScreen && 'md:flex',
|
||||
isFullScreen && '!inset-0 !flex !p-0 !opacity-100 !backdrop-blur-none',
|
||||
)}
|
||||
>
|
||||
{/* Chat popup container - only visible on small screens or when popup mode is selected */}
|
||||
<div
|
||||
className={cn(
|
||||
'bg-panelLight dark:bg-panelDark w-full overflow-hidden rounded-2xl border border-[#E7E7E7] shadow-lg dark:border-[#252525]',
|
||||
'md:hidden', // Hide on md+ screens by default
|
||||
isPopup &&
|
||||
!isFullScreen &&
|
||||
'w-[600px] max-w-[90vw] sm:w-[400px] md:block', // Show on md+ screens when in popup mode
|
||||
isFullScreen && '!block !max-w-none !rounded-none !border-none', // Full screen mode
|
||||
'md:hidden',
|
||||
isPopup && !isFullScreen && 'w-[600px] max-w-[90vw] sm:w-[400px] md:block',
|
||||
isFullScreen && '!block !max-w-none !rounded-none !border-none',
|
||||
)}
|
||||
>
|
||||
<div
|
||||
@@ -569,182 +539,20 @@ function AISidebar({ className }: AISidebarProps) {
|
||||
isFullScreen ? 'h-screen' : 'h-[90vh] sm:h-[600px] sm:max-h-[85vh]',
|
||||
)}
|
||||
>
|
||||
<div className="relative flex items-center justify-between border-b border-[#E7E7E7] px-1 py-2 pb-1 dark:border-[#252525]">
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setOpen(false);
|
||||
setIsFullScreen(false);
|
||||
}}
|
||||
variant="ghost"
|
||||
className="md:h-fit md:px-2"
|
||||
>
|
||||
<X className="dark:text-iconDark text-iconLight" />
|
||||
<span className="sr-only">Close chat</span>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Close chat</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
{isFullScreen && (
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
onClick={() => setIsFullScreen(!isFullScreen)}
|
||||
variant="ghost"
|
||||
className="hidden md:flex md:h-fit md:px-2"
|
||||
>
|
||||
<ArrowsPointingIn className="dark:fill-iconDark fill-iconLight" />
|
||||
|
||||
<span className="sr-only">Toggle view mode</span>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Remove full screen</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
)}
|
||||
|
||||
{!isFullScreen && (
|
||||
<>
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
onClick={() => setIsFullScreen(!isFullScreen)}
|
||||
// onClick={toggleViewMode}
|
||||
variant="ghost"
|
||||
className="hidden md:flex md:h-fit md:px-2 [&>svg]:size-2"
|
||||
>
|
||||
<Expand className="dark:text-iconDark text-iconLight" />
|
||||
<span className="sr-only">Toggle view mode</span>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Go to full screen</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
onClick={toggleViewMode}
|
||||
variant="ghost"
|
||||
className="md:h-fit md:px-2"
|
||||
>
|
||||
<PanelLeftOpen className="dark:fill-iconDark fill-iconLight" />
|
||||
|
||||
<span className="sr-only"></span>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Go to sidebar</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</>
|
||||
)}
|
||||
|
||||
{!isPro && (
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild className="md:h-fit md:px-2">
|
||||
<div>
|
||||
<Gauge
|
||||
value={50 - chatMessages.remaining!}
|
||||
size="small"
|
||||
showValue={true}
|
||||
/>
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>
|
||||
You've used {50 - chatMessages.remaining!} out of 50 chat messages.
|
||||
</p>
|
||||
<p className="mb-2">Upgrade for unlimited messages!</p>
|
||||
<Button onClick={handleUpgrade} className="h-8 w-full">
|
||||
Upgrade
|
||||
</Button>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
)}
|
||||
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<Dialog>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<DialogTrigger asChild>
|
||||
<Button variant="ghost" className="md:h-fit md:px-2 [&>svg]:size-3">
|
||||
<Paper className="dark:fill-iconDark fill-iconLight h-3.5 w-3.5" />
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Prompts</TooltipContent>
|
||||
</Tooltip>
|
||||
<DialogContent showOverlay={true}>
|
||||
<DialogHeader>
|
||||
<DialogTitle>AI System Prompts</DialogTitle>
|
||||
<DialogDescription>
|
||||
We believe in Open Source, so we're open sourcing our AI system
|
||||
prompts. Soon you will be able to customize them to your liking. For
|
||||
now, here are the default prompts:
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="text-muted-foreground mb-1 mt-4 flex gap-2 text-sm">
|
||||
<span>Zero Chat / System Prompt</span>
|
||||
<Link
|
||||
href={'https://github.com/Mail-0/Zero.git'}
|
||||
target="_blank"
|
||||
className="flex items-center gap-1 underline"
|
||||
>
|
||||
<span>Contribute</span>
|
||||
<GitBranchPlus className="h-4 w-4" />
|
||||
</Link>
|
||||
</div>
|
||||
<Textarea
|
||||
className="min-h-60"
|
||||
readOnly
|
||||
value={AiChatPrompt('', '', '')}
|
||||
/>
|
||||
<div className="text-muted-foreground mb-1 mt-4 flex gap-2 text-sm">
|
||||
<span>Zero Compose / System Prompt</span>
|
||||
<Link
|
||||
href={'https://github.com/Mail-0/Zero.git'}
|
||||
target="_blank"
|
||||
className="flex items-center gap-1 underline"
|
||||
>
|
||||
<span>Contribute</span>
|
||||
<GitBranchPlus className="h-4 w-4" />
|
||||
</Link>
|
||||
</div>
|
||||
<Textarea
|
||||
className="min-h-60"
|
||||
readOnly
|
||||
value={StyledEmailAssistantSystemPrompt().trim()}
|
||||
/>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</TooltipProvider>
|
||||
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
onClick={handleNewChat}
|
||||
variant="ghost"
|
||||
className="md:h-fit md:px-2"
|
||||
>
|
||||
<Plus className="dark:text-iconDark text-iconLight" />
|
||||
<span className="sr-only">New chat</span>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>New chat</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
</div>
|
||||
<ChatHeader
|
||||
onClose={() => {
|
||||
setOpen(false);
|
||||
setIsFullScreen(false);
|
||||
}}
|
||||
onToggleFullScreen={() => setIsFullScreen(!isFullScreen)}
|
||||
onToggleViewMode={toggleViewMode}
|
||||
isFullScreen={isFullScreen}
|
||||
isPopup={isPopup}
|
||||
chatMessages={chatMessages}
|
||||
isPro={isPro}
|
||||
onUpgrade={handleUpgrade}
|
||||
onNewChat={handleNewChat}
|
||||
/>
|
||||
<div className="relative flex-1 overflow-hidden">
|
||||
<AIChat key={resetKey} {...chatState} />
|
||||
</div>
|
||||
|
||||
127
apps/mail/components/ui/prompts-dialog.tsx
Normal file
127
apps/mail/components/ui/prompts-dialog.tsx
Normal file
@@ -0,0 +1,127 @@
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from './dialog';
|
||||
import {
|
||||
BookDashedIcon,
|
||||
GitBranchPlus,
|
||||
MessageSquareIcon,
|
||||
RefreshCcwDotIcon,
|
||||
SendIcon,
|
||||
} from 'lucide-react';
|
||||
import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from '@/components/ui/tooltip';
|
||||
import { AiChatPrompt, StyledEmailAssistantSystemPrompt } from '@/lib/prompts';
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from './tabs';
|
||||
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import { useTRPC } from '@/providers/query-provider';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Paper } from '../icons/icons';
|
||||
import { Textarea } from './textarea';
|
||||
import Link from 'next/link';
|
||||
|
||||
export function PromptsDialog() {
|
||||
const trpc = useTRPC();
|
||||
const { data: prompts } = useQuery(trpc.brain.getPrompts.queryOptions());
|
||||
return (
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<Dialog>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<DialogTrigger asChild>
|
||||
<Button variant="ghost" className="md:h-fit md:px-2 [&>svg]:size-3">
|
||||
<Paper className="dark:fill-iconDark fill-iconLight h-3.5 w-3.5" />
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Prompts</TooltipContent>
|
||||
</Tooltip>
|
||||
<DialogContent className="max-w-screen-lg" showOverlay={true}>
|
||||
<DialogHeader>
|
||||
<DialogTitle className="flex items-center gap-2">
|
||||
ZeroAI System Prompts{' '}
|
||||
<Link
|
||||
href={'https://github.com/Mail-0/Zero.git'}
|
||||
target="_blank"
|
||||
className="flex items-center gap-1 text-xs underline"
|
||||
>
|
||||
<span>Contribute</span>
|
||||
<GitBranchPlus className="h-4 w-4" />
|
||||
</Link>
|
||||
</DialogTitle>
|
||||
<DialogDescription>
|
||||
We believe in Open Source, so we're open sourcing our AI system prompts. Soon you will
|
||||
be able to customize them to your liking.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<Tabs className="mt-2">
|
||||
<TabsList className="w-full justify-start">
|
||||
<TabsTrigger value="chat">
|
||||
<MessageSquareIcon className="mr-2 h-4 w-4" /> Chat
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="compose">
|
||||
<SendIcon className="mr-2 h-4 w-4" /> Compose
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="summarizeThread">
|
||||
<BookDashedIcon className="mr-2 h-4 w-4" /> Summarize Thread
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="reSummarizeThread">
|
||||
<RefreshCcwDotIcon className="mr-2 h-4 w-4" /> Re-Summarize Thread
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="summarizeMessage">
|
||||
<BookDashedIcon className="mr-2 h-4 w-4" /> Summarize Message
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="chat">
|
||||
<span className="text-muted-foreground mb-2 flex gap-2 text-sm">
|
||||
This system prompt is used in the chat sidebar agent. The agent has multiple tools
|
||||
available.
|
||||
</span>
|
||||
<Textarea className="min-h-60" readOnly value={AiChatPrompt('', '', '')} />
|
||||
</TabsContent>
|
||||
<TabsContent value="compose">
|
||||
<span className="text-muted-foreground mb-2 flex gap-2 text-sm">
|
||||
This system prompt is used to compose emails that sound like you.
|
||||
</span>
|
||||
<Textarea
|
||||
className="min-h-60"
|
||||
readOnly
|
||||
value={StyledEmailAssistantSystemPrompt().trim()}
|
||||
/>
|
||||
</TabsContent>
|
||||
{prompts ? (
|
||||
<TabsContent value="summarizeThread">
|
||||
<span className="text-muted-foreground mb-2 flex gap-2 text-sm">
|
||||
This system prompt is used to summarize threads. It takes the entire thread and
|
||||
key information and summarizes them.
|
||||
</span>
|
||||
<Textarea className="min-h-60" readOnly value={prompts?.SummarizeThread} />
|
||||
</TabsContent>
|
||||
) : null}
|
||||
{prompts ? (
|
||||
<TabsContent value="reSummarizeThread">
|
||||
<span className="text-muted-foreground mb-2 flex gap-2 text-sm">
|
||||
This system prompt is used to re-summarize threads. It's used when the thread
|
||||
messages change and a new context is needed.
|
||||
</span>
|
||||
<Textarea className="min-h-60" readOnly value={prompts?.ReSummarizeThread} />
|
||||
</TabsContent>
|
||||
) : null}
|
||||
{prompts ? (
|
||||
<TabsContent value="summarizeMessage">
|
||||
<span className="text-muted-foreground mb-2 flex gap-2 text-sm">
|
||||
This system prompt is used to summarize messages. It takes a single message and
|
||||
summarizes it.
|
||||
</span>
|
||||
<Textarea className="min-h-60" readOnly value={prompts?.SummarizeMessage} />
|
||||
</TabsContent>
|
||||
) : null}
|
||||
</Tabs>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</TooltipProvider>
|
||||
);
|
||||
}
|
||||
192
apps/server/src/lib/brain.fallback.prompts.ts
Normal file
192
apps/server/src/lib/brain.fallback.prompts.ts
Normal file
@@ -0,0 +1,192 @@
|
||||
export const SummarizeMessage = `
|
||||
<system_prompt>
|
||||
<role>You are a high-accuracy email summarization agent. Your task is to extract and summarize emails in XML format with absolute precision, ensuring no critical details are lost while maintaining high efficiency.</role>
|
||||
|
||||
<instructions>
|
||||
<extract>
|
||||
<item>Sender, recipient, and CC names (exclude email addresses)</item>
|
||||
<item>Exact date and time of the email</item>
|
||||
<item>All actionable details, including confirmations, requests, deadlines, and follow-ups</item>
|
||||
</extract>
|
||||
|
||||
<omit>
|
||||
<item>Email addresses</item>
|
||||
<item>Greetings, sign-offs, and generic pleasantries</item>
|
||||
<item>Unnecessary or redundant information</item>
|
||||
</omit>
|
||||
|
||||
<format>
|
||||
<item>Ensure structured, concise, and complete summaries</item>
|
||||
<item>No omissions, distortions, or misinterpretations</item>
|
||||
<item>Use parties names, never say "the recipient" or "the sender"</item>
|
||||
<item>If there are not additional details to add, do not add anything. Do not say "no additional details provided in the body of the email"</item>
|
||||
<item>If there is not content, say "None". do not say "no content" or "with no message content provided".</item>
|
||||
</format>
|
||||
</instructions>
|
||||
|
||||
<example_input>
|
||||
<message>
|
||||
<from>Josh</from>
|
||||
<to>Adam</to>
|
||||
<cc>Emily</cc>
|
||||
<date>2025-03-24T14:23:00</date>
|
||||
<subject>83(b) Election Mailing</subject>
|
||||
<body>Adam,
|
||||
|
||||
Nothing further needed on your end – I've asked our mail team to expedite the mailing of Adam's 83(b) election, which will go out tomorrow. I'll send the proof of mailing to YC after it is sent out and will separately confirm when done with you.
|
||||
|
||||
Best,
|
||||
Josh</body>
|
||||
</message>
|
||||
</example_input>
|
||||
|
||||
<expected_output>
|
||||
<summary>On Monday, March 24, at 2:23 PM, Josh informs Adam (CC: Emily) that no further action is required. The mail team will expedite the mailing of Adam's 83(b) election tomorrow. Josh will send the proof of mailing to YC and confirm separately with Adam once it is sent.</summary>
|
||||
</expected_output>
|
||||
|
||||
<strict_guidelines>Strictly follow these rules. No missing details. No extra fluff. Just precise, high-performance summarization. Never say "Here is"</strict_guidelines>
|
||||
</system_prompt>`;
|
||||
export const SummarizeThread = `
|
||||
<system_prompt>
|
||||
<role>You are a high-accuracy email thread summarization agent. Your task is to process a full email thread with multiple messages and generate a structured, limited-length summary that retains all critical details, ensuring no information is lost.</role>
|
||||
|
||||
<instructions>
|
||||
<input_structure>
|
||||
<item>Thread title</item>
|
||||
<item>List of participants (sender, recipients, CCs)</item>
|
||||
<item>Ordered sequence of messages, each containing:</item>
|
||||
<subitem>Sender name</subitem>
|
||||
<subitem>Timestamp (exact date and time)</subitem>
|
||||
<subitem>Message content</subitem>
|
||||
</input_structure>
|
||||
|
||||
<output_requirements>
|
||||
<item>Summarize each message concisely while preserving its exact meaning.</item>
|
||||
<item>Include all participants and timestamps for context.</item>
|
||||
<item>Use clear formatting to distinguish different messages.</item>
|
||||
<item>Ensure the summary is within the length limit while retaining all essential details.</item>
|
||||
<item>Do not add interpretations, assumptions, or extra context beyond what is provided.</item>
|
||||
</output_requirements>
|
||||
</instructions>
|
||||
|
||||
<example_input>
|
||||
<thread>
|
||||
<title>83(b) Election Mailing</title>
|
||||
<participants>
|
||||
<participant>Josh</participant>
|
||||
<participant>Adam</participant>
|
||||
<participant>Emily</participant>
|
||||
</participants>
|
||||
<messages>
|
||||
<message>
|
||||
<from>Josh</from>
|
||||
<to>Adam</to>
|
||||
<cc>Emily</cc>
|
||||
<date>2025-03-24T14:23:00</date>
|
||||
<body>Adam, nothing further needed on your end. I've asked our mail team to expedite the mailing of Adam's 83(b) election, which will go out tomorrow. I'll send the proof of mailing to YC after it is sent and will confirm separately with you.</body>
|
||||
</message>
|
||||
<message>
|
||||
<from>Adam</from>
|
||||
<to>Josh</to>
|
||||
<cc>Emily</cc>
|
||||
<date>2025-03-24T15:10:00</date>
|
||||
<body>Thanks, Josh. Please let me know once it's sent.</body>
|
||||
</message>
|
||||
<message>
|
||||
<from>Josh</from>
|
||||
<to>Adam</to>
|
||||
<cc>Emily</cc>
|
||||
<date>2025-03-25T09:45:00</date>
|
||||
<body>The mail team has sent out the 83(b) election. I've attached the proof of mailing. Let me know if you need anything else.</body>
|
||||
</message>
|
||||
</messages>
|
||||
</thread>
|
||||
</example_input>
|
||||
|
||||
<expected_output>
|
||||
<summary>
|
||||
Thread: 83(b) Election Mailing
|
||||
Participants: Josh, Adam, Emily
|
||||
|
||||
- March 24, 2:23 PM – Josh informs Adam (CC: Emily) that no further action is needed. The mail team will expedite the mailing of Adam's 83(b) election tomorrow. Proof of mailing will be sent to YC, and Josh will confirm separately.
|
||||
- March 24, 3:10 PM – Adam acknowledges Josh's message and requests confirmation once the mailing is sent.
|
||||
- March 25, 9:45 AM – Josh confirms that the 83(b) election has been sent and attaches proof of mailing. He asks if anything else is needed.
|
||||
</summary>
|
||||
</expected_output>
|
||||
|
||||
<strict_guidelines>Maintain absolute accuracy. No omissions. No extra assumptions. No distortions. Ensure clarity and brevity within the length limit.</strict_guidelines>
|
||||
<strict_guidelines>Do not include any notes or additional context beyond the summary.</strict_guidelines>
|
||||
<strict_guidelines>Never say "Here is"</strict_guidelines>
|
||||
</system_prompt>
|
||||
`;
|
||||
export const ReSummarizeThread = `
|
||||
<system_prompt>
|
||||
<role>You are a high-accuracy email thread summarization agent. Your task is to process a full email thread, including new messages and an existing summary, and generate a structured, limited-length updated summary that retains all critical details.</role>
|
||||
|
||||
<instructions>
|
||||
<input_structure>
|
||||
<item>Thread title</item>
|
||||
<item>List of participants (sender, recipients, CCs)</item>
|
||||
<item>Existing summary (if available)</item>
|
||||
<item>Ordered sequence of new messages, each containing:</item>
|
||||
<subitem>Sender name</subitem>
|
||||
<subitem>Timestamp (exact date and time)</subitem>
|
||||
<subitem>Message content</subitem>
|
||||
</input_structure>
|
||||
|
||||
<update_logic>
|
||||
<item>If an existing summary is provided, update it by integrating new messages while preserving all prior details.</item>
|
||||
<item>Maintain chronological order and ensure completeness.</item>
|
||||
<item>Summarize each new message concisely while preserving its exact meaning.</item>
|
||||
<item>Ensure clarity and readability by distinguishing different messages.</item>
|
||||
<item>Enforce a strict length limit while retaining all essential details.</item>
|
||||
</update_logic>
|
||||
|
||||
<strict_requirements>
|
||||
<item>No omissions, distortions, or assumptions.</item>
|
||||
<item>Do not modify or rewrite prior content except to append new updates.</item>
|
||||
<item>Ensure final summary remains structured and factual.</item>
|
||||
<item>Do not include any notes or additional context beyond the summary.</item>
|
||||
</strict_requirements>
|
||||
</instructions>
|
||||
|
||||
<example_input>
|
||||
<thread>
|
||||
<title>83(b) Election Mailing</title>
|
||||
<participants>
|
||||
<participant>Josh</participant>
|
||||
<participant>Adam</participant>
|
||||
<participant>Emily</participant>
|
||||
</participants>
|
||||
<existing_summary>
|
||||
Thread: 83(b) Election Mailing
|
||||
Participants: Josh, Adam, Emily
|
||||
|
||||
- March 24, 2:23 PM – Josh informs Adam (CC: Emily) that no further action is needed. The mail team will expedite the mailing of Adam's 83(b) election tomorrow. Proof of mailing will be sent to YC, and Josh will confirm separately.
|
||||
- March 24, 3:10 PM – Adam acknowledges Josh's message and requests confirmation once the mailing is sent.
|
||||
</existing_summary>
|
||||
<new_messages>
|
||||
<message>
|
||||
<from>Josh</from>
|
||||
<to>Adam</to>
|
||||
<cc>Emily</cc>
|
||||
<date>2025-03-25T09:45:00</date>
|
||||
<body>The mail team has sent out the 83(b) election. I've attached the proof of mailing. Let me know if you need anything else.</body>
|
||||
</message>
|
||||
</new_messages>
|
||||
</thread>
|
||||
</example_input>
|
||||
|
||||
<expected_output>
|
||||
<updated_summary>
|
||||
Thread: 83(b) Election Mailing
|
||||
Participants: Josh, Adam, Emily
|
||||
|
||||
- March 24, 2:23 PM – Josh informs Adam (CC: Emily) that no further action is needed. The mail team will expedite the mailing of Adam's 83(b) election tomorrow. Proof of mailing will be sent to YC, and Josh will confirm separately.
|
||||
- March 24, 3:10 PM – Adam acknowledges Josh's message and requests confirmation once the mailing is sent.
|
||||
- March 25, 9:45 AM – Josh confirms that the 83(b) election has been sent and attaches proof of mailing. He asks if anything else is needed.
|
||||
</updated_summary>
|
||||
</expected_output>
|
||||
|
||||
<strict_guidelines>Maintain absolute accuracy. No missing details. No extra assumptions. No modifications to previous content beyond appending updates. Ensure clarity and brevity within the length limit. Never say "Here is"</strict_guidelines>
|
||||
</system_prompt>`;
|
||||
@@ -1,4 +1,6 @@
|
||||
import { ReSummarizeThread, SummarizeMessage, SummarizeThread } from './brain.fallback.prompts';
|
||||
import { env } from 'cloudflare:workers';
|
||||
import { EPrompts } from '../types';
|
||||
|
||||
export const enableBrainFunction = async (connection: { id: string; providerId: string }) => {
|
||||
return await env.zero.subscribe({
|
||||
@@ -13,3 +15,39 @@ export const disableBrainFunction = async (connection: { id: string; providerId:
|
||||
providerId: connection.providerId,
|
||||
});
|
||||
};
|
||||
|
||||
const getPromptName = (connectionId: string, prompt: EPrompts) => {
|
||||
return `${connectionId}-${prompt}`;
|
||||
};
|
||||
|
||||
export const getPrompt = async (promptName: string, fallback: string) => {
|
||||
const existingPrompt = await env.prompts_storage.get(promptName);
|
||||
if (!existingPrompt) {
|
||||
await env.prompts_storage.put(promptName, fallback);
|
||||
return fallback;
|
||||
}
|
||||
return existingPrompt;
|
||||
};
|
||||
|
||||
export const getPrompts = async ({ connectionId }: { connectionId: string }) => {
|
||||
const prompts: Record<EPrompts, string> = {
|
||||
[EPrompts.SummarizeMessage]: '',
|
||||
[EPrompts.ReSummarizeThread]: '',
|
||||
[EPrompts.SummarizeThread]: '',
|
||||
// [EPrompts.ThreadLabels]: '',
|
||||
// [EPrompts.Chat]: '',
|
||||
};
|
||||
const fallbackPrompts = {
|
||||
[EPrompts.SummarizeMessage]: SummarizeMessage,
|
||||
[EPrompts.ReSummarizeThread]: ReSummarizeThread,
|
||||
[EPrompts.SummarizeThread]: SummarizeThread,
|
||||
// [EPrompts.ThreadLabels]: '',
|
||||
// [EPrompts.Chat]: '',
|
||||
};
|
||||
for (const promptType of Object.values(EPrompts)) {
|
||||
const promptName = getPromptName(connectionId, promptType);
|
||||
const prompt = await getPrompt(promptName, fallbackPrompts[promptType]);
|
||||
prompts[promptType] = prompt;
|
||||
}
|
||||
return prompts;
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { activeConnectionProcedure, brainServerAvailableMiddleware, router } from '../trpc';
|
||||
import { disableBrainFunction, enableBrainFunction } from '../../lib/brain';
|
||||
import { disableBrainFunction, enableBrainFunction, getPrompts } from '../../lib/brain';
|
||||
import { env } from 'cloudflare:workers';
|
||||
import { z } from 'zod';
|
||||
|
||||
@@ -105,6 +105,12 @@ export const brainRouter = router({
|
||||
return [];
|
||||
}
|
||||
}),
|
||||
getPrompts: activeConnectionProcedure
|
||||
.use(brainServerAvailableMiddleware)
|
||||
.query(async ({ ctx }) => {
|
||||
const connection = ctx.activeConnection;
|
||||
return await getPrompts({ connectionId: connection.id });
|
||||
}),
|
||||
updateLabels: activeConnectionProcedure
|
||||
.use(brainServerAvailableMiddleware)
|
||||
.input(
|
||||
|
||||
@@ -155,3 +155,11 @@ export enum Tools {
|
||||
}
|
||||
|
||||
export type AppContext = Context<{ Bindings: Env }>;
|
||||
|
||||
export enum EPrompts {
|
||||
SummarizeMessage = 'SummarizeMessage',
|
||||
ReSummarizeThread = 'ReSummarizeThread',
|
||||
SummarizeThread = 'SummarizeThread',
|
||||
// ThreadLabels = 'ThreadLabels',
|
||||
// Chat = 'Chat',
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user