Skip to content

Commit

Permalink
chore: updated strucutre of main page (#396)
Browse files Browse the repository at this point in the history
* updated strucutre of main page

* updated the names

* modifed main folder

* removed some variables from context and refined some names

* added different useEffect for each case

* added default model selection

* removed a console log
  • Loading branch information
ChoudharyHarish authored Nov 18, 2024
1 parent d086977 commit a3af665
Show file tree
Hide file tree
Showing 14 changed files with 896 additions and 1 deletion.
2 changes: 1 addition & 1 deletion frontend/src/router/DocsQA.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import ScreenFallbackLoader from '@/components/base/molecules/ScreenFallbackLoad
import DataHub from '@/screens/dashboard/docsqa/DataSources'
import NavBar from '@/screens/dashboard/docsqa/Navbar'
import Applications from '@/screens/dashboard/docsqa/Applications'
const DocsQA = lazy(() => import('@/screens/dashboard/docsqa'))
const DocsQA = lazy(() => import('@/screens/dashboard/docsqa/main'))
const DocsQAChatbot = lazy(() => import('@/screens/dashboard/docsqa/Chatbot'))
const DocsQASettings = lazy(() => import('@/screens/dashboard/docsqa/settings'))

Expand Down
44 changes: 44 additions & 0 deletions frontend/src/screens/dashboard/docsqa/main/DocsQA.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import React, { useState } from 'react'

import Spinner from '@/components/base/atoms/Spinner/Spinner'
import ApplicationModal from './components/ApplicationModal'
import NoCollections from '../NoCollections'
import { useDocsQAContext } from './context'
import ConfigSidebar from './components/ConfigSidebar'
import Chat from './components/Chat'

const DocsQA = () => {
const { selectedCollection, isCollectionsLoading } = useDocsQAContext()

const [isCreateApplicationModalOpen, setIsCreateApplicationModalOpen] =
useState(false)

return (
<>
{isCreateApplicationModalOpen && (
<ApplicationModal
isCreateApplicationModalOpen={isCreateApplicationModalOpen}
setIsCreateApplicationModalOpen={setIsCreateApplicationModalOpen}
/>
)}
<div className="flex gap-5 h-[calc(100vh-6.5rem)] w-full">
{isCollectionsLoading ? (
<div className="h-full w-full flex items-center">
<Spinner center big />
</div>
) : selectedCollection ? (
<>
<ConfigSidebar
setIsCreateApplicationModalOpen={setIsCreateApplicationModalOpen}
/>
<Chat />
</>
) : (
<NoCollections fullWidth />
)}
</div>
</>
)
}

export default DocsQA
27 changes: 27 additions & 0 deletions frontend/src/screens/dashboard/docsqa/main/components/Answer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React from 'react'

import SourceDocsPreview from '../../DocsQA/SourceDocsPreview'
import IconProvider from '@/components/assets/IconProvider'
import Markdown from 'react-markdown'
import { useDocsQAContext } from '../context'

const Answer = (props: any) => {
const { sourceDocs, answer } = useDocsQAContext()

return (
<div className="overflow-y-auto flex flex-col gap-4 mt-7 h-[calc(100%-70px)]">
<div className="max-h-[60%] h-full overflow-y-auto flex gap-4">
<div className="bg-indigo-400 w-6 h-6 rounded-full flex items-center justify-center mt-0.5">
<IconProvider icon="message" className="text-white" />
</div>
<div className="w-full font-inter text-base">
<div className="font-bold text-lg">Answer:</div>
<Markdown>{answer}</Markdown>
</div>
</div>
{sourceDocs && <SourceDocsPreview sourceDocs={sourceDocs} />}
</div>
)
}

export default Answer
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
import React, { useState } from 'react'

import { LightTooltip } from '@/components/base/atoms/Tooltip'
import { useCreateApplicationMutation } from '@/stores/qafoundry'
import notify from '@/components/base/molecules/Notify'
import Button from '@/components/base/atoms/Button'
import Modal from '@/components/base/atoms/Modal'
import Input from '@/components/base/atoms/Input'
import { useDocsQAContext } from '../context'

const ApplicationModal = (props: any) => {
const {
allEnabledModels,
selectedCollection,
modelConfig,
retrieverConfig,
selectedQueryModel,
selectedRetriever,
promptTemplate,
selectedQueryController,
} = useDocsQAContext()

const [createApplication, { isLoading: isCreateApplicationLoading }] =
useCreateApplicationMutation()

const { isCreateApplicationModalOpen, setIsCreateApplicationModalOpen } =
props

const [applicationName, setApplicationName] = useState('')
const [questions, setQuestions] = useState<string[]>([])

const pattern = /^[a-z][a-z0-9-]*$/
const isValidApplicationName = pattern.test(applicationName)

const createChatApplication = async (
applicationName: string,
questions: string[],
setApplicationName: (name: string) => void,
) => {
if (!applicationName) {
return notify('error', 'Application name is required')
}
const selectedModel = allEnabledModels.find(
(model: any) => model.name == selectedQueryModel,
)

try {
await createApplication({
name: `${applicationName}-rag-app`,
config: {
collection_name: selectedCollection,
model_configuration: {
name: selectedModel.name,
provider: selectedModel.provider,
...JSON.parse(modelConfig),
},
retriever_name: selectedRetriever?.name ?? '',
retriever_config: JSON.parse(retrieverConfig),
prompt_template: promptTemplate,
query_controller: selectedQueryController,
},
questions,
}).unwrap()
setApplicationName('')
setIsCreateApplicationModalOpen(false)
notify('success', 'Application created successfully')
} catch (err: any) {
notify('error', 'Failed to create application', err?.data?.detail)
}
}

return (
<Modal
open={isCreateApplicationModalOpen}
onClose={() => {
setApplicationName('')
setQuestions([])
setIsCreateApplicationModalOpen(false)
}}
>
<div className="modal-box">
<div className="text-center font-medium text-xl mb-2">
Create Application
</div>
<div>
<div className="text-sm">Enter the name of the application</div>
<Input
value={applicationName}
onChange={(e) => setApplicationName(e.target.value)}
className="py-1 input-sm mt-1"
placeholder="E.g. query-bot"
/>
{applicationName && !isValidApplicationName ? (
<div className="text-sm text-error mt-1">
Application name should start with a lowercase letter and can only
contain lowercase letters, numbers and hyphens
</div>
) : applicationName ? (
<div className="text-sm mt-1">
The application name will be generated as{' '}
<span className="font-medium">"{applicationName}-rag-app"</span>
</div>
) : (
<></>
)}
<div className="mt-2 text-sm">Questions (Optional)</div>
{questions.map((question: any, index: any) => (
<div className="flex items-center gap-2 mt-2 w-full">
<div className="flex-1">
<Input
key={index}
value={question}
onChange={(e) => {
const updatedQuestions = [...questions]
updatedQuestions[index] = e.target.value
setQuestions(updatedQuestions)
}}
className="py-1 input-sm w-full"
placeholder={`Question ${index + 1}`}
maxLength={100}
/>
</div>
<Button
icon="trash-alt"
className="btn-sm hover:bg-red-600 hover:border-white hover:text-white"
onClick={() => {
setQuestions(questions.filter((_, i) => i !== index))
}}
/>
</div>
))}
<LightTooltip
title={
questions.length === 4 ? 'Maximum 4 questions are allowed' : ''
}
size="fit"
>
<div className="w-fit">
<Button
text="Add Question"
white
disabled={questions.length == 4}
className="text-sm font-medium text-gray-1000 hover:bg-white mt-2"
onClick={() => {
if (questions.length < 4) {
setQuestions([...questions, ''])
}
}}
/>
</div>
</LightTooltip>
</div>
<div className="flex justify-end w-full mt-4 gap-2">
<Button
text="Cancel"
className="btn-sm"
onClick={() => {
setApplicationName('')
setQuestions([])
setIsCreateApplicationModalOpen(false)
}}
/>
<Button
text="Create"
className="btn-sm btn-neutral"
loading={isCreateApplicationLoading}
onClick={() =>
createChatApplication(
applicationName,
questions,
setApplicationName,
)
}
/>
</div>
</div>
</Modal>
)
}

export default ApplicationModal
37 changes: 37 additions & 0 deletions frontend/src/screens/dashboard/docsqa/main/components/Chat.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React, { useState } from 'react'

import Spinner from '@/components/base/atoms/Spinner'
import { useDocsQAContext } from '../context'
import PromptForm from './PromptForm'
import ErrorAnswer from './ErrorAnswer'
import Answer from './Answer'
import NoAnswer from './NoAnswer'

const Right = () => {
const { errorMessage, answer } = useDocsQAContext()

const [isRunningPrompt, setIsRunningPrompt] = useState(false)

return (
<div className="h-full border rounded-lg border-[#CEE0F8] w-[calc(100%-25rem)] bg-white p-4">
<PromptForm
isRunningPrompt={isRunningPrompt}
setIsRunningPrompt={setIsRunningPrompt}
/>
{answer ? (
<Answer />
) : isRunningPrompt ? (
<div className="overflow-y-auto flex flex-col justify-center items-center gap-2 h-[calc(100%-4.375rem)]">
<Spinner center medium />
<div className="text-center">Fetching Answer...</div>
</div>
) : errorMessage ? (
<ErrorAnswer />
) : (
<NoAnswer />
)}
</div>
)
}

export default Right
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import React from 'react'

import { MenuItem, Select } from '@mui/material'

interface ConfigProps {
title: string
placeholder: string
initialValue: string
data: any[] | undefined
handleOnChange: (e: any) => void
renderItem?: (e: any) => React.ReactNode
className?: string
}

const ConfigSelector = (props: ConfigProps) => {
const {
title,
placeholder,
initialValue,
data,
className,
handleOnChange,
renderItem,
} = props
return (
<div className={`flex justify-between items-center ${className}`}>
<div className="text-sm">{title}:</div>
<Select
value={initialValue}
onChange={(e) => handleOnChange(e)}
placeholder={placeholder + '...'}
sx={{
background: 'white',
height: '2rem',
width: '13.1875rem',
border: '1px solid #CEE0F8 !important',
outline: 'none !important',
'& fieldset': {
border: 'none !important',
},
}}
>
{data?.map((item: any) =>
renderItem ? (
renderItem(item)
) : (
<MenuItem value={item} key={item}>
{item}
</MenuItem>
),
)}
</Select>
</div>
)
}

export default ConfigSelector
Loading

0 comments on commit a3af665

Please sign in to comment.