'use client' import styles from './agents.module.css'; import Image from 'next/image'; import Link from 'next/link'; import useSWR from 'swr'; import { useEffect, useState } from 'react'; import { useAuthenticatedData, UserProfile } from '../common/auth'; import { Button } from '@/components/ui/button'; import { Lightbulb, Robot, Aperture, GraduationCap, Jeep, Island, MathOperations, Asclepius, Couch, Code, Atom, ClockCounterClockwise, PaperPlaneTilt, Info, UserCircle } from "@phosphor-icons/react"; import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'; import { Dialog, DialogClose, DialogContent, DialogFooter, DialogHeader, DialogTrigger } from '@/components/ui/dialog'; import { Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerTitle, DrawerTrigger } from '@/components/ui/drawer'; import LoginPrompt from '../components/loginPrompt/loginPrompt'; import Loading, { InlineLoading } from '../components/loading/loading'; import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'; interface IconMap { [key: string]: (color: string, width: string, height: string) => JSX.Element | null; } const iconMap: IconMap = { Lightbulb: (color: string, width: string, height: string) => , Robot: (color: string, width: string, height: string) => , Aperture: (color: string, width: string, height: string) => , GraduationCap: (color: string, width: string, height: string) => , Jeep: (color: string, width: string, height: string) => , Island: (color: string, width: string, height: string) => , MathOperations: (color: string, width: string, height: string) => , Asclepius: (color: string, width: string, height: string) => , Couch: (color: string, width: string, height: string) => , Code: (color: string, width: string, height: string) => , Atom: (color: string, width: string, height: string) => , ClockCounterClockwise: (color: string, width: string, height: string) => , }; export interface AgentData { slug: string; avatar: string; name: string; personality: string; color: string; icon: string; } async function openChat(slug: string, userData: UserProfile | null) { const unauthenticatedRedirectUrl = `/login?next=/agents?agent=${slug}`; if (!userData) { window.location.href = unauthenticatedRedirectUrl; return; } const response = await fetch(`/api/chat/sessions?agent_slug=${slug}`, { method: "POST" }); if (response.status == 200) { window.location.href = `/chat`; } else if (response.status == 403 || response.status == 401) { window.location.href = unauthenticatedRedirectUrl; } else { alert("Failed to start chat session"); } } const agentsFetcher = () => window.fetch('/api/agents').then(res => res.json()).catch(err => console.log(err)); interface AgentCardProps { data: AgentData; userProfile: UserProfile | null; isMobileWidth: boolean; } function getIconFromIconName(iconName: string, color: string = 'gray', width: string = 'w-8', height: string = 'h-8') { const icon = iconMap[iconName]; const colorName = color.toLowerCase(); const colorClass = convertColorToTextClass(colorName); return icon ? icon(colorClass, width, height) : null; } function convertColorToClass(color: string) { // We can't dyanmically generate the classes for tailwindcss, so we have to explicitly use the whole string. // See models/__init__.py 's definition of the Agent model for the color choices. if (color === 'red') return `bg-red-500 hover:bg-red-600`; if (color === 'yellow') return `bg-yellow-500 hover:bg-yellow-600`; if (color === 'green') return `bg-green-500 hover:bg-green-600`; if (color === 'blue') return `bg-blue-500 hover:bg-blue-600`; if (color === 'orange') return `bg-orange-500 hover:bg-orange-600`; if (color === 'purple') return `bg-purple-500 hover:bg-purple-600`; if (color === 'pink') return `bg-pink-500 hover:bg-pink-600`; if (color === 'teal') return `bg-teal-500 hover:bg-teal-600`; if (color === 'cyan') return `bg-cyan-500 hover:bg-cyan-600`; if (color === 'lime') return `bg-lime-500 hover:bg-lime-600`; if (color === 'indigo') return `bg-indigo-500 hover:bg-indigo-600`; if (color === 'fuschia') return `bg-fuschia-500 hover:bg-fuschia-600`; if (color === 'rose') return `bg-rose-500 hover:bg-rose-600`; if (color === 'sky') return `bg-sky-500 hover:bg-sky-600`; if (color === 'amber') return `bg-amber-500 hover:bg-amber-600`; if (color === 'emerald') return `bg-emerald-500 hover:bg-emerald-600`; return `bg-gray-500 hover:bg-gray-600`; } function convertColorToTextClass(color: string) { if (color === 'red') return `text-red-500`; if (color === 'yellow') return `text-yellow-500`; if (color === 'green') return `text-green-500`; if (color === 'blue') return `text-blue-500`; if (color === 'orange') return `text-orange-500`; if (color === 'purple') return `text-purple-500`; if (color === 'pink') return `text-pink-500`; if (color === 'teal') return `text-teal-500`; if (color === 'cyan') return `text-cyan-500`; if (color === 'lime') return `text-lime-500`; if (color === 'indigo') return `text-indigo-500`; if (color === 'fuschia') return `text-fuschia-500`; if (color === 'rose') return `text-rose-500`; if (color === 'sky') return `text-sky-500`; if (color === 'amber') return `text-amber-500`; if (color === 'emerald') return `text-emerald-500`; return `text-gray-500`; } function AgentCard(props: AgentCardProps) { const searchParams = new URLSearchParams(window.location.search); const agentSlug = searchParams.get('agent'); const [showModal, setShowModal] = useState(agentSlug === props.data.slug); const [showLoginPrompt, setShowLoginPrompt] = useState(false); const userData = props.userProfile; if (showModal) { window.history.pushState({}, `Khoj AI - Agent ${props.data.slug}`, `/agents?agent=${props.data.slug}`); } const stylingString = convertColorToClass(props.data.color); return ( { showLoginPrompt && } { !props.isMobileWidth ? { setShowModal(!showModal); window.history.pushState({}, `Khoj AI - Agents`, `/agents`); }}>
{ getIconFromIconName(props.data.icon, props.data.color) || {props.data.name} } {props.data.name}
{ getIconFromIconName(props.data.icon, props.data.color) || {props.data.name} } {props.data.name}
{props.data.personality}
: { setShowModal(open); window.history.pushState({}, `Khoj AI - Agents`, `/agents`); }}>
{ getIconFromIconName(props.data.icon, props.data.color) || {props.data.name} } {props.data.name}
{props.data.name} Full Prompt {props.data.personality} Done
}
{ props.userProfile ? : }
) } export default function Agents() { const { data, error } = useSWR('agents', agentsFetcher, { revalidateOnFocus: false }); const authenticatedData = useAuthenticatedData(); const [isMobileWidth, setIsMobileWidth] = useState(false); const [showLoginPrompt, setShowLoginPrompt] = useState(false); useEffect(() => { if (typeof window !== 'undefined') { setIsMobileWidth(window.innerWidth < 768); } window.addEventListener('resize', () => { setIsMobileWidth(window.innerWidth < 768); }); }, []); if (error) { return (
Talk to a Specialized Agent
Error loading agents
); } if (!data) { return (
Talk to a Specialized Agent
booting up your agents
); } return (

Agents

{ showLoginPrompt && } How this works You can use any of these specialized agents to tailor to tune your conversation to your needs. { !authenticatedData && <>
}
Coming Soon: Support for making your own agents.
{data.map(agent => ( ))}
); }