mirror of
https://github.com/langgenius/webapp-conversation.git
synced 2025-12-08 17:32:27 +08:00
fix: suggestion is not rendered
This commit is contained in:
@ -21,6 +21,9 @@ const Button: FC<IButtonProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
let style = 'cursor-pointer'
|
let style = 'cursor-pointer'
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
case 'link':
|
||||||
|
style = disabled ? 'border-solid border border-gray-200 bg-gray-200 cursor-not-allowed text-gray-800' : 'border-solid border border-gray-200 cursor-pointer text-blue-600 bg-white hover:shadow-sm hover:border-gray-300'
|
||||||
|
break
|
||||||
case 'primary':
|
case 'primary':
|
||||||
style = (disabled || loading) ? 'bg-primary-600/75 cursor-not-allowed text-white' : 'bg-primary-600 hover:bg-primary-600/75 hover:shadow-md cursor-pointer text-white hover:shadow-sm'
|
style = (disabled || loading) ? 'bg-primary-600/75 cursor-not-allowed text-white' : 'bg-primary-600 hover:bg-primary-600/75 hover:shadow-md cursor-pointer text-white hover:shadow-sm'
|
||||||
break
|
break
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import type { ChatItem, MessageRating, VisionFile } from '@/types/app'
|
|||||||
import Tooltip from '@/app/components/base/tooltip'
|
import Tooltip from '@/app/components/base/tooltip'
|
||||||
import WorkflowProcess from '@/app/components/workflow/workflow-process'
|
import WorkflowProcess from '@/app/components/workflow/workflow-process'
|
||||||
import { Markdown } from '@/app/components/base/markdown'
|
import { Markdown } from '@/app/components/base/markdown'
|
||||||
|
import Button from '@/app/components/base/button'
|
||||||
import type { Emoji } from '@/types/tools'
|
import type { Emoji } from '@/types/tools'
|
||||||
|
|
||||||
const OperationBtn = ({ innerContent, onClick, className }: { innerContent: React.ReactNode; onClick?: () => void; className?: string }) => (
|
const OperationBtn = ({ innerContent, onClick, className }: { innerContent: React.ReactNode; onClick?: () => void; className?: string }) => (
|
||||||
@ -60,6 +61,7 @@ type IAnswerProps = {
|
|||||||
onFeedback?: FeedbackFunc
|
onFeedback?: FeedbackFunc
|
||||||
isResponding?: boolean
|
isResponding?: boolean
|
||||||
allToolIcons?: Record<string, string | Emoji>
|
allToolIcons?: Record<string, string | Emoji>
|
||||||
|
suggestionClick?: (suggestion: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
@ -69,8 +71,9 @@ const Answer: FC<IAnswerProps> = ({
|
|||||||
onFeedback,
|
onFeedback,
|
||||||
isResponding,
|
isResponding,
|
||||||
allToolIcons,
|
allToolIcons,
|
||||||
|
suggestionClick = () => { },
|
||||||
}) => {
|
}) => {
|
||||||
const { id, content, feedback, agent_thoughts, workflowProcess } = item
|
const { id, content, feedback, agent_thoughts, workflowProcess, suggestedQuestions = [] } = item
|
||||||
const isAgentMode = !!agent_thoughts && agent_thoughts.length > 0
|
const isAgentMode = !!agent_thoughts && agent_thoughts.length > 0
|
||||||
|
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
@ -192,6 +195,17 @@ const Answer: FC<IAnswerProps> = ({
|
|||||||
: (
|
: (
|
||||||
<Markdown content={content} />
|
<Markdown content={content} />
|
||||||
))}
|
))}
|
||||||
|
{suggestedQuestions.length > 0 && (
|
||||||
|
<div className='mt-3'>
|
||||||
|
<div className='flex gap-1 mt-1 flex-wrap'>
|
||||||
|
{suggestedQuestions.map((suggestion, index) => (
|
||||||
|
<div key={index} className='flex items-center gap-1'>
|
||||||
|
<Button className='text-sm' type='link' onClick={() => suggestionClick(suggestion)}>{suggestion}</Button>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className='absolute top-[-14px] right-[-14px] flex flex-row justify-end gap-1'>
|
<div className='absolute top-[-14px] right-[-14px] flex flex-row justify-end gap-1'>
|
||||||
{!feedbackDisabled && !item.feedbackDisabled && renderItemOperation()}
|
{!feedbackDisabled && !item.feedbackDisabled && renderItemOperation()}
|
||||||
|
|||||||
@ -52,9 +52,12 @@ const Chat: FC<IChatProps> = ({
|
|||||||
const isUseInputMethod = useRef(false)
|
const isUseInputMethod = useRef(false)
|
||||||
|
|
||||||
const [query, setQuery] = React.useState('')
|
const [query, setQuery] = React.useState('')
|
||||||
|
const queryRef = useRef('')
|
||||||
|
|
||||||
const handleContentChange = (e: any) => {
|
const handleContentChange = (e: any) => {
|
||||||
const value = e.target.value
|
const value = e.target.value
|
||||||
setQuery(value)
|
setQuery(value)
|
||||||
|
queryRef.current = value
|
||||||
}
|
}
|
||||||
|
|
||||||
const logError = (message: string) => {
|
const logError = (message: string) => {
|
||||||
@ -62,16 +65,19 @@ const Chat: FC<IChatProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const valid = () => {
|
const valid = () => {
|
||||||
|
const query = queryRef.current
|
||||||
if (!query || query.trim() === '') {
|
if (!query || query.trim() === '') {
|
||||||
logError('Message cannot be empty')
|
logError(t('app.errorMessage.valueOfVarRequired'))
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (controlClearQuery)
|
if (controlClearQuery) {
|
||||||
setQuery('')
|
setQuery('')
|
||||||
|
queryRef.current = ''
|
||||||
|
}
|
||||||
}, [controlClearQuery])
|
}, [controlClearQuery])
|
||||||
const {
|
const {
|
||||||
files,
|
files,
|
||||||
@ -86,7 +92,7 @@ const Chat: FC<IChatProps> = ({
|
|||||||
const handleSend = () => {
|
const handleSend = () => {
|
||||||
if (!valid() || (checkCanSend && !checkCanSend()))
|
if (!valid() || (checkCanSend && !checkCanSend()))
|
||||||
return
|
return
|
||||||
onSend(query, files.filter(file => file.progress !== -1).map(fileItem => ({
|
onSend(queryRef.current, files.filter(file => file.progress !== -1).map(fileItem => ({
|
||||||
type: 'image',
|
type: 'image',
|
||||||
transfer_method: fileItem.type,
|
transfer_method: fileItem.type,
|
||||||
url: fileItem.url,
|
url: fileItem.url,
|
||||||
@ -95,8 +101,10 @@ const Chat: FC<IChatProps> = ({
|
|||||||
if (!files.find(item => item.type === TransferMethod.local_file && !item.fileId)) {
|
if (!files.find(item => item.type === TransferMethod.local_file && !item.fileId)) {
|
||||||
if (files.length)
|
if (files.length)
|
||||||
onClear()
|
onClear()
|
||||||
if (!isResponding)
|
if (!isResponding) {
|
||||||
setQuery('')
|
setQuery('')
|
||||||
|
queryRef.current = ''
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,11 +120,19 @@ const Chat: FC<IChatProps> = ({
|
|||||||
const handleKeyDown = (e: any) => {
|
const handleKeyDown = (e: any) => {
|
||||||
isUseInputMethod.current = e.nativeEvent.isComposing
|
isUseInputMethod.current = e.nativeEvent.isComposing
|
||||||
if (e.code === 'Enter' && !e.shiftKey) {
|
if (e.code === 'Enter' && !e.shiftKey) {
|
||||||
setQuery(query.replace(/\n$/, ''))
|
const result = query.replace(/\n$/, '')
|
||||||
|
setQuery(result)
|
||||||
|
queryRef.current = result
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const suggestionClick = (suggestion: string) => {
|
||||||
|
setQuery(suggestion)
|
||||||
|
queryRef.current = suggestion
|
||||||
|
handleSend()
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cn(!feedbackDisabled && 'px-3.5', 'h-full')}>
|
<div className={cn(!feedbackDisabled && 'px-3.5', 'h-full')}>
|
||||||
{/* Chat List */}
|
{/* Chat List */}
|
||||||
@ -130,6 +146,7 @@ const Chat: FC<IChatProps> = ({
|
|||||||
feedbackDisabled={feedbackDisabled}
|
feedbackDisabled={feedbackDisabled}
|
||||||
onFeedback={onFeedback}
|
onFeedback={onFeedback}
|
||||||
isResponding={isResponding && isLast}
|
isResponding={isResponding && isLast}
|
||||||
|
suggestionClick={suggestionClick}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -101,6 +101,7 @@ const Main: FC<IMainProps> = () => {
|
|||||||
|
|
||||||
const conversationName = currConversationInfo?.name || t('app.chat.newChatDefaultName') as string
|
const conversationName = currConversationInfo?.name || t('app.chat.newChatDefaultName') as string
|
||||||
const conversationIntroduction = currConversationInfo?.introduction || ''
|
const conversationIntroduction = currConversationInfo?.introduction || ''
|
||||||
|
const suggestedQuestions = currConversationInfo?.suggested_questions || []
|
||||||
|
|
||||||
const handleConversationSwitch = () => {
|
const handleConversationSwitch = () => {
|
||||||
if (!inited)
|
if (!inited)
|
||||||
@ -117,6 +118,7 @@ const Main: FC<IMainProps> = () => {
|
|||||||
setExistConversationInfo({
|
setExistConversationInfo({
|
||||||
name: item?.name || '',
|
name: item?.name || '',
|
||||||
introduction: notSyncToStateIntroduction,
|
introduction: notSyncToStateIntroduction,
|
||||||
|
suggested_questions: suggestedQuestions,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -192,6 +194,7 @@ const Main: FC<IMainProps> = () => {
|
|||||||
name: t('app.chat.newChatDefaultName'),
|
name: t('app.chat.newChatDefaultName'),
|
||||||
inputs: newConversationInputs,
|
inputs: newConversationInputs,
|
||||||
introduction: conversationIntroduction,
|
introduction: conversationIntroduction,
|
||||||
|
suggested_questions: suggestedQuestions,
|
||||||
})
|
})
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
@ -209,6 +212,7 @@ const Main: FC<IMainProps> = () => {
|
|||||||
isAnswer: true,
|
isAnswer: true,
|
||||||
feedbackDisabled: true,
|
feedbackDisabled: true,
|
||||||
isOpeningStatement: isShowPrompt,
|
isOpeningStatement: isShowPrompt,
|
||||||
|
suggestedQuestions: suggestedQuestions,
|
||||||
}
|
}
|
||||||
if (calculatedIntroduction)
|
if (calculatedIntroduction)
|
||||||
return [openStatement]
|
return [openStatement]
|
||||||
@ -233,15 +237,24 @@ const Main: FC<IMainProps> = () => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
const _conversationId = getConversationIdFromStorage(APP_ID)
|
const _conversationId = getConversationIdFromStorage(APP_ID)
|
||||||
const isNotNewConversation = conversations.some(item => item.id === _conversationId)
|
const currentConversation = conversations.find(item => item.id === _conversationId)
|
||||||
|
const isNotNewConversation = !!currentConversation
|
||||||
|
|
||||||
// fetch new conversation info
|
// fetch new conversation info
|
||||||
const { user_input_form, opening_statement: introduction, file_upload, system_parameters }: any = appParams
|
const { user_input_form, opening_statement: introduction, file_upload, system_parameters, suggested_questions = [] }: any = appParams
|
||||||
setLocaleOnClient(APP_INFO.default_language, true)
|
setLocaleOnClient(APP_INFO.default_language, true)
|
||||||
setNewConversationInfo({
|
setNewConversationInfo({
|
||||||
name: t('app.chat.newChatDefaultName'),
|
name: t('app.chat.newChatDefaultName'),
|
||||||
introduction,
|
introduction,
|
||||||
|
suggested_questions
|
||||||
})
|
})
|
||||||
|
if (isNotNewConversation) {
|
||||||
|
setExistConversationInfo({
|
||||||
|
name: currentConversation.name || t('app.chat.newChatDefaultName'),
|
||||||
|
introduction,
|
||||||
|
suggested_questions
|
||||||
|
})
|
||||||
|
}
|
||||||
const prompt_variables = userInputsFormToPromptVariables(user_input_form)
|
const prompt_variables = userInputsFormToPromptVariables(user_input_form)
|
||||||
setPromptConfig({
|
setPromptConfig({
|
||||||
prompt_template: promptTemplate,
|
prompt_template: promptTemplate,
|
||||||
|
|||||||
@ -102,7 +102,8 @@ export type ConversationItem = {
|
|||||||
id: string
|
id: string
|
||||||
name: string
|
name: string
|
||||||
inputs: Record<string, any> | null
|
inputs: Record<string, any> | null
|
||||||
introduction: string
|
introduction: string,
|
||||||
|
suggested_questions?: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export type AppInfo = {
|
export type AppInfo = {
|
||||||
|
|||||||
Reference in New Issue
Block a user