Commit e673a5b1 authored by Joel's avatar Joel

feat: run once to new struct

parent e6a4af75
...@@ -11,21 +11,19 @@ import s from './style.module.css' ...@@ -11,21 +11,19 @@ import s from './style.module.css'
import RunBatch from './run-batch' import RunBatch from './run-batch'
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
import ConfigScence from '@/app/components/share/text-generation/config-scence' import ConfigScence from '@/app/components/share/text-generation/config-scence'
import NoData from '@/app/components/share/text-generation/no-data'
// import History from '@/app/components/share/text-generation/history' // import History from '@/app/components/share/text-generation/history'
import { fetchSavedMessage as doFetchSavedMessage, fetchAppInfo, fetchAppParams, removeMessage, saveMessage, sendCompletionMessage, updateFeedback } from '@/service/share' import { fetchSavedMessage as doFetchSavedMessage, fetchAppInfo, fetchAppParams, removeMessage, saveMessage } from '@/service/share'
import type { SiteInfo } from '@/models/share' import type { SiteInfo } from '@/models/share'
import type { MoreLikeThisConfig, PromptConfig, SavedMessage } from '@/models/debug' import type { MoreLikeThisConfig, PromptConfig, SavedMessage } from '@/models/debug'
import Toast from '@/app/components/base/toast'
import AppIcon from '@/app/components/base/app-icon' import AppIcon from '@/app/components/base/app-icon'
import type { Feedbacktype } from '@/app/components/app/chat'
import { changeLanguage } from '@/i18n/i18next-config' import { changeLanguage } from '@/i18n/i18next-config'
import Loading from '@/app/components/base/loading' import Loading from '@/app/components/base/loading'
import { userInputsFormToPromptVariables } from '@/utils/model-config' import { userInputsFormToPromptVariables } from '@/utils/model-config'
import TextGenerationRes from '@/app/components/app/text-generate/item' import Res from '@/app/components/share/text-generation/result'
import SavedItems from '@/app/components/app/text-generate/saved-items' import SavedItems from '@/app/components/app/text-generate/saved-items'
import type { InstalledApp } from '@/models/explore' import type { InstalledApp } from '@/models/explore'
import { appDefaultIconBackground } from '@/config' import { appDefaultIconBackground } from '@/config'
import Toast from '@/app/components/base/toast'
export type IMainProps = { export type IMainProps = {
isInstalledApp?: boolean isInstalledApp?: boolean
...@@ -36,34 +34,22 @@ const TextGeneration: FC<IMainProps> = ({ ...@@ -36,34 +34,22 @@ const TextGeneration: FC<IMainProps> = ({
isInstalledApp = false, isInstalledApp = false,
installedAppInfo, installedAppInfo,
}) => { }) => {
const { notify } = Toast
const { t } = useTranslation() const { t } = useTranslation()
const media = useBreakpoints() const media = useBreakpoints()
const isPC = media === MediaType.pc const isPC = media === MediaType.pc
const isTablet = media === MediaType.tablet const isTablet = media === MediaType.tablet
const isMoble = media === MediaType.mobile const isMobile = media === MediaType.mobile
const [currTab, setCurrTab] = useState<string>('batch') const [currTab, setCurrTab] = useState<string>('batch')
const [inputs, setInputs] = useState<Record<string, any>>({}) const [inputs, setInputs] = useState<Record<string, any>>({})
const [query, setQuery] = useState('') // run once query content
const [appId, setAppId] = useState<string>('') const [appId, setAppId] = useState<string>('')
const [siteInfo, setSiteInfo] = useState<SiteInfo | null>(null) const [siteInfo, setSiteInfo] = useState<SiteInfo | null>(null)
const [promptConfig, setPromptConfig] = useState<PromptConfig | null>(null) const [promptConfig, setPromptConfig] = useState<PromptConfig | null>(null)
const [moreLikeThisConfig, setMoreLikeThisConfig] = useState<MoreLikeThisConfig | null>(null) const [moreLikeThisConfig, setMoreLikeThisConfig] = useState<MoreLikeThisConfig | null>(null)
const [isResponsing, { setTrue: setResponsingTrue, setFalse: setResponsingFalse }] = useBoolean(false)
const [query, setQuery] = useState('')
const [completionRes, setCompletionRes] = useState('')
const { notify } = Toast
const isNoData = !completionRes
const [messageId, setMessageId] = useState<string | null>(null)
const [feedback, setFeedback] = useState<Feedbacktype>({
rating: null,
})
const handleFeedback = async (feedback: Feedbacktype) => {
await updateFeedback({ url: `/messages/${messageId}/feedbacks`, body: { rating: feedback.rating } }, isInstalledApp, installedAppInfo?.id)
setFeedback(feedback)
}
const [savedMessages, setSavedMessages] = useState<SavedMessage[]>([]) const [savedMessages, setSavedMessages] = useState<SavedMessage[]>([])
...@@ -91,83 +77,15 @@ const TextGeneration: FC<IMainProps> = ({ ...@@ -91,83 +77,15 @@ const TextGeneration: FC<IMainProps> = ({
const logError = (message: string) => { const logError = (message: string) => {
notify({ type: 'error', message }) notify({ type: 'error', message })
} }
const [controlSend, setControlSend] = useState(0)
const checkCanSend = () => { const handleSend = () => {
const prompt_variables = promptConfig?.prompt_variables setControlSend(Date.now())
if (!prompt_variables || prompt_variables?.length === 0)
return true
let hasEmptyInput = false
const requiredVars = prompt_variables?.filter(({ key, name, required }) => {
const res = (!key || !key.trim()) || (!name || !name.trim()) || (required || required === undefined || required === null)
return res
}) || [] // compatible with old version
requiredVars.forEach(({ key }) => {
if (hasEmptyInput)
return
if (!inputs[key])
hasEmptyInput = true
})
if (hasEmptyInput) {
logError(t('appDebug.errorMessage.valueOfVarRequired'))
return false
}
return !hasEmptyInput
} }
const handleSend = async () => { const handleRunBatch = () => {
if (isResponsing) { setControlSend(Date.now())
notify({ type: 'info', message: t('appDebug.errorMessage.waitForResponse') })
return false
}
if (!checkCanSend())
return
if (!query) {
logError(t('appDebug.errorMessage.queryRequired'))
return false
}
const data = {
inputs,
query,
}
setMessageId(null)
setFeedback({
rating: null,
})
setCompletionRes('')
const res: string[] = []
let tempMessageId = ''
if (!isPC)
// eslint-disable-next-line @typescript-eslint/no-use-before-define
showResSidebar()
setResponsingTrue()
sendCompletionMessage(data, {
onData: (data: string, _isFirstMessage: boolean, { messageId }: any) => {
tempMessageId = messageId
res.push(data)
setCompletionRes(res.join(''))
},
onCompleted: () => {
setResponsingFalse()
setMessageId(tempMessageId)
},
onError() {
setResponsingFalse()
},
}, isInstalledApp, installedAppInfo?.id)
} }
const handleRunBatch = () => { }
const fetchInitData = () => { const fetchInitData = () => {
return Promise.all([isInstalledApp return Promise.all([isInstalledApp
? { ? {
...@@ -219,7 +137,7 @@ const TextGeneration: FC<IMainProps> = ({ ...@@ -219,7 +137,7 @@ const TextGeneration: FC<IMainProps> = ({
cn( cn(
'flex flex-col h-full shrink-0', 'flex flex-col h-full shrink-0',
isPC ? 'px-10 py-8' : 'bg-gray-50', isPC ? 'px-10 py-8' : 'bg-gray-50',
isTablet && 'p-6', isMoble && 'p-4') isTablet && 'p-6', isMobile && 'p-4')
} }
> >
<> <>
...@@ -239,33 +157,20 @@ const TextGeneration: FC<IMainProps> = ({ ...@@ -239,33 +157,20 @@ const TextGeneration: FC<IMainProps> = ({
</div> </div>
<div className='grow overflow-y-auto'> <div className='grow overflow-y-auto'>
{(isResponsing && !completionRes) <Res
? ( isBatch={currTab === 'batch'}
<div className='flex h-full w-full justify-center items-center'> isPC={isPC}
<Loading type='area' /> isMobile={isMobile}
</div>) isInstalledApp={!!isInstalledApp}
: ( installedAppInfo={installedAppInfo}
<> promptConfig={promptConfig}
{isNoData moreLikeThisEnabled={!!moreLikeThisConfig?.enabled}
? <NoData /> inputs={inputs}
: ( query={query}
<TextGenerationRes controlSend={controlSend}
className='mt-3' onShowRes={showResSidebar}
content={completionRes} handleSaveMessage={handleSaveMessage}
messageId={messageId} />
isInWebApp
moreLikeThis={moreLikeThisConfig?.enabled}
onFeedback={handleFeedback}
feedback={feedback}
onSave={handleSaveMessage}
isMobile={isMoble}
isInstalledApp={isInstalledApp}
installedAppId={installedAppInfo?.id}
/>
)
}
</>
)}
</div> </div>
</> </>
</div> </div>
......
import type { FC } from 'react'
import React from 'react'
import Header from './header'
import type { Feedbacktype } from '@/app/components/app/chat'
import { format } from '@/service/base'
export type IResultProps = {
content: string
showFeedback: boolean
feedback: Feedbacktype
onFeedback: (feedback: Feedbacktype) => void
}
const Result: FC<IResultProps> = ({
content,
showFeedback,
feedback,
onFeedback,
}) => {
return (
<div className='basis-3/4 h-max'>
<Header result={content} showFeedback={showFeedback} feedback={feedback} onFeedback={onFeedback} />
<div
className='mt-4 w-full flex text-sm leading-5 overflow-scroll font-normal text-gray-900'
style={{
maxHeight: '70vh',
}}
dangerouslySetInnerHTML={{
__html: format(content),
}}
></div>
</div>
)
}
export default React.memo(Result)
'use client'
import type { FC } from 'react' import type { FC } from 'react'
import React from 'react' import React, { useEffect, useState } from 'react'
import Header from './header' import { useBoolean } from 'ahooks'
import { Feedbacktype } from '@/app/components/app/chat' import { t } from 'i18next'
import { format } from '@/service/base' import cn from 'classnames'
import TextGenerationRes from '@/app/components/app/text-generate/item'
import NoData from '@/app/components/share/text-generation/no-data'
import Toast from '@/app/components/base/toast'
import { sendCompletionMessage, updateFeedback } from '@/service/share'
import type { Feedbacktype } from '@/app/components/app/chat'
import Loading from '@/app/components/base/loading'
import type { PromptConfig } from '@/models/debug'
import type { InstalledApp } from '@/models/explore'
export type IResultProps = { export type IResultProps = {
content: string isBatch: boolean
showFeedback: boolean isPC: boolean
feedback: Feedbacktype isMobile: boolean
onFeedback: (feedback: Feedbacktype) => void isInstalledApp: boolean
installedAppInfo?: InstalledApp
promptConfig: PromptConfig | null
moreLikeThisEnabled: boolean
inputs: Record<string, any>
query: string
controlSend: number
onShowRes: () => void
handleSaveMessage: (messageId: string) => void
} }
const Result: FC<IResultProps> = ({ const Result: FC<IResultProps> = ({
content, isBatch,
showFeedback, isPC,
feedback, isMobile,
onFeedback isInstalledApp,
installedAppInfo,
promptConfig,
moreLikeThisEnabled,
inputs,
query,
controlSend,
onShowRes,
handleSaveMessage,
}) => { }) => {
const [isResponsing, { setTrue: setResponsingTrue, setFalse: setResponsingFalse }] = useBoolean(false)
const [completionRes, setCompletionRes] = useState('')
const { notify } = Toast
const isNoData = !completionRes
const [messageId, setMessageId] = useState<string | null>(null)
const [feedback, setFeedback] = useState<Feedbacktype>({
rating: null,
})
const handleFeedback = async (feedback: Feedbacktype) => {
await updateFeedback({ url: `/messages/${messageId}/feedbacks`, body: { rating: feedback.rating } }, isInstalledApp, installedAppInfo?.id)
setFeedback(feedback)
}
const logError = (message: string) => {
notify({ type: 'error', message })
}
const checkCanSend = () => {
const prompt_variables = promptConfig?.prompt_variables
if (!prompt_variables || prompt_variables?.length === 0)
return true
let hasEmptyInput = false
const requiredVars = prompt_variables?.filter(({ key, name, required }) => {
const res = (!key || !key.trim()) || (!name || !name.trim()) || (required || required === undefined || required === null)
return res
}) || [] // compatible with old version
requiredVars.forEach(({ key }) => {
if (hasEmptyInput)
return
if (!inputs[key])
hasEmptyInput = true
})
if (hasEmptyInput) {
logError(t('appDebug.errorMessage.valueOfVarRequired'))
return false
}
return !hasEmptyInput
}
const handleSend = async () => {
if (isResponsing) {
notify({ type: 'info', message: t('appDebug.errorMessage.waitForResponse') })
return false
}
if (!checkCanSend())
return
if (!query) {
logError(t('appDebug.errorMessage.queryRequired'))
return false
}
const data = {
inputs,
query,
}
setMessageId(null)
setFeedback({
rating: null,
})
setCompletionRes('')
const res: string[] = []
let tempMessageId = ''
if (!isPC)
onShowRes()
setResponsingTrue()
sendCompletionMessage(data, {
onData: (data: string, _isFirstMessage: boolean, { messageId }: any) => {
tempMessageId = messageId
res.push(data)
setCompletionRes(res.join(''))
},
onCompleted: () => {
setResponsingFalse()
setMessageId(tempMessageId)
},
onError() {
setResponsingFalse()
},
}, isInstalledApp, installedAppInfo?.id)
}
useEffect(() => {
if (controlSend)
handleSend()
}, [controlSend])
return ( return (
<div className='basis-3/4 h-max'> <div className={cn((isBatch && !isNoData) ? 'h-52' : 'h-full')}>
<Header result={content} showFeedback={showFeedback} feedback={feedback} onFeedback={onFeedback} /> {(isResponsing && !completionRes)
<div ? (
className='mt-4 w-full flex text-sm leading-5 overflow-scroll font-normal text-gray-900' <div className='flex h-full w-full justify-center items-center'>
style={{ <Loading type='area' />
maxHeight: '70vh' </div>)
}} : (
dangerouslySetInnerHTML={{ <>
__html: format(content) {isNoData
}} ? <NoData />
></div> : (
<TextGenerationRes
className='mt-3'
content={completionRes}
messageId={messageId}
isInWebApp
moreLikeThis={moreLikeThisEnabled}
onFeedback={handleFeedback}
feedback={feedback}
onSave={handleSaveMessage}
isMobile={isMobile}
isInstalledApp={isInstalledApp}
installedAppId={installedAppInfo?.id}
/>
)
}
</>
)}
</div> </div>
) )
} }
......
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