Commit 50a006fd authored by Joel's avatar Joel

Merge branch 'feat/universal-chat-fe' into deploy/dev

parents 5456c7cf 36c84fb2
...@@ -20,6 +20,7 @@ import Tooltip from '@/app/components/base/tooltip' ...@@ -20,6 +20,7 @@ import Tooltip from '@/app/components/base/tooltip'
import { Markdown } from '@/app/components/base/markdown' import { Markdown } from '@/app/components/base/markdown'
import AutoHeightTextarea from '@/app/components/base/auto-height-textarea' import AutoHeightTextarea from '@/app/components/base/auto-height-textarea'
import Button from '@/app/components/base/button' import Button from '@/app/components/base/button'
import type { DataSet } from '@/models/datasets'
const Divider: FC<{ name: string }> = ({ name }) => { const Divider: FC<{ name: string }> = ({ name }) => {
const { t } = useTranslation() const { t } = useTranslation()
return <div className='flex items-center my-2'> return <div className='flex items-center my-2'>
...@@ -45,9 +46,10 @@ export type IAnswerProps = { ...@@ -45,9 +46,10 @@ export type IAnswerProps = {
answerIconClassName?: string answerIconClassName?: string
thoughts?: ThoughtItem[] thoughts?: ThoughtItem[]
isThinking?: boolean isThinking?: boolean
dataSets?: DataSet[]
} }
// The component needs to maintain its own state to control whether to display input component // The component needs to maintain its own state to control whether to display input component
const Answer: FC<IAnswerProps> = ({ item, feedbackDisabled = false, isHideFeedbackEdit = false, onFeedback, onSubmitAnnotation, displayScene = 'web', isResponsing, answerIconClassName, thoughts, isThinking }) => { const Answer: FC<IAnswerProps> = ({ item, feedbackDisabled = false, isHideFeedbackEdit = false, onFeedback, onSubmitAnnotation, displayScene = 'web', isResponsing, answerIconClassName, thoughts, isThinking, dataSets }) => {
const { id, content, more, feedback, adminFeedback, annotation: initAnnotation } = item const { id, content, more, feedback, adminFeedback, annotation: initAnnotation } = item
const [showEdit, setShowEdit] = useState(false) const [showEdit, setShowEdit] = useState(false)
const [loading, setLoading] = useState(false) const [loading, setLoading] = useState(false)
...@@ -196,6 +198,13 @@ const Answer: FC<IAnswerProps> = ({ item, feedbackDisabled = false, isHideFeedba ...@@ -196,6 +198,13 @@ const Answer: FC<IAnswerProps> = ({ item, feedbackDisabled = false, isHideFeedba
<div className='text-xs text-gray-500'>{t('appDebug.openingStatement.title')}</div> <div className='text-xs text-gray-500'>{t('appDebug.openingStatement.title')}</div>
</div> </div>
)} )}
{(thoughts && thoughts.length > 0) && (
<Thought
list={thoughts || []}
isThinking={isThinking}
dataSets={dataSets}
/>
)}
{(isResponsing && !content) {(isResponsing && !content)
? ( ? (
<div className='flex items-center justify-center w-6 h-5'> <div className='flex items-center justify-center w-6 h-5'>
...@@ -204,9 +213,6 @@ const Answer: FC<IAnswerProps> = ({ item, feedbackDisabled = false, isHideFeedba ...@@ -204,9 +213,6 @@ const Answer: FC<IAnswerProps> = ({ item, feedbackDisabled = false, isHideFeedba
) )
: ( : (
<div> <div>
{(thoughts && thoughts.length > 0) && (
<Thought list={thoughts || []} isThinking={isThinking} />
)}
<Markdown content={content} /> <Markdown content={content} />
</div> </div>
)} )}
......
...@@ -19,6 +19,8 @@ import VoiceInput from '@/app/components/base/voice-input' ...@@ -19,6 +19,8 @@ import VoiceInput from '@/app/components/base/voice-input'
import { Microphone01 } from '@/app/components/base/icons/src/vender/line/mediaAndDevices' import { Microphone01 } from '@/app/components/base/icons/src/vender/line/mediaAndDevices'
import { Microphone01 as Microphone01Solid } from '@/app/components/base/icons/src/vender/solid/mediaAndDevices' import { Microphone01 as Microphone01Solid } from '@/app/components/base/icons/src/vender/solid/mediaAndDevices'
import { XCircle } from '@/app/components/base/icons/src/vender/solid/general' import { XCircle } from '@/app/components/base/icons/src/vender/solid/general'
import type { DataSet } from '@/models/datasets'
export type IChatProps = { export type IChatProps = {
configElem?: React.ReactNode configElem?: React.ReactNode
chatList: IChatItem[] chatList: IChatItem[]
...@@ -48,6 +50,7 @@ export type IChatProps = { ...@@ -48,6 +50,7 @@ export type IChatProps = {
answerIconClassName?: string answerIconClassName?: string
isShowConfigElem?: boolean isShowConfigElem?: boolean
isThoughting?: boolean isThoughting?: boolean
dataSets?: DataSet[]
} }
const Chat: FC<IChatProps> = ({ const Chat: FC<IChatProps> = ({
...@@ -72,6 +75,7 @@ const Chat: FC<IChatProps> = ({ ...@@ -72,6 +75,7 @@ const Chat: FC<IChatProps> = ({
isShowSpeechToText, isShowSpeechToText,
answerIconClassName, answerIconClassName,
isShowConfigElem, isShowConfigElem,
dataSets,
}) => { }) => {
const { t } = useTranslation() const { t } = useTranslation()
const { notify } = useContext(ToastContext) const { notify } = useContext(ToastContext)
...@@ -170,6 +174,7 @@ const Chat: FC<IChatProps> = ({ ...@@ -170,6 +174,7 @@ const Chat: FC<IChatProps> = ({
answerIconClassName={answerIconClassName} answerIconClassName={answerIconClassName}
thoughts={thoughts} thoughts={thoughts}
isThinking={isThinking} isThinking={isThinking}
dataSets={dataSets}
/> />
} }
return <Question key={item.id} id={item.id} content={item.content} more={item.more} useCurrentUserAvatar={useCurrentUserAvatar} /> return <Question key={item.id} id={item.id} content={item.content} more={item.more} useCurrentUserAvatar={useCurrentUserAvatar} />
......
...@@ -5,20 +5,22 @@ import cn from 'classnames' ...@@ -5,20 +5,22 @@ import cn from 'classnames'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import type { ThoughtItem } from '../type' import type { ThoughtItem } from '../type'
import s from './style.module.css' import s from './style.module.css'
import { DataSet, Loading as LodingIcon, Search, ThoughtList, WebReader } from '@/app/components/base/icons/src/public/thought' import { DataSet as DataSetIcon, Loading as LodingIcon, Search, ThoughtList, WebReader } from '@/app/components/base/icons/src/public/thought'
import { ChevronDown } from '@/app/components/base/icons/src/vender/line/arrows' import { ChevronDown } from '@/app/components/base/icons/src/vender/line/arrows'
import type { DataSet } from '@/models/datasets'
// https://www.freecodecamp.org/news/how-to-write-a-regular-expression-for-a-url/ // https://www.freecodecamp.org/news/how-to-write-a-regular-expression-for-a-url/
const urlRegex = /(https:\/\/www\.|http:\/\/www\.|https:\/\/|http:\/\/)?[a-zA-Z]{2,}(\.[a-zA-Z]{2,})(\.[a-zA-Z]{2,})?\/[a-zA-Z0-9]{2,}|((https:\/\/www\.|http:\/\/www\.|https:\/\/|http:\/\/)?[a-zA-Z]{2,}(\.[a-zA-Z]{2,})(\.[a-zA-Z]{2,})?)|(https:\/\/www\.|http:\/\/www\.|https:\/\/|http:\/\/)?[a-zA-Z0-9]{2,}\.[a-zA-Z0-9]{2,}\.[a-zA-Z0-9]{2,}(\.[a-zA-Z0-9]{2,})?/gi const urlRegex = /(https:\/\/www\.|http:\/\/www\.|https:\/\/|http:\/\/)?[a-zA-Z]{2,}(\.[a-zA-Z]{2,})(\.[a-zA-Z]{2,})?\/[a-zA-Z0-9]{2,}|((https:\/\/www\.|http:\/\/www\.|https:\/\/|http:\/\/)?[a-zA-Z]{2,}(\.[a-zA-Z]{2,})(\.[a-zA-Z]{2,})?)|(https:\/\/www\.|http:\/\/www\.|https:\/\/|http:\/\/)?[a-zA-Z0-9]{2,}\.[a-zA-Z0-9]{2,}\.[a-zA-Z0-9]{2,}(\.[a-zA-Z0-9]{2,})?/gi
export type IThoughtProps = { export type IThoughtProps = {
list: ThoughtItem[] list: ThoughtItem[]
isThinking?: boolean isThinking?: boolean
dataSets?: DataSet[]
} }
const getIcon = (toolId: string) => { const getIcon = (toolId: string) => {
switch (toolId) { switch (toolId) {
case 'dataset': case 'dataset':
return <DataSet /> return <DataSetIcon />
case 'web_reader': case 'web_reader':
return <WebReader /> return <WebReader />
default: default:
...@@ -29,16 +31,20 @@ const getIcon = (toolId: string) => { ...@@ -29,16 +31,20 @@ const getIcon = (toolId: string) => {
const Thought: FC<IThoughtProps> = ({ const Thought: FC<IThoughtProps> = ({
list, list,
isThinking, isThinking,
dataSets,
}) => { }) => {
const { t } = useTranslation() const { t } = useTranslation()
const [isShowDetail, setIsShowDetail] = React.useState(false) const [isShowDetail, setIsShowDetail] = React.useState(false)
const getThoughtText = (item: ThoughtItem) => { const getThoughtText = (item: ThoughtItem) => {
try { try {
const input = JSON.parse(item.tool_input) const input = JSON.parse(item.tool_input)
switch (item.tool) { switch (item.tool) {
case 'dataset': case 'dataset':
// debugger // eslint-disable-next-line no-case-declarations
return t('explore.universalChat.thought.res.dataset', { input: input.dataset_id }) const datasetName = dataSets?.find(item => item.id === input.dataset_id)?.name || 'unknown dataset'
return t('explore.universalChat.thought.res.dataset').replace('{datasetName}', `<span class="text-gray-700">${datasetName}</span>`)
case 'web_reader': case 'web_reader':
return t(`explore.universalChat.thought.res.webReader.${!input.cursor ? 'normal' : 'hasPageInfo'}`).replace('{url}', `<a href="${input.url}" class="text-[#155EEF]">${input.url}</a>`) return t(`explore.universalChat.thought.res.webReader.${!input.cursor ? 'normal' : 'hasPageInfo'}`).replace('{url}', `<a href="${input.url}" class="text-[#155EEF]">${input.url}</a>`)
default: // google, wikipedia default: // google, wikipedia
......
...@@ -86,7 +86,7 @@ const VoiceInput = ({ ...@@ -86,7 +86,7 @@ const VoiceInput = ({
const formData = new FormData() const formData = new FormData()
formData.append('file', mp3File) formData.append('file', mp3File)
let url = '' let url = '/universal-chat/audio-to-text'
let isPublic = false let isPublic = false
if (params.token) { if (params.token) {
......
...@@ -518,7 +518,6 @@ const Main: FC<IMainProps> = () => { ...@@ -518,7 +518,6 @@ const Main: FC<IMainProps> = () => {
draft.push({ ...questionItem }) draft.push({ ...questionItem })
draft.push({ ...responseItem }) draft.push({ ...responseItem })
}) })
// console.log('start render thought')
setChatList(newListWithAnswer) setChatList(newListWithAnswer)
}, },
onError() { onError() {
...@@ -663,6 +662,7 @@ const Main: FC<IMainProps> = () => { ...@@ -663,6 +662,7 @@ const Main: FC<IMainProps> = () => {
isShowSuggestion={doShowSuggestion} isShowSuggestion={doShowSuggestion}
suggestionList={suggestQuestions} suggestionList={suggestQuestions}
isShowSpeechToText={speechToTextConfig?.enabled} isShowSpeechToText={speechToTextConfig?.enabled}
dataSets={dataSets}
/> />
</div> </div>
</div> </div>
......
...@@ -67,7 +67,7 @@ const translation = { ...@@ -67,7 +67,7 @@ const translation = {
hasPageInfo: 'Reading next page of {url}', hasPageInfo: 'Reading next page of {url}',
}, },
search: 'Searching {{query}}', search: 'Searching {{query}}',
dataset: 'Retrieving dataset {{datasetName}}', dataset: 'Retrieving dataset {datasetName}',
}, },
}, },
viewConfigDetailTip: 'In conversation, cannot change above settings', viewConfigDetailTip: 'In conversation, cannot change above settings',
......
...@@ -67,7 +67,7 @@ const translation = { ...@@ -67,7 +67,7 @@ const translation = {
hasPageInfo: '解析链接 {url} 的下一页', hasPageInfo: '解析链接 {url} 的下一页',
}, },
search: '搜索 {{query}}', search: '搜索 {{query}}',
dataset: '检索数据集 {{dataset}}', dataset: '检索数据集 {datasetName}',
}, },
}, },
viewConfigDetailTip: '在对话中,无法更改上述设置', viewConfigDetailTip: '在对话中,无法更改上述设置',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment