forked from aws-samples/amplify-next-template
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #268 from cabcookie:add-ai-features
Implementiere einen einfachen Chatbot ohne Spezialwissen
- Loading branch information
Showing
31 changed files
with
4,214 additions
and
1,364 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
{ | ||
// Use IntelliSense to learn about possible attributes. | ||
// Hover to view descriptions of existing attributes. | ||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 | ||
"version": "0.2.0", | ||
"configurations": [ | ||
{ | ||
"type": "chrome", | ||
"request": "launch", | ||
"name": "Launch Chrome against localhost", | ||
"url": "http://localhost:3000", | ||
"webRoot": "${workspaceFolder}" | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
import { Schema } from "@/amplify/data/resource"; | ||
import { createAIHooks } from "@aws-amplify/ui-react-ai"; | ||
import { generateClient } from "aws-amplify/api"; | ||
import { flow, identity, sortBy } from "lodash/fp"; | ||
import useSWR from "swr"; | ||
import { handleApiErrors } from "./globals"; | ||
const client = generateClient<Schema>({ authMode: "userPool" }); | ||
|
||
export const { useAIConversation } = createAIHooks(client); | ||
|
||
type Message = { | ||
content: { | ||
text?: string; | ||
}[]; | ||
}; | ||
|
||
const fetchConversations = async () => { | ||
const { data, errors } = await client.conversations.generalChat.list(); | ||
if (errors) { | ||
handleApiErrors(errors); | ||
throw errors; | ||
} | ||
return flow( | ||
identity<Schema["generalChat"]["type"][]>, | ||
sortBy((c) => -new Date(c.updatedAt).getTime()) | ||
)(data); | ||
}; | ||
|
||
export const useGeneralChat = () => { | ||
const { | ||
data: conversations, | ||
error, | ||
isLoading, | ||
mutate, | ||
} = useSWR("/api/chat/general", fetchConversations); | ||
|
||
const createConversation = async () => { | ||
const { data, errors } = await client.conversations.generalChat.create({}); | ||
if (errors) handleApiErrors(errors); | ||
if (data) mutate([...(conversations || []), data]); | ||
return data || undefined; | ||
}; | ||
|
||
const updateConversation = async ( | ||
conversation: Partial<Schema["generalChat"]["type"]> & { id: string } | ||
) => { | ||
const updated = conversations?.map((c) => | ||
c.id !== conversation.id ? c : { ...c, ...conversation } | ||
); | ||
if (updated) mutate(updated, false); | ||
const { data, errors } = | ||
await client.conversations.generalChat.update(conversation); | ||
if (errors) handleApiErrors(errors, "Error updating conversation"); | ||
if (updated) mutate(updated); | ||
return data; | ||
}; | ||
|
||
const deleteConversation = async (conversationId: string) => { | ||
const updated = conversations?.filter((c) => c.id !== conversationId); | ||
if (updated) mutate(updated, false); | ||
const { data, errors } = await client.conversations.generalChat.delete({ | ||
id: conversationId, | ||
}); | ||
if (errors) handleApiErrors(errors, "Error deleting conversation"); | ||
if (updated) mutate(updated); | ||
return data; | ||
}; | ||
|
||
const setConversationName = async ( | ||
conversationId: string, | ||
messages: Message[] | ||
) => { | ||
if (!messages.length) return; | ||
const name = await generateChatName(messages); | ||
return updateConversation({ id: conversationId, name }); | ||
}; | ||
|
||
const generateChatName = async (messages: Message[]) => { | ||
const content = messages | ||
.map((m) => m.content.map((c) => c.text ?? "").join("")) | ||
.join("\n"); | ||
const { data, errors } = await client.generations.chatNamer({ | ||
content, | ||
}); | ||
if (errors) handleApiErrors(errors, "Error generating chat name"); | ||
return data?.name ?? ""; | ||
}; | ||
|
||
return { | ||
conversations, | ||
error, | ||
isLoading, | ||
createConversation, | ||
setConversationName, | ||
deleteConversation, | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
import { useAIConversation, useGeneralChat } from "@/api/useGeneralChat"; | ||
import useCurrentUser from "@/api/useUser"; | ||
import { AIConversation, Avatars, SendMessage } from "@aws-amplify/ui-react-ai"; | ||
import { FC, useEffect, useState } from "react"; | ||
import ReactMarkdown from "react-markdown"; | ||
import CircleProfileImg from "../profile/CircleProfileImg"; | ||
|
||
interface ChatConversationProps { | ||
chatId: string; | ||
} | ||
|
||
const ChatConversation: FC<ChatConversationProps> = ({ chatId }) => { | ||
const [ | ||
{ | ||
data: { messages, conversation }, | ||
messages: errors, | ||
hasError, | ||
isLoading, | ||
}, | ||
sendMessage, | ||
] = useAIConversation("generalChat", { id: chatId }); | ||
const { setConversationName } = useGeneralChat(); | ||
const { user } = useCurrentUser(); | ||
const [avatars, setAvatars] = useState<Avatars | undefined>(); | ||
const [messageRenderer] = useState< | ||
Parameters<typeof AIConversation>[number]["messageRenderer"] | ||
>({ text: ({ text }) => <ReactMarkdown>{text}</ReactMarkdown> }); | ||
|
||
useEffect(() => { | ||
if (!user) return; | ||
setAvatars({ | ||
user: { | ||
username: user.userName, | ||
avatar: ( | ||
<CircleProfileImg | ||
user={user} | ||
fallbackInitials="US" | ||
className="w-8 h-8" | ||
/> | ||
), | ||
}, | ||
}); | ||
}, [user]); | ||
|
||
const handleSendMessage: SendMessage = async (message) => { | ||
if (!chatId) return; | ||
if (!conversation) return; | ||
await sendMessage(message); | ||
if (conversation.name) return; | ||
await setConversationName(chatId, [...messages, message]); | ||
}; | ||
|
||
return ( | ||
<> | ||
<AIConversation | ||
{...{ | ||
handleSendMessage, | ||
isLoading, | ||
messages, | ||
messageRenderer, | ||
avatars, | ||
}} | ||
/> | ||
{hasError && | ||
errors?.map((error, index) => ( | ||
<div key={index} className="text-sm p-2 text-red-600 font-semibold"> | ||
{error.message} | ||
</div> | ||
))} | ||
</> | ||
); | ||
}; | ||
|
||
export default ChatConversation; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
import { useContextContext } from "@/contexts/ContextContext"; | ||
import { | ||
NavMenuContextProvider, | ||
useNavMenuContext, | ||
} from "@/contexts/NavMenuContext"; | ||
import { addKeyDownListener } from "@/helpers/keyboard-events/main-layout"; | ||
import Head from "next/head"; | ||
import { useRouter } from "next/router"; | ||
import { FC, ReactNode, useEffect, useState } from "react"; | ||
import Header from "../header/Header"; | ||
import CreateInboxItemDialog from "../inbox/CreateInboxItemDialog"; | ||
import NavigationMenu from "../navigation-menu/NavigationMenu"; | ||
import { SidebarProvider, SidebarTrigger } from "../ui/sidebar"; | ||
import { Toaster } from "../ui/toaster"; | ||
import ConversationsSidebar from "./misc/ConversationsSidebar"; | ||
|
||
export type ChatLayoutProps = { | ||
children: ReactNode; | ||
}; | ||
|
||
const ChatLayoutInner: FC<ChatLayoutProps> = ({ children }) => { | ||
const { toggleMenu } = useNavMenuContext(); | ||
const [isOpen, setIsOpen] = useState(false); | ||
const { context: storedContext, setContext } = useContextContext(); | ||
const context = storedContext || "family"; | ||
const router = useRouter(); | ||
|
||
useEffect( | ||
() => | ||
addKeyDownListener(router, setContext, toggleMenu, () => setIsOpen(true)), | ||
[router, setContext, toggleMenu] | ||
); | ||
|
||
return ( | ||
<> | ||
<Head> | ||
<title>Impulso – Chat</title> | ||
</Head> | ||
<div className="flex flex-col items-center justify-center w-full"> | ||
<Header context={context} /> | ||
<NavigationMenu /> | ||
<main className="w-full"> | ||
<div className="flex flex-col px-2 lg:pr-4 mb-4 md:mb-8"> | ||
<SidebarProvider> | ||
<ConversationsSidebar /> | ||
<div className="w-full px-0 mx-0"> | ||
<header className="sticky top-12 md:top-16 py-1 z-40 bg-bgTransparent"> | ||
<SidebarTrigger /> | ||
</header> | ||
<div>{children}</div> | ||
</div> | ||
</SidebarProvider> | ||
</div> | ||
<Toaster /> | ||
<CreateInboxItemDialog open={isOpen} onOpenChange={setIsOpen} /> | ||
</main> | ||
</div> | ||
</> | ||
); | ||
}; | ||
|
||
const ChatLayout: FC<ChatLayoutProps> = (props) => ( | ||
<NavMenuContextProvider> | ||
<ChatLayoutInner {...props} /> | ||
</NavMenuContextProvider> | ||
); | ||
|
||
export default ChatLayout; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.