Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor backend integration #133

Open
wants to merge 18 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
449f4c3
moved logging conversation to our Flask Backend to frontend api
Maxwell-Lindsey Mar 25, 2024
f52a9e7
Merge branch 'advanced-document-controls-ui' into refactor-backend-in…
Maxwell-Lindsey Mar 25, 2024
62345f6
Merge branch 'main' into refactor-backend-integration
Maxwell-Lindsey Mar 25, 2024
56fc52d
Merge branch 'main' into refactor-backend-integration
Maxwell-Lindsey Mar 26, 2024
9a0a786
fixed new implementation for logConversationToFlask
Maxwell-Lindsey Mar 26, 2024
bc6a67d
fixed response handling for calling logConversationToFlask
Maxwell-Lindsey Mar 30, 2024
fd786e6
now calls logConversation to Flask api instead of backend in NewChat.tsx
Maxwell-Lindsey Mar 30, 2024
155bb9d
refactored downloadConversationHistory to call frontend api instead o…
Maxwell-Lindsey Mar 30, 2024
0d5ccd6
Merge branch 'main' into refactor-backend-integration
Maxwell-Lindsey Apr 10, 2024
fb2e4d4
deleted MantineYourMaterialsTable; was completely commented out alrea…
Maxwell-Lindsey Apr 10, 2024
58af97c
deleted unused file handling code in the query analysis page; cleaned…
Maxwell-Lindsey Apr 10, 2024
4225d0a
refactored deleteDocuments call to use frontend api to call backend
Maxwell-Lindsey Apr 10, 2024
4082489
refactored canvas ingest; need to test when canvas ingest is back up …
Maxwell-Lindsey Apr 10, 2024
73eb72b
refactored mit ocw ingest; need to test when it is live again on backend
Maxwell-Lindsey Apr 10, 2024
ede64a0
swapped all flask urls to keys
Maxwell-Lindsey Apr 10, 2024
0c8f037
reverted document groups title to project files; fixed mistake from m…
Maxwell-Lindsey Apr 11, 2024
4e8fd78
Merge branch 'main' into refactor-backend-integration
Maxwell-Lindsey Apr 22, 2024
0589541
Merge branch 'main' into refactor-backend-integration
KastanDay May 16, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
734 changes: 711 additions & 23 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"@mozilla/readability": "^0.4.4",
"@next-auth/prisma-adapter": "^1.0.5",
"@prisma/client": "^4.11.0",
"@qdrant/js-client-rest": "^1.8.1",
"@supabase/supabase-js": "^2.38.4",
"@t3-oss/env-nextjs": "^0.2.1",
"@tabler/icons-react": "^2.17.0",
Expand All @@ -52,6 +53,7 @@
"@vercel/analytics": "^1.0.1",
"@vercel/edge-config": "^0.2.0",
"@vercel/kv": "^0.2.1",
"ai": "^3.1.2",
"aws-sdk": "^2.1380.0",
"axios": "^1.6.0",
"dayjs": "^1.11.7",
Expand All @@ -66,6 +68,7 @@
"next-auth": "^4.24.5",
"next-axiom": "^1.0.0-rc.1",
"next-i18next": "^13.2.2",
"openai": "^4.42.0",
"posthog-js": "^1.96.0",
"react": "18.2.0",
"react-beautiful-dnd": "^13.1.1",
Expand Down
61 changes: 61 additions & 0 deletions src/app/api/chat/openAI/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import OpenAI from 'openai'
import { OpenAIStream, StreamingTextResponse } from 'ai'
import { NextResponse } from 'next/server'
import { decrypt } from '~/utils/crypto'

export const runtime = 'edge'

export async function POST(req: Request) {
try {
// let { messages, apiKey } = await req.json()
// console.log('headers', req.headers);
// const headers = {
// 'Content-type': 'application/json;charset=UTF-8',
// 'Authorization': `Bearer ${apiKey}`,
// }
// const openai = new OpenAI({
// apiKey: apiKey,
// headers: headers,
// })

const authHeader = req.headers.get('authorization')
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return new Response(JSON.stringify({ error: 'Unauthorized' }), {
status: 401,
})
}
let apiKey = authHeader.substring(7)
const { messages } = await req.json()

if (!apiKey.startsWith('sk')) {
apiKey = (await decrypt(
apiKey,
process.env.NEXT_PUBLIC_SIGNING_KEY as string,
)) as string
console.log('apikey', apiKey)
}
if (!apiKey) {
apiKey = process.env.VLADS_OPENAI_KEY as string
}

const openai = new OpenAI({
apiKey,
})
const response = await openai.chat.completions.create({
model: 'gpt-3.5-turbo',
stream: true,
messages,
})

const stream = OpenAIStream(response)

return new StreamingTextResponse(stream)
} catch (error) {
if (error instanceof OpenAI.APIError) {
const { name, status, headers, message } = error
return NextResponse.json({ name, status, headers, message }, { status })
} else {
throw error
}
}
}
160 changes: 134 additions & 26 deletions src/components/Chat/Chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
IconBrain,
IconCreditCard,
IconAlertCircle,
IconSearch,
// IconArrowUpRight,
// IconFileTextAi,
// IconX,
Expand All @@ -29,7 +30,15 @@ import {
useRef,
useState,
} from 'react'
import { Button, Text } from '@mantine/core'
import {
Button,
Group,
Switch,
Text,
UnstyledButton,
createStyles,
rem,
} from '@mantine/core'
import { useTranslation } from 'next-i18next'

import { getEndpoint } from '@/utils/app/api'
Expand All @@ -42,6 +51,7 @@ import {
type Conversation,
type Message,
Content,
Action,
} from '@/types/chat'
import { type Plugin } from '@/types/plugin'

Expand Down Expand Up @@ -75,11 +85,19 @@ import { Montserrat } from 'next/font/google'
import { montserrat_heading, montserrat_paragraph } from 'fonts'
import { fetchImageDescription } from '~/pages/api/UIUC-api/fetchImageDescription'
import { State, processChunkWithStateMachine } from '~/utils/streamProcessing'
import { useFetchEnabledDocGroups } from '~/hooks/docGroupsQueries'

const montserrat_med = Montserrat({
weight: '500',
subsets: ['latin'],
})

const DEFAULT_DOCUMENT_GROUP = {
id: 'DocGroup-all',
name: 'All Documents', // This value can be stored in an env variable
checked: true,
}

export const Chat = memo(({ stopConversationRef, courseMetadata }: Props) => {
const { t } = useTranslation('chat')
const clerk_obj = useUser()
Expand All @@ -92,6 +110,16 @@ export const Chat = memo(({ stopConversationRef, courseMetadata }: Props) => {

const [inputContent, setInputContent] = useState<string>('')

const [enabledDocumentGroups, setEnabledDocumentGroups] = useState<string[]>(
[],
)

const {
data: docGroups,
isSuccess,
isError,
} = useFetchEnabledDocGroups(getCurrentPageName())

useEffect(() => {
if (
courseMetadata?.banner_image_s3 &&
Expand All @@ -117,6 +145,8 @@ export const Chat = memo(({ stopConversationRef, courseMetadata }: Props) => {
prompts,
showModelSettings,
isImg2TextLoading,
documentGroups,
tools,
},
handleUpdateConversation,
dispatch: homeDispatch,
Expand All @@ -132,6 +162,63 @@ export const Chat = memo(({ stopConversationRef, courseMetadata }: Props) => {
const chatContainerRef = useRef<HTMLDivElement>(null)
const textareaRef = useRef<HTMLTextAreaElement>(null)

useEffect(() => {
// console.log('isSuccess: ', isSuccess)
if (isSuccess) {
const documentGroupActions = [
DEFAULT_DOCUMENT_GROUP,
...(docGroups?.map((docGroup, index) => ({
id: `DocGroup-${index}`,
name: docGroup.name,
checked: false,
onTrigger: () => console.log(`${docGroup.name} triggered`),
})) || []),
]

// console.log('documentGroupActions: ', documentGroupActions)

// const toolsActions = ['Tool 1', 'Tool 2', 'Tool 3'].map(
// (tool, index) => ({
// // const toolsActions = tools.map((tool, index) => ({
// id: `tool-${index}`,
// title: tool,
// description: `Description for ${tool}`,
// group: 'Tools',
// checked: true,
// onTrigger: () => console.log(`${tool} triggered`),
// }),
// )

homeDispatch({
field: 'documentGroups',
value: [...documentGroupActions],
})
setEnabledDocumentGroups(
documentGroups
.filter((documentGroup) => documentGroup.checked)
.map((documentGroup) => documentGroup.name),
)

// homeDispatch({
// field: 'tools',
// value: [...toolsActions],
// })
// setEnabledTools(toolsActions.filter((tool) => tool.checked).map((tool) => tool.name))
}
}, [docGroups, isSuccess])

useEffect(() => {
setEnabledDocumentGroups(
documentGroups
.filter((action) => action.checked)
.map((action) => action.name),
)
}, [documentGroups])

useEffect(() => {
console.log('enabledDocumentGroups: ', enabledDocumentGroups)
}, [enabledDocumentGroups])

const onMessageReceived = async (conversation: Conversation) => {
// Log conversation to Supabase
try {
Expand All @@ -153,26 +240,28 @@ export const Chat = memo(({ stopConversationRef, courseMetadata }: Props) => {
}

try {
// Log conversation to our Flask Backend (especially Nomic)
const response = await fetch(
`https://flask-production-751b.up.railway.app/onResponseCompletion`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
course_name: getCurrentPageName(),
conversation: conversation,
}),
const response = await fetch(`/api/UIUC-api/logConversationToFlask`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
)
const data = await response.json()
if (!response.ok) throw new Error(data.message)
body: JSON.stringify({
course_name: getCurrentPageName(),
conversation: conversation,
}),
});

const data = await response.json();

if (!response.ok) {
throw new Error(data.message);
}

// console.log('new method worked frontend');

return data.success
} catch (error) {
console.error('Error in chat.tsx running onResponseCompletion():', error)
return false
console.error('Error in chat.tsx running onMessageReceived():', error);
}
}

Expand All @@ -186,6 +275,9 @@ export const Chat = memo(({ stopConversationRef, courseMetadata }: Props) => {
const imageContent = (message.content as Content[]).filter(
(content) => content.type === 'image_url',
)

// console.log("Image Content: ", imageContent)

if (imageContent.length > 0) {
homeDispatch({ field: 'isImg2TextLoading', value: true })

Expand Down Expand Up @@ -237,6 +329,7 @@ export const Chat = memo(({ stopConversationRef, courseMetadata }: Props) => {
message: Message,
selectedConversation: Conversation,
searchQuery: string,
documentGroups: string[],
) => {
if (getCurrentPageName() != 'gpt4') {
// Extract text from all user messages in the conversation
Expand All @@ -254,6 +347,7 @@ export const Chat = memo(({ stopConversationRef, courseMetadata }: Props) => {
getCurrentPageName(),
searchQuery,
token_limit,
documentGroups,
).then((curr_contexts) => {
message.contexts = curr_contexts as ContextWithMetadata[]
console.log('message.contexts: ', message.contexts)
Expand All @@ -263,7 +357,12 @@ export const Chat = memo(({ stopConversationRef, courseMetadata }: Props) => {

// THIS IS WHERE MESSAGES ARE SENT.
const handleSend = useCallback(
async (message: Message, deleteCount = 0, plugin: Plugin | null = null) => {
async (
message: Message,
deleteCount = 0,
plugin: Plugin | null = null,
enabledDocumentGroups: string[],
) => {
setCurrentMessage(message)
// New way with React Context API
// TODO: MOVE THIS INTO ChatMessage
Expand Down Expand Up @@ -303,6 +402,8 @@ export const Chat = memo(({ stopConversationRef, courseMetadata }: Props) => {

const controller = new AbortController()

// console.log("Made it to message image handling code in handleSend with message: ", message)

// Run image to text conversion, attach to Message object.
if (Array.isArray(message.content)) {
searchQuery = await handleImageContent(
Expand All @@ -315,7 +416,12 @@ export const Chat = memo(({ stopConversationRef, courseMetadata }: Props) => {
}

// Run context search, attach to Message object.
await handleContextSearch(message, selectedConversation, searchQuery)
await handleContextSearch(
message,
selectedConversation,
searchQuery,
enabledDocumentGroups,
)

const chatBody: ChatBody = {
model: updatedConversation.model,
Expand Down Expand Up @@ -541,7 +647,7 @@ export const Chat = memo(({ stopConversationRef, courseMetadata }: Props) => {
field: 'conversations',
value: updatedConversations,
})
console.log('updatedConversations: ', updatedConversations)
// console.log('updatedConversations: ', updatedConversations)
saveConversations(updatedConversations)
homeDispatch({ field: 'messageIsStreaming', value: false })
} catch (error) {
Expand Down Expand Up @@ -604,7 +710,7 @@ export const Chat = memo(({ stopConversationRef, courseMetadata }: Props) => {
;(currentMessage.content as Content[]).splice(imgDescIndex, 1)
}

handleSend(currentMessage, 2, null)
handleSend(currentMessage, 2, null, enabledDocumentGroups)
}
}, [currentMessage, handleSend])

Expand Down Expand Up @@ -820,7 +926,7 @@ export const Chat = memo(({ stopConversationRef, courseMetadata }: Props) => {

return (
<div className="overflow-wrap relative flex h-screen w-full flex-col overflow-hidden bg-white dark:bg-[#15162c]">
<div className="justify-center" style={{ height: '46px' }}>
<div className="justify-center" style={{ height: '40px' }}>
<ChatNavbar bannerUrl={bannerUrl as string} isgpt4={true} />
</div>
<div className="mt-10 flex-grow overflow-auto">
Expand Down Expand Up @@ -885,10 +991,10 @@ export const Chat = memo(({ stopConversationRef, courseMetadata }: Props) => {
</Text>
</div>
</div>
<div className="absolute bottom-4 left-0 ml-4 mt-4 animate-ping flex-col place-items-start text-left">
<div className="absolute bottom-4 left-0 animate-ping flex-col place-items-start text-left">
<IconArrowLeft
size={'36'}
className="mr-2 transform text-purple-500 transition-transform duration-500 ease-in-out hover:-translate-x-1"
className="transform text-purple-500 transition-transform duration-500 ease-in-out hover:-translate-x-1"
/>
</div>
</div>
Expand Down Expand Up @@ -920,6 +1026,8 @@ export const Chat = memo(({ stopConversationRef, courseMetadata }: Props) => {
handleSend(
editedMessage,
selectedConversation?.messages.length - index,
null,
enabledDocumentGroups,
)
}}
onImageUrlsUpdate={onImageUrlsUpdate}
Expand All @@ -939,7 +1047,7 @@ export const Chat = memo(({ stopConversationRef, courseMetadata }: Props) => {
textareaRef={textareaRef}
onSend={(message, plugin) => {
// setCurrentMessage(message)
handleSend(message, 0, plugin)
handleSend(message, 0, plugin, enabledDocumentGroups)
}}
onScrollDownClick={handleScrollDown}
onRegenerate={handleRegenerate}
Expand Down
Loading
Loading