Skip to content

Commit 0707dc2

Browse files
authored
Merge pull request #4 from etrobot/dev
0.2.0
2 parents fd8be4e + 26d934a commit 0707dc2

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+1279
-361
lines changed

app-icon.png

152 KB
Loading

app/api/agents/route.ts

-7
This file was deleted.

app/api/search/route.ts

-7
This file was deleted.

app/favicon.ico

-10.3 KB
Binary file not shown.

app/globals.css

+26-36
Original file line numberDiff line numberDiff line change
@@ -2,72 +2,55 @@
22
@tailwind components;
33
@tailwind utilities;
44

5+
56
@layer base {
67
:root {
78
--background: 0 0% 100%;
89
--foreground: 240 10% 3.9%;
9-
10-
--muted: 240 4.8% 95.9%;
11-
--muted-foreground: 240 3.8% 46.1%;
12-
13-
--popover: 0 0% 100%;
14-
--popover-foreground: 240 10% 3.9%;
15-
1610
--card: 0 0% 100%;
1711
--card-foreground: 240 10% 3.9%;
18-
19-
--border: 240 5.9% 90%;
20-
--input: 240 5.9% 90%;
21-
12+
--popover: 0 0% 100%;
13+
--popover-foreground: 240 10% 3.9%;
2214
--primary: 240 5.9% 10%;
2315
--primary-foreground: 0 0% 98%;
24-
2516
--secondary: 240 4.8% 95.9%;
2617
--secondary-foreground: 240 5.9% 10%;
27-
18+
--muted: 240 4.8% 95.9%;
19+
--muted-foreground: 240 3.8% 46.1%;
2820
--accent: 240 4.8% 95.9%;
29-
--accent-foreground: ;
30-
21+
--accent-foreground: 240 5.9% 10%;
3122
--destructive: 0 84.2% 60.2%;
3223
--destructive-foreground: 0 0% 98%;
33-
34-
--ring: 240 5% 64.9%;
35-
24+
--border: 240 5.9% 90%;
25+
--input: 240 5.9% 90%;
26+
--ring: 240 5.9% 10%;
3627
--radius: 0.5rem;
3728
}
3829

3930
.dark {
4031
--background: 240 10% 3.9%;
4132
--foreground: 0 0% 98%;
42-
43-
--muted: 240 3.7% 15.9%;
44-
--muted-foreground: 240 5% 64.9%;
45-
46-
--popover: 240 10% 3.9%;
47-
--popover-foreground: 0 0% 98%;
48-
4933
--card: 240 10% 3.9%;
5034
--card-foreground: 0 0% 98%;
51-
52-
--border: 240 3.7% 15.9%;
53-
--input: 240 3.7% 15.9%;
54-
35+
--popover: 240 10% 3.9%;
36+
--popover-foreground: 0 0% 98%;
5537
--primary: 0 0% 98%;
5638
--primary-foreground: 240 5.9% 10%;
57-
5839
--secondary: 240 3.7% 15.9%;
5940
--secondary-foreground: 0 0% 98%;
60-
41+
--muted: 240 3.7% 15.9%;
42+
--muted-foreground: 240 5% 64.9%;
6143
--accent: 240 3.7% 15.9%;
62-
--accent-foreground: ;
63-
44+
--accent-foreground: 0 0% 98%;
6445
--destructive: 0 62.8% 30.6%;
65-
--destructive-foreground: 0 85.7% 97.3%;
66-
67-
--ring: 240 3.7% 15.9%;
46+
--destructive-foreground: 0 0% 98%;
47+
--border: 240 3.7% 15.9%;
48+
--input: 240 3.7% 15.9%;
49+
--ring: 240 4.9% 83.9%;
6850
}
6951
}
7052

53+
7154
@layer base {
7255
* {
7356
@apply border-border;
@@ -76,3 +59,10 @@
7659
@apply bg-background text-foreground;
7760
}
7861
}
62+
63+
.title {
64+
background: linear-gradient(to right, orange,#10b981);
65+
-webkit-background-clip: text;
66+
color: transparent;
67+
display: inline-block;
68+
}

app/layout.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import '@/app/globals.css'
66
import { cn } from '@/lib/utils'
77
import { TailwindIndicator } from '@/components/tailwind-indicator'
88
import { Providers } from '@/components/providers'
9-
import { Header } from '@/components/header'
9+
import Header from '@/components/header'
1010

1111
export const metadata = {
1212
metadataBase: new URL(`http://${process.env.VERCEL_URL}`),

components.json

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"$schema": "https://ui.shadcn.com/schema.json",
3+
"style": "default",
4+
"rsc": true,
5+
"tsx": true,
6+
"tailwind": {
7+
"config": "tailwind.config.ts",
8+
"css": "app/globals.css",
9+
"baseColor": "green",
10+
"cssVariables": true,
11+
"prefix": ""
12+
},
13+
"aliases": {
14+
"components": "@/components",
15+
"utils": "@/lib/utils"
16+
}
17+
}

components/agents.tsx

+227
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
'use client'
2+
import { UseChatHelpers } from 'ai/react'
3+
import {
4+
Card,
5+
CardContent,
6+
CardDescription,
7+
CardFooter,
8+
CardHeader,
9+
CardTitle,
10+
} from "@/components/ui/card"
11+
import { Button } from "@/components/ui/button"
12+
import {
13+
Dialog,
14+
DialogContent,
15+
DialogDescription,
16+
DialogFooter,
17+
DialogHeader,
18+
DialogTitle
19+
} from '@/components/ui/dialog'
20+
import { Input } from './ui/input'
21+
import { Label } from "@/components/ui/label"
22+
import { useState, useTransition } from 'react'
23+
import {
24+
AlertDialog,
25+
AlertDialogAction,
26+
AlertDialogCancel,
27+
AlertDialogContent,
28+
AlertDialogDescription,
29+
AlertDialogFooter,
30+
AlertDialogHeader,
31+
AlertDialogTitle
32+
} from '@/components/ui/alert-dialog'
33+
import { Checkbox } from "@/components/ui/checkbox"
34+
import { IconSpinner, IconTrash } from '@/components/ui/icons'
35+
import { Textarea } from "@/components/ui/textarea"
36+
import { IconEdit,IconPlus } from '@/components/ui/icons'
37+
import { useRouter } from 'next/navigation'
38+
import { ExternalLink } from '@/components/external-link'
39+
function getRandomColor(): string {
40+
const letters = '0123456789ABCDEF';
41+
let color = '#';
42+
for (let i = 0; i < 6; i++) {
43+
color += letters[Math.floor(Math.random() * 16)];
44+
}
45+
return color;
46+
}
47+
48+
interface Agent {
49+
id: string;
50+
name: string;
51+
prompt: string;
52+
}
53+
54+
export default function Agents({ setInput }: Pick<UseChatHelpers, 'setInput'>) {
55+
const router=useRouter()
56+
const [editorOpen, setEditorOpen] = useState(false)
57+
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false)
58+
const [isRemovePending, startRemoveTransition] = useTransition()
59+
const [currentAgent, setCurrentAgent] = useState({ id: '', name: '', prompt: '' })
60+
const [agents, setAgents] = useState(() => {
61+
const storedAgents = localStorage.getItem('Agents');
62+
if (storedAgents) {
63+
return JSON.parse(storedAgents);
64+
} else {
65+
const newAgent = `{"#666666":{"name":"Search","prompt":"Get Info from Internet[//]: (ReAct-Tools)"},"#666777":{"name":"CoT","prompt":"Let's think step by step."}}`
66+
localStorage.setItem('Agents', newAgent);
67+
return JSON.parse(newAgent); // or you can return an empty object {} if that's the desired initial state
68+
}
69+
});
70+
const [usetool, setUsetool] = useState(false) // Load agents from localStorage or initialize as an empty object
71+
72+
// Function to open the editor with the selected agent's details
73+
const handleEditAgent = (agentId:string) => {
74+
setCurrentAgent({ ...agents[agentId], id: agentId })
75+
setEditorOpen(true)
76+
}
77+
78+
// Function to handle saving the current agent to the local state and localStorage
79+
const handleSaveAgents = () => {
80+
const tool = '[//]: (ReAct-Tools)'
81+
var prmt=currentAgent.prompt
82+
if(usetool){
83+
if(!prmt.endsWith(tool)){
84+
prmt=prmt+tool
85+
}
86+
}else{
87+
if(prmt.endsWith(tool)){
88+
prmt=prmt.replace(tool,'')
89+
}
90+
}
91+
const updatedAgents = {
92+
...agents,
93+
[currentAgent.id]: { name: currentAgent.name, prompt:prmt }
94+
}
95+
setAgents(updatedAgents)
96+
localStorage.setItem('Agents', JSON.stringify(updatedAgents)) // Save to localStorage
97+
setEditorOpen(false) // Close the editor
98+
router.replace('/')
99+
router.refresh()
100+
}
101+
102+
// Function to open the editor for creating a new agent
103+
const handleNewAgent = () => {
104+
setCurrentAgent({ id: getRandomColor(), name: '', prompt: '' })
105+
setEditorOpen(true)
106+
}
107+
108+
return (
109+
<>
110+
<div className="flex flex-wrap gap-4 m-4">
111+
{Object.entries(agents).map(([key, agent]) => (
112+
<>
113+
<Card key={key} className="w-[300px] h-[200px]">
114+
<CardHeader>
115+
<CardTitle>{(agent as Agent).name}</CardTitle>
116+
<CardDescription className='h-[64px]'>{(agent as Agent).prompt.slice(0,70)+' ...'}</CardDescription>
117+
</CardHeader>
118+
<CardFooter className="flex gap-2">
119+
<Button onClick={() => handleEditAgent(key)}><IconEdit/></Button>
120+
<Button variant="outline" onClick={() => setInput(`@${(agent as Agent).name} `)}>@</Button>
121+
<Button
122+
variant="ghost"
123+
className="ml-auto size-6 p-0 hover:bg-background"
124+
disabled={isRemovePending}
125+
onClick={() => setDeleteDialogOpen(true)}
126+
>
127+
<IconTrash />
128+
<span className="sr-only">Delete</span>
129+
</Button>
130+
</CardFooter>
131+
</Card>
132+
<AlertDialog open={deleteDialogOpen} onOpenChange={setDeleteDialogOpen}>
133+
<AlertDialogContent>
134+
<AlertDialogHeader>
135+
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
136+
<AlertDialogDescription>
137+
This will permanently delete your chat message and remove your
138+
data from our servers.
139+
</AlertDialogDescription>
140+
</AlertDialogHeader>
141+
<AlertDialogFooter>
142+
<AlertDialogCancel disabled={isRemovePending}>
143+
Cancel
144+
</AlertDialogCancel>
145+
<AlertDialogAction
146+
disabled={isRemovePending}
147+
onClick={() => {
148+
const updatedAgents = { ...agents }
149+
delete updatedAgents[key] // Remove the agent from the object
150+
setAgents(updatedAgents) // Update local state
151+
localStorage.setItem('Agents', JSON.stringify(updatedAgents)) // Update localStorage
152+
}
153+
}
154+
>
155+
{isRemovePending && <IconSpinner className="mr-2 animate-spin" />}
156+
Delete
157+
</AlertDialogAction>
158+
</AlertDialogFooter>
159+
</AlertDialogContent>
160+
</AlertDialog>
161+
</>
162+
))}
163+
<Dialog open={editorOpen} onOpenChange={setEditorOpen}>
164+
<DialogContent className="sm:max-w-xl">
165+
<DialogHeader>
166+
<DialogTitle>Edit Agent</DialogTitle>
167+
</DialogHeader>
168+
<div className="grid gap-2 py-4">
169+
<div className="grid grid-cols-5 items-center gap-2">
170+
<Label htmlFor="name" className="text-right">
171+
* Name
172+
</Label>
173+
<Input className="col-span-4"
174+
value={currentAgent.name}
175+
placeholder="Input an Agent Name"
176+
onChange={(e) => {
177+
const newName = e.target.value;
178+
// const usernamePattern = /^[A-Za-z][A-Za-z0-9_-]{2,15}$/;
179+
// if (usernamePattern.test(newName)) {
180+
setCurrentAgent({ ...currentAgent, name: newName });
181+
// }
182+
}}
183+
/>
184+
</div>
185+
<div className="grid grid-cols-5 items-center gap-2">
186+
<Label htmlFor="name" className="text-right">
187+
Prompt
188+
</Label>
189+
<Textarea className="col-span-4 h-[200px]"
190+
value={currentAgent.prompt}
191+
placeholder="Agent System Role Prompt"
192+
onChange={(e) => setCurrentAgent({ ...currentAgent, prompt: e.target.value })}
193+
/>
194+
</div>
195+
</div>
196+
<DialogFooter className="items-center">
197+
<Checkbox id="usetool" checked={usetool} onCheckedChange={()=>setUsetool(!usetool)}/>
198+
<label
199+
htmlFor="terms"
200+
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
201+
>
202+
Use Tools
203+
</label>
204+
<Button onClick={handleSaveAgents}>Save Agent</Button>
205+
</DialogFooter>
206+
</DialogContent>
207+
</Dialog>
208+
<Card className="w-[300px] h-[200px] text-center">
209+
<button className="mt-20" onClick={handleNewAgent}>+ New Agent</button>
210+
</Card>
211+
</div>
212+
<div className="mx-auto px-4 text-center mt-12">
213+
<p className="mb-2 leading-normal text-muted-foreground">
214+
This is an open source AI app built with{' '}
215+
<ExternalLink href="https://nextjs.org">▲ Next.js</ExternalLink> and{' '}
216+
<ExternalLink href="https://js.langchain.com/docs">
217+
LangChain.js 🦜🔗
218+
</ExternalLink>
219+
.
220+
</p>
221+
<p className="leading-normal text-muted-foreground">
222+
You can add your own agents and use '@' to mention them for conversation.
223+
</p>
224+
</div>
225+
</>
226+
)
227+
}

components/chat-history.tsx

+2-4
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,9 @@ interface ChatHistoryProps {
1111
userId?: string
1212
}
1313

14-
export async function ChatHistory({ userId }: ChatHistoryProps) {
14+
export function ChatHistory({ userId }: ChatHistoryProps) {
1515
return (
16-
<div className="flex flex-col h-full">
17-
<div className="px-2 my-4">
18-
</div>
16+
<div className="flex flex-col h-full pt-3">
1917
<React.Suspense
2018
fallback={
2119
<div className="flex flex-col flex-1 px-4 space-y-4 overflow-auto">

0 commit comments

Comments
 (0)