Skip to content

Commit

Permalink
feat: modify data storage method
Browse files Browse the repository at this point in the history
  • Loading branch information
sunls24 committed Dec 7, 2024
1 parent 3f24368 commit 39791a4
Show file tree
Hide file tree
Showing 23 changed files with 738 additions and 1,514 deletions.
17 changes: 9 additions & 8 deletions app/api/chat/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,33 @@ import { getLocaleTime } from "@/lib/utils";
import { tools } from "@/app/api/chat/tools";
import { NextResponse } from "next/server";

export const runtime = "edge";

export async function POST(req: Request) {
const { messages, config } = await req.json();

try {
const result = await streamText({
topP: 1,
const result = streamText({
temperature: config.temperature,
model: getOpenAI(config.apiKey).chat(config.model),
system: config.systemPrompt ? systemPrompt() : undefined,
system: getSystem(config.systemPrompt),
messages: messages,
maxSteps: 6,
tools: Object.fromEntries(
Object.entries(tools).filter(([key]) => config.plugins[key] === true),
),
});

return result.toDataStreamResponse({
getErrorMessage: (err: any) => err.message ?? err.toString(),
});
return result.toTextStreamResponse();
} catch (err: any) {
return new NextResponse(err.message ?? err.toString(), { status: 500 });
}
}

function getSystem(prompt: any) {
if (prompt === true) {
return systemPrompt();
}
}

function systemPrompt(): string {
return `You are an AI assistant, your duty is to provide accurate and rigorous answers. When encountering questions that cannot be handled, you need to clearly inform and guide the user to propose new questions. Please reply in Chinese.
Current time: ${getLocaleTime()}`;
Expand Down
11 changes: 4 additions & 7 deletions app/api/chat/tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,19 @@ export const tools: Record<string, CoreTool> = {
}),
execute: async ({ keyword }) => {
console.log(`googleSearch: ${keyword}`);
const nothing = "nothing";

const apiKey = process.env.GOOGLE_API_KEY;
const engineId = process.env.GOOGLE_ENGINE_ID;
if (!apiKey || !engineId) {
console.log("googleSearch: apiKey or engineId is empty");
return nothing;
return "apiKey or engineId is empty";
}
try {
const res = await fetch(
`https://www.googleapis.com/customsearch/v1?&fields=items(title,link,snippet,pagemap/metatags(og:description))&key=${apiKey}&cx=${engineId}&q=${keyword}`,
);
const result = await res.json();
return result.items ?? nothing;
return (await res.json()).items;
} catch (err: any) {
console.log(`googleSearch: ${err.cause ?? err}`);
return nothing;
return (err.cause ?? err).toString();
}
},
}),
Expand Down
8 changes: 3 additions & 5 deletions app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
import "./globals.css";
import type { Metadata, Viewport } from "next";
import React from "react";
import { ThemeProvider } from "@/components/theme-provider";
import { Toaster } from "@/components/ui/sonner";
import { Analytics } from "@vercel/analytics/react";
import Umami from "@/components/umami";
import { ThemeProvider } from "next-themes";

export const metadata: Metadata = {
title: {
default: "NEXT AI",
template: "%s - NEXT AI",
},
description:
"一个简单而优雅的 AI 聊天程序,支持 Google 搜索函数调用, GPT-3.5-Turbo, GPT4, Gemini-Pro",
"一个简单而优雅的 AI 聊天程序,支持 Google 搜索工具调用, GPT-3.5-Turbo, GPT4, Tools, Image input",
appleWebApp: {
title: "NEXT AI",
},
Expand Down Expand Up @@ -41,15 +40,14 @@ export default function RootLayout({
</head>
<body>
<ThemeProvider
enableSystem
attribute="class"
defaultTheme="system"
enableSystem
disableTransitionOnChange
>
{children}
<Toaster richColors position="top-center" />
</ThemeProvider>
{process.env.VERCEL && <Analytics />}
<Umami />
</body>
</html>
Expand Down
51 changes: 27 additions & 24 deletions components/chat-body.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Message } from "ai";
import { emitter, mittKey } from "@/lib/mitt";
import { ScrollArea } from "@/components/ui/scroll-area";
import { Loader } from "lucide-react";
import Mounted from "@/components/mounted";

function ChatBody({
isLoading,
Expand Down Expand Up @@ -71,30 +72,32 @@ function ChatBody({
viewportClass="p-2 sm:p-4"
onScroll={(e) => onScroll(e.currentTarget)}
>
{messages.map((value, index) => {
return (
<div key={value.id}>
<ChatMsg
index={index}
msg={value}
deleteMsg={!isLoading ? deleteMsg : undefined}
editMsg={!isLoading ? editMsg : undefined}
reload={
!isLoading &&
index === messages.length - 1 &&
(index != 0 || value.role === "user")
? reload
: undefined
}
dot={
isLoading &&
index === messages.length - 1 &&
value.role === "assistant"
}
/>
</div>
);
})}
<Mounted>
{messages.map((value, index) => {
return (
<div key={value.id}>
<ChatMsg
index={index}
msg={value}
deleteMsg={!isLoading ? deleteMsg : undefined}
editMsg={!isLoading ? editMsg : undefined}
reload={
!isLoading &&
index === messages.length - 1 &&
(index != 0 || value.role === "user")
? reload
: undefined
}
dot={
isLoading &&
index === messages.length - 1 &&
value.role === "assistant"
}
/>
</div>
);
})}
</Mounted>
{isLoading && messages[messages.length - 1]?.role === "user" && (
<Loader className="animate-spin" strokeWidth={1.5} />
)}
Expand Down
8 changes: 6 additions & 2 deletions components/chat-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
import TooltipWrap from "@/components/tooltip-wrap";
import { cn } from "@/lib/utils";
import { useChatStore } from "@/lib/store/chat";
import { toast } from "sonner";

function ChatInput({
isLoading,
Expand All @@ -19,8 +20,8 @@ function ChatInput({
handleSubmit,
stop,
}: {
isLoading: boolean;
input: string;
isLoading: boolean;
setInput: React.Dispatch<React.SetStateAction<string>>;
handleInputChange: ChangeEventHandler<HTMLTextAreaElement>;
handleSubmit: ChangeEventHandler<HTMLFormElement>;
Expand Down Expand Up @@ -65,7 +66,10 @@ function ChatInput({
className="h-8"
size="icon"
variant="ghost"
onClick={resetSession}
onClick={() => {
resetSession();
toast.success("聊天已重置");
}}
>
<MessageCircleOff strokeWidth={1.5} size={22} />
</Button>
Expand Down
33 changes: 3 additions & 30 deletions components/chat-msg.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import Markdown from "@/components/markdown";
import { ChatGPT } from "@/components/svg";
import { copyToClipboard, fmtLocaleTime } from "@/lib/utils";
import { Button } from "@/components/ui/button";
import { Copy, Loader, Pencil, RefreshCw, Trash } from "lucide-react";
import { Copy, Pencil, RefreshCw, Trash } from "lucide-react";
import CommonEdit from "@/components/dialog/common-edit";
import { DOT_FLAG } from "@/lib/constants";
import { toast } from "sonner";
import ChatTool from "@/components/chat-tool";

function ChatMsg({
index,
Expand Down Expand Up @@ -67,7 +67,6 @@ function ChatMsg({
content={msg.content}
updateContent={(content) => {
editMsg(index, content);
toast.success("消息内容编辑成功");
}}
trigger={
<Button
Expand Down Expand Up @@ -108,33 +107,7 @@ function ChatMsg({
)}
{msg.toolInvocations &&
msg.toolInvocations.map((tool) => (
<div key={tool.toolCallId} className="max-w-full rounded-md border">
<div className="flex items-center p-2">
<span className="font-medium">谷歌搜索:</span>
{tool.args.keyword}
{tool.state !== "result" && (
<Loader
size={20}
className="ml-1 inline animate-spin"
strokeWidth={1.5}
/>
)}
</div>
{tool.state === "result" && (
<div className="space-y-2 border-t p-2">
{tool.result.map((v: any, i: number) => (
<a
key={i}
className="block cursor-pointer underline decoration-transparent underline-offset-4 transition-colors hover:text-blue-400 hover:decoration-blue-400"
href={v.link}
target="_blank"
>
{v.title}
</a>
))}
</div>
)}
</div>
<ChatTool key={tool.toolCallId} tool={tool} />
))}
<CreatedAt time={msg.createdAt} />
</div>
Expand Down
40 changes: 40 additions & 0 deletions components/chat-tool.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React from "react";
import { Loader } from "lucide-react";
import { ToolInvocation } from "ai";

// TODO: 暂时只支持谷歌搜索工具调用
function ChatTool({ tool }: { tool: ToolInvocation }) {
return (
<div className="max-w-full rounded-md border">
<div className="flex items-center p-2">
<span className="font-medium">谷歌搜索:</span>
{tool.args.keyword}
{tool.state !== "result" && (
<Loader
size={20}
className="ml-1 inline animate-spin"
strokeWidth={1.5}
/>
)}
</div>
{tool.state === "result" && (
<div className="space-y-2 border-t p-2">
{typeof tool.result === "string" && tool.result}
{Array.isArray(tool.result) &&
tool.result.map((v: any, i: number) => (
<a
key={i}
className="block w-fit cursor-pointer underline decoration-transparent underline-offset-4 transition-colors hover:text-blue-400 hover:decoration-blue-400"
href={v.link}
target="_blank"
>
{v.title}
</a>
))}
</div>
)}
</div>
);
}

export default ChatTool;
Loading

0 comments on commit 39791a4

Please sign in to comment.