diff --git a/app/api/parameters/route.ts b/app/api/parameters/route.ts
index b8139d6..f133da8 100644
--- a/app/api/parameters/route.ts
+++ b/app/api/parameters/route.ts
@@ -9,7 +9,8 @@ export async function GET(request: NextRequest) {
return NextResponse.json(data as object, {
headers: setSession(sessionId),
})
- } catch (error) {
- return NextResponse.json([]);
+ }
+ catch (error) {
+ return NextResponse.json([])
}
}
diff --git a/app/components/chat/answer/index.tsx b/app/components/chat/answer/index.tsx
new file mode 100644
index 0000000..cc0c463
--- /dev/null
+++ b/app/components/chat/answer/index.tsx
@@ -0,0 +1,164 @@
+'use client'
+import type { FC } from 'react'
+import React from 'react'
+import { HandThumbDownIcon, HandThumbUpIcon } from '@heroicons/react/24/outline'
+import { useTranslation } from 'react-i18next'
+import LoadingAnim from '../loading-anim'
+import type { FeedbackFunc, IChatItem } from '../type'
+import s from '../style.module.css'
+import { randomString } from '@/utils/string'
+import type { MessageRating } from '@/types/app'
+import Tooltip from '@/app/components/base/tooltip'
+import { Markdown } from '@/app/components/base/markdown'
+
+const OperationBtn = ({ innerContent, onClick, className }: { innerContent: React.ReactNode; onClick?: () => void; className?: string }) => (
+
+ {innerContent}
+
+)
+
+const OpeningStatementIcon: FC<{ className?: string }> = ({ className }) => (
+
+)
+
+const RatingIcon: FC<{ isLike: boolean }> = ({ isLike }) => {
+ return isLike ? :
+}
+
+const EditIcon: FC<{ className?: string }> = ({ className }) => {
+ return
+}
+
+export const EditIconSolid: FC<{ className?: string }> = ({ className }) => {
+ return
+}
+
+const IconWrapper: FC<{ children: React.ReactNode | string }> = ({ children }) => {
+ return
+ {children}
+
+}
+
+type IAnswerProps = {
+ item: IChatItem
+ feedbackDisabled: boolean
+ onFeedback?: FeedbackFunc
+ isResponsing?: boolean
+}
+
+// The component needs to maintain its own state to control whether to display input component
+const Answer: FC = ({ item, feedbackDisabled = false, onFeedback, isResponsing }) => {
+ const { id, content, feedback } = item
+ const { t } = useTranslation()
+
+ /**
+ * Render feedback results (distinguish between users and administrators)
+ * User reviews cannot be cancelled in Console
+ * @param rating feedback result
+ * @param isUserFeedback Whether it is user's feedback
+ * @returns comp
+ */
+ const renderFeedbackRating = (rating: MessageRating | undefined) => {
+ if (!rating)
+ return null
+
+ const isLike = rating === 'like'
+ const ratingIconClassname = isLike ? 'text-primary-600 bg-primary-100 hover:bg-primary-200' : 'text-red-600 bg-red-100 hover:bg-red-200'
+ // The tooltip is always displayed, but the content is different for different scenarios.
+ return (
+
+ {
+ await onFeedback?.(id, { rating: null })
+ }}
+ >
+
+
+
+
+
+ )
+ }
+
+ /**
+ * Different scenarios have different operation items.
+ * @returns comp
+ */
+ const renderItemOperation = () => {
+ const userOperation = () => {
+ return feedback?.rating
+ ? null
+ :
+
+ {OperationBtn({ innerContent: , onClick: () => onFeedback?.(id, { rating: 'like' }) })}
+
+
+ {OperationBtn({ innerContent: , onClick: () => onFeedback?.(id, { rating: 'dislike' }) })}
+
+
+ }
+
+ return (
+
+ {userOperation()}
+
+ )
+ }
+
+ return (
+
+
+
+ {isResponsing
+ &&
+
+
+ }
+
+
+
+
+ {item.isOpeningStatement && (
+
+
+
{t('app.chat.openingStatementTitle')}
+
+ )}
+ {(isResponsing && !content)
+ ? (
+
+
+
+ )
+ : (
+
+ )}
+
+
+ {!feedbackDisabled && !item.feedbackDisabled && renderItemOperation()}
+ {/* User feedback must be displayed */}
+ {!feedbackDisabled && renderFeedbackRating(feedback?.rating)}
+
+
+
+
+
+ )
+}
+export default React.memo(Answer)
diff --git a/app/components/chat/index.tsx b/app/components/chat/index.tsx
index da57740..ace2ec4 100644
--- a/app/components/chat/index.tsx
+++ b/app/components/chat/index.tsx
@@ -2,23 +2,19 @@
import type { FC } from 'react'
import React, { useEffect, useRef } from 'react'
import cn from 'classnames'
-import { HandThumbDownIcon, HandThumbUpIcon } from '@heroicons/react/24/outline'
import { useTranslation } from 'react-i18next'
import Textarea from 'rc-textarea'
import s from './style.module.css'
-import LoadingAnim from './loading-anim'
-import { randomString } from '@/utils/string'
-import type { Feedbacktype, MessageRating, VisionFile, VisionSettings } from '@/types/app'
+import Answer from './answer'
+import Question from './question'
+import type { FeedbackFunc, Feedbacktype } from './type'
+import type { VisionFile, VisionSettings } from '@/types/app'
import { TransferMethod } from '@/types/app'
import Tooltip from '@/app/components/base/tooltip'
import Toast from '@/app/components/base/toast'
-import { Markdown } from '@/app/components/base/markdown'
import ChatImageUploader from '@/app/components/base/image-uploader/chat-image-uploader'
import ImageList from '@/app/components/base/image-uploader/image-list'
import { useImageFiles } from '@/app/components/base/image-uploader/hooks'
-import ImageGallery from '@/app/components/base/image-gallery'
-
-export type FeedbackFunc = (messageId: string, feedback: Feedbacktype) => Promise
export type IChatProps = {
chatList: IChatItem[]
@@ -60,190 +56,6 @@ export type IChatItem = {
message_files?: VisionFile[]
}
-const OperationBtn = ({ innerContent, onClick, className }: { innerContent: React.ReactNode; onClick?: () => void; className?: string }) => (
-
- {innerContent}
-
-)
-
-const OpeningStatementIcon: FC<{ className?: string }> = ({ className }) => (
-
-)
-
-const RatingIcon: FC<{ isLike: boolean }> = ({ isLike }) => {
- return isLike ? :
-}
-
-const EditIcon: FC<{ className?: string }> = ({ className }) => {
- return
-}
-
-export const EditIconSolid: FC<{ className?: string }> = ({ className }) => {
- return
-}
-
-const IconWrapper: FC<{ children: React.ReactNode | string }> = ({ children }) => {
- return
- {children}
-
-}
-
-type IAnswerProps = {
- item: IChatItem
- feedbackDisabled: boolean
- onFeedback?: FeedbackFunc
- isResponsing?: boolean
-}
-
-// The component needs to maintain its own state to control whether to display input component
-const Answer: FC = ({ item, feedbackDisabled = false, onFeedback, isResponsing }) => {
- const { id, content, feedback } = item
- const { t } = useTranslation()
-
- /**
- * Render feedback results (distinguish between users and administrators)
- * User reviews cannot be cancelled in Console
- * @param rating feedback result
- * @param isUserFeedback Whether it is user's feedback
- * @returns comp
- */
- const renderFeedbackRating = (rating: MessageRating | undefined) => {
- if (!rating)
- return null
-
- const isLike = rating === 'like'
- const ratingIconClassname = isLike ? 'text-primary-600 bg-primary-100 hover:bg-primary-200' : 'text-red-600 bg-red-100 hover:bg-red-200'
- // The tooltip is always displayed, but the content is different for different scenarios.
- return (
-
- {
- await onFeedback?.(id, { rating: null })
- }}
- >
-
-
-
-
-
- )
- }
-
- /**
- * Different scenarios have different operation items.
- * @returns comp
- */
- const renderItemOperation = () => {
- const userOperation = () => {
- return feedback?.rating
- ? null
- :
-
- {OperationBtn({ innerContent: , onClick: () => onFeedback?.(id, { rating: 'like' }) })}
-
-
- {OperationBtn({ innerContent: , onClick: () => onFeedback?.(id, { rating: 'dislike' }) })}
-
-
- }
-
- return (
-
- {userOperation()}
-
- )
- }
-
- return (
-
-
-
- {isResponsing
- &&
-
-
- }
-
-
-
-
- {item.isOpeningStatement && (
-
-
-
{t('app.chat.openingStatementTitle')}
-
- )}
- {(isResponsing && !content)
- ? (
-
-
-
- )
- : (
-
- )}
-
-
- {!feedbackDisabled && !item.feedbackDisabled && renderItemOperation()}
- {/* User feedback must be displayed */}
- {!feedbackDisabled && renderFeedbackRating(feedback?.rating)}
-
-
-
-
-
- )
-}
-
-type IQuestionProps = Pick & {
- imgSrcs?: string[]
-}
-
-const Question: FC = ({ id, content, useCurrentUserAvatar, imgSrcs }) => {
- const userName = ''
- return (
-
-
-
-
- {imgSrcs && imgSrcs.length > 0 && (
-
- )}
-
-
-
-
- {useCurrentUserAvatar
- ? (
-
- {userName?.[0].toLocaleUpperCase()}
-
- )
- : (
-
- )}
-
- )
-}
-
const Chat: FC = ({
chatList,
feedbackDisabled = false,
diff --git a/app/components/chat/question/index.tsx b/app/components/chat/question/index.tsx
new file mode 100644
index 0000000..75b5391
--- /dev/null
+++ b/app/components/chat/question/index.tsx
@@ -0,0 +1,43 @@
+'use client'
+import type { FC } from 'react'
+import React from 'react'
+import type { IChatItem } from '../type'
+import s from '../style.module.css'
+
+import { Markdown } from '@/app/components/base/markdown'
+import ImageGallery from '@/app/components/base/image-gallery'
+
+type IQuestionProps = Pick & {
+ imgSrcs?: string[]
+}
+
+const Question: FC = ({ id, content, useCurrentUserAvatar, imgSrcs }) => {
+ const userName = ''
+ return (
+
+
+
+
+ {imgSrcs && imgSrcs.length > 0 && (
+
+ )}
+
+
+
+
+ {useCurrentUserAvatar
+ ? (
+
+ {userName?.[0].toLocaleUpperCase()}
+
+ )
+ : (
+
+ )}
+
+ )
+}
+
+export default React.memo(Question)
diff --git a/app/components/chat/type.ts b/app/components/chat/type.ts
new file mode 100644
index 0000000..7bbc72b
--- /dev/null
+++ b/app/components/chat/type.ts
@@ -0,0 +1,134 @@
+import type { VisionFile } from '@/types/app'
+
+export type LogAnnotation = {
+ content: string
+ account: {
+ id: string
+ name: string
+ email: string
+ }
+ created_at: number
+}
+
+export type Annotation = {
+ id: string
+ authorName: string
+ logAnnotation?: LogAnnotation
+ created_at?: number
+}
+
+export const MessageRatings = ['like', 'dislike', null] as const
+export type MessageRating = typeof MessageRatings[number]
+
+export type MessageMore = {
+ time: string
+ tokens: number
+ latency: number | string
+}
+
+export type Feedbacktype = {
+ rating: MessageRating
+ content?: string | null
+}
+
+export type FeedbackFunc = (messageId: string, feedback: Feedbacktype) => Promise
+export type SubmitAnnotationFunc = (messageId: string, content: string) => Promise
+
+export type DisplayScene = 'web' | 'console'
+
+export type ToolInfoInThought = {
+ name: string
+ input: string
+ output: string
+ isFinished: boolean
+}
+
+export type ThoughtItem = {
+ id: string
+ tool: string // plugin or dataset. May has multi.
+ thought: string
+ tool_input: string
+ message_id: string
+ observation: string
+ position: number
+ files?: string[]
+ message_files?: VisionFile[]
+}
+
+export type CitationItem = {
+ content: string
+ data_source_type: string
+ dataset_name: string
+ dataset_id: string
+ document_id: string
+ document_name: string
+ hit_count: number
+ index_node_hash: string
+ segment_id: string
+ segment_position: number
+ score: number
+ word_count: number
+}
+
+export type IChatItem = {
+ id: string
+ content: string
+ citation?: CitationItem[]
+ /**
+ * Specific message type
+ */
+ isAnswer: boolean
+ /**
+ * The user feedback result of this message
+ */
+ feedback?: Feedbacktype
+ /**
+ * The admin feedback result of this message
+ */
+ adminFeedback?: Feedbacktype
+ /**
+ * Whether to hide the feedback area
+ */
+ feedbackDisabled?: boolean
+ /**
+ * More information about this message
+ */
+ more?: MessageMore
+ annotation?: Annotation
+ useCurrentUserAvatar?: boolean
+ isOpeningStatement?: boolean
+ suggestedQuestions?: string[]
+ log?: { role: string; text: string }[]
+ agent_thoughts?: ThoughtItem[]
+ message_files?: VisionFile[]
+}
+
+export type MessageEnd = {
+ id: string
+ metadata: {
+ retriever_resources?: CitationItem[]
+ annotation_reply: {
+ id: string
+ account: {
+ id: string
+ name: string
+ }
+ }
+ }
+}
+
+export type MessageReplace = {
+ id: string
+ task_id: string
+ answer: string
+ conversation_id: string
+}
+
+export type AnnotationReply = {
+ id: string
+ task_id: string
+ answer: string
+ conversation_id: string
+ annotation_id: string
+ annotation_author_name: string
+}