Commit e71e99cf by HoMeTown

feat: 最大对话轮数处理

parent b065bf04
......@@ -92,6 +92,7 @@ export default defineConfig({
pathRewrite: { [`^${apiUrl}`]: '' },
},
},
compress: false,
},
security: {
// 与 Web 安全有关的选项
......
......@@ -63,8 +63,12 @@ export function fetchStreamResponse(url: string, body: Record<string, any>, onMe
method: 'POST',
body: JSON.stringify(body),
})
.then(response => response.body?.getReader())
.then(reader => processMessage(reader))
.then((response) => {
return response.body?.getReader()
})
.then((reader) => {
return processMessage(reader)
})
.catch(error => onMessage({
type: 'ERROR',
content: error,
......
......@@ -3,6 +3,7 @@ import { useParams } from 'react-router-dom'
import { Button } from '@nextui-org/react'
import { motion } from 'framer-motion'
import { useScroll } from 'ahooks'
import { toast } from 'react-hot-toast'
import styles from './Chat.module.less'
import { ChatSlogan } from './components/ChatSlogan'
import { ChatMaskBar } from './components/ChatMaskBar'
......@@ -15,12 +16,13 @@ import { ChatEditor } from '@/components/ChatEditor'
import type { ChatRecord } from '@/types/chat'
import { fetchUserQaRecordPage } from '@/api/conversation'
import { fetchCheckTokenApi, fetchStreamResponse } from '@/api/chat'
import { clearShouldSendQuestion, fetchConversations } from '@/store/conversationSlice'
import { clearShouldSendQuestion, createConversation, fetchConversations } from '@/store/conversationSlice'
import type { RootState } from '@/store'
import { useAppDispatch, useAppSelector } from '@/store/hook'
import ScrollBtoIcon from '@/assets/svg/scrollBto.svg?react'
import { setIsAsking } from '@/store/chatSlice'
import SdreamLoading from '@/components/SdreamLoading'
import AddNewChat from '@/assets/svg/addNewChat.svg?react'
export const Chat: React.FC = () => {
let ignore = false
......@@ -68,7 +70,7 @@ export const Chat: React.FC = () => {
productCode,
},
(msg) => {
if (msg.type === 'DATA') {
if (msg?.type === 'DATA' && msg?.content?.code === '00000000') {
setAllItems((prevItems) => {
const newItems = [...prevItems] // 创建数组的浅拷贝
const lastIndex = newItems.length - 1
......@@ -89,6 +91,38 @@ export const Chat: React.FC = () => {
return newItems
})
}
if (msg?.type === 'DATA' && msg?.content?.code === '01010005') {
// showToast('已超过会话支持的轮数上线!', 'error')
toast(t => (
<div className="flex items-center">
<p className="text-[14px]">⚠️ 超过最大轮数上线!请新建对话 👉🏻</p>
<Button
color="primary"
size="sm"
variant="light"
isIconOnly
onClick={() => {
dispatch(createConversation({
conversationData: {},
shouldNavigate: true,
shouldSendQuestion: '',
}))
toast.dismiss(t.id)
}}
>
<AddNewChat />
</Button>
</div>
), {
position: 'bottom-center',
duration: 0,
style: {
// transform: 'translateY(-400px)',
marginBottom: '120px',
},
})
return
}
if (msg.type === 'END') {
if (isNew) {
setTimeout(() => {
......
......@@ -38,8 +38,8 @@ export const ChatAnswerAttachment: React.FC<ChatAnswerAttachmentProps> = ({ from
}
}
return (
<div className="attachmentList flex flex-col gap-[20px]">
{answer.attachmentList && answer.attachmentList.map((attachment, index) => {
<div className="cardList flex flex-col gap-[20px]">
{answer.cardList && answer.cardList.map((attachment, index) => {
if (attachment?.type) {
return (
<div key={`${attachment.type}_${index}`}>
......
......@@ -56,7 +56,7 @@ export const ChatAnswerBox: React.FC<ChatAnswerBoxProps> = ({ record, showIndex,
<div
className="ml-[20px] bg-white rounded-[20px] box-border px-[16px] py-[12px] sm:px-[24px] sm:py-[20px]"
>
{(item.answer?.length || item.attachmentList?.length)
{(item.answer?.length || item.cardList?.length)
? (
<div className="content">
{item.isShow && <ChatAnswerShower onSubmitQuestion={onSubmitQuestion} isLastAnswer={isLastAnswer} answer={item} />}
......
import React, { useEffect, useState } from 'react'
import { Chip } from '@nextui-org/react'
import { ChatAnswerAttachment } from './ChatAnswerAttchment'
import { ChatAnswerOperate } from './ChatAnswerOperate'
import { formatMarkdown } from './markdownFormatter'
......@@ -16,6 +17,24 @@ interface ChatAnswerParserProps {
onSubmitQuestion: (question: string, productCode?: string) => void
}
function CheckIcon({ size, height, width, ...props }) {
return (
<svg
fill="none"
height={size || height || 24}
viewBox="0 0 24 24"
width={size || width || 24}
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path
d="M12 2C6.49 2 2 6.49 2 12C2 17.51 6.49 22 12 22C17.51 22 22 17.51 22 12C22 6.49 17.51 2 12 2ZM16.78 9.7L11.11 15.37C10.97 15.51 10.78 15.59 10.58 15.59C10.38 15.59 10.19 15.51 10.05 15.37L7.22 12.54C6.93 12.25 6.93 11.77 7.22 11.48C7.51 11.19 7.99 11.19 8.28 11.48L10.58 13.78L15.72 8.64C16.01 8.35 16.49 8.35 16.78 8.64C17.07 8.93 17.07 9.4 16.78 9.7Z"
fill="currentColor"
/>
</svg>
)
}
export const ChatAnswerParser: React.FC<ChatAnswerParserProps> = ({ isLastAnswer, onTyping, onComplate, answer, isStopTyping, onSubmitQuestion }) => {
const formatAnswer = formatMarkdown(answer.answer || '')
const [displayedText, setDisplayedText] = useState('')
......@@ -124,21 +143,36 @@ export const ChatAnswerParser: React.FC<ChatAnswerParserProps> = ({ isLastAnswer
}, [isStopTyping])
useEffect(() => {
setHideOperate((answer.attachmentList || []).some(attachment => attachment?.type === 'box' || attachment?.type?.includes('card-')))
}, [answer.attachmentList])
setHideOperate((answer.cardList || []).some(attachment => attachment?.type === 'box' || attachment?.type?.includes('card-')))
}, [answer.cardList])
return (
<div className="answerParser">
<div className="mb-[8px]">
{/* <Chip color="primary" className="mb-[12px]">{answer.step?.message}</Chip> */}
{ answer.step?.step === 'answering' && (
<Chip color="warning" variant="flat">
{answer.step?.message}
</Chip>
)}
{answer.step?.step === 'finished' && (
<Chip color="primary" variant="flat" startContent={<CheckIcon size={18} />}>
{answer.step?.message}
</Chip>
)}
</div>
{!!displayedText.length && (
<div className={answer.attachmentList?.length ? 'mb-[20px]' : ''}>
<div className={answer.cardList?.length ? 'mb-[20px]' : ''}>
<MarkdownDetail>
{displayedText}
</MarkdownDetail>
</div>
)}
{!isTyping
&& answer.attachmentList
&& answer.attachmentList?.length !== 0
&& answer.cardList
&& answer.cardList?.length !== 0
&& <ChatAnswerAttachment fromParser isLastAnswer={isLastAnswer} onSubmitQuestion={onSubmitQuestion} answer={answer} />}
{!isTyping && !hideOperate && <ChatAnswerOperate answer={answer} />}
......
......@@ -11,11 +11,14 @@ interface ChatAnswerRecommendProps {
export const ChatAnswerRecommend: React.FC<ChatAnswerRecommendProps> = ({ answer, onSubmitQuestion }) => {
let isGet = false
const [questionList, setQuestionList] = useState<string[]>([])
const [loading, setLoading] = useState<boolean>(false)
const getAnswerRecommend = async () => {
setLoading(true)
const res = await fetchQueryRecommendQuestion(answer.conversationId || '', answer.recordId || '')
if (res.ok) {
setQuestionList(res.data.questionList)
}
setLoading(false)
}
useEffect(() => {
......@@ -26,7 +29,7 @@ export const ChatAnswerRecommend: React.FC<ChatAnswerRecommendProps> = ({ answer
}, [])
return (
<div className="pl-[62px] mt-[12px] flex flex-col">
{questionList.length !== 0 && (
{!loading && questionList.length !== 0 && (
<div className="flex flex-col gap-[8px]">
{
questionList.map((item, index) => (
......@@ -41,7 +44,7 @@ export const ChatAnswerRecommend: React.FC<ChatAnswerRecommendProps> = ({ answer
</div>
)}
{
questionList.length === 0 && (
loading && questionList.length === 0 && (
<div className="flex flex-col gap-[8px]">
<Skeleton className="w-full sm:w-[300px] rounded-lg">
<div className="h-[40px] w-full rounded-lg bg-[#fff]"></div>
......
......@@ -11,17 +11,17 @@ interface ChatAnswerShowerProps {
}
export const ChatAnswerShower: React.FC<ChatAnswerShowerProps> = ({ answer, isLastAnswer, onSubmitQuestion }) => {
const hideOperate = (answer.attachmentList || []).some(attachment => attachment.type === 'box' || attachment?.type?.includes('card-'))
const hideOperate = (answer.cardList || []).some(attachment => attachment.type === 'box' || attachment?.type?.includes('card-'))
return (
<div className="answerShower">
{answer.answer && (
<div className={answer.attachmentList?.length ? 'sm:mb-[20px]' : ''}>
<div className={answer.cardList?.length ? 'sm:mb-[20px]' : ''}>
<MarkdownDetail>
{formatMarkdown(answer.answer || '')}
</MarkdownDetail>
</div>
)}
{answer.attachmentList && answer.attachmentList?.length !== 0 && <ChatAnswerAttachment onSubmitQuestion={onSubmitQuestion} isLastAnswer={isLastAnswer} answer={answer} />}
{answer.cardList && answer.cardList?.length !== 0 && <ChatAnswerAttachment onSubmitQuestion={onSubmitQuestion} isLastAnswer={isLastAnswer} answer={answer} />}
{/* {} */}
{!hideOperate && <ChatAnswerOperate answer={answer} />}
</div>
......
......@@ -131,7 +131,7 @@ export const Collect: React.FC = () => {
<MarkdownDetail>
{formatMarkdown(item.answer || '')}
</MarkdownDetail>
{item.attachmentList && item.attachmentList?.length !== 0 && <ChatAnswerAttachment answer={item} />}
{item.cardList && item.cardList?.length !== 0 && <ChatAnswerAttachment answer={item} />}
<div className="mt-[12px] flex gap-[4px] justify-between items-center">
<div className="text-12px text-[#B2B8C1]">
{item.collectionTime}
......
......@@ -22,6 +22,11 @@ export interface Attachment {
content: AttachmentContent
}
interface AnswerStep {
step: string
message: string
}
export interface Answer {
endAnswerFlag?: boolean
isStopTyping?: boolean
......@@ -35,7 +40,8 @@ export interface Answer {
conversationId?: string
terminateFlag?: boolean
toolName?: string
attachmentList: Attachment[]
cardList: Attachment[]
step?: AnswerStep
}
export interface OriginalRecord {
......
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