frontend for model runtime (#1861)

Co-authored-by: Joel <iamjoel007@gmail.com>
This commit is contained in:
zxhlyh 2024-01-03 00:05:08 +08:00 committed by GitHub
parent d069c668f8
commit d70d61b1cb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 1442 additions and 917 deletions

View File

@ -275,7 +275,7 @@ const Answer: FC<IAnswerProps> = ({
className={cn(s.copyBtn, 'mr-1')} className={cn(s.copyBtn, 'mr-1')}
/> />
)} )}
{supportAnnotation && ( {(supportAnnotation && !item.isOpeningStatement) && (
<AnnotationCtrlBtn <AnnotationCtrlBtn
appId={appId!} appId={appId!}
messageId={id} messageId={id}

View File

@ -347,7 +347,6 @@ const Debug: FC<IDebug> = ({
} }
}, },
onMessageEnd: (messageEnd) => { onMessageEnd: (messageEnd) => {
// TODO
if (messageEnd.metadata?.annotation_reply) { if (messageEnd.metadata?.annotation_reply) {
responseItem.id = messageEnd.id responseItem.id = messageEnd.id
responseItem.annotation = ({ responseItem.annotation = ({
@ -382,27 +381,6 @@ const Debug: FC<IDebug> = ({
onMessageReplace: (messageReplace) => { onMessageReplace: (messageReplace) => {
responseItem.content = messageReplace.answer responseItem.content = messageReplace.answer
}, },
onAnnotationReply: (annotationReply) => {
// TODO: temp debug
responseItem.id = annotationReply.id
responseItem.content = annotationReply.answer
responseItem.annotation = ({
id: annotationReply.annotation_id,
authorName: annotationReply.annotation_author_name,
} as AnnotationType)
const newListWithAnswer = produce(
getChatList().filter(item => item.id !== responseItem.id && item.id !== placeholderAnswerId),
(draft) => {
if (!draft.find(item => item.id === questionId))
draft.push({ ...questionItem })
draft.push({
...responseItem,
id: annotationReply.id,
})
})
setChatList(newListWithAnswer)
},
onError() { onError() {
setResponsingFalse() setResponsingFalse()
// role back placeholder answer // role back placeholder answer

View File

@ -318,6 +318,7 @@ const Configuration: FC = () => {
...visionConfig, ...visionConfig,
enabled: supportVision, enabled: supportVision,
}, true) }, true)
setCompletionParams({})
} }
const isShowVisionConfig = !!currModel?.features?.includes(ModelFeatureEnum.vision) const isShowVisionConfig = !!currModel?.features?.includes(ModelFeatureEnum.vision)
@ -656,7 +657,6 @@ const Configuration: FC = () => {
onCompletionParamsChange={(newParams: FormValue) => { onCompletionParamsChange={(newParams: FormValue) => {
setCompletionParams(newParams) setCompletionParams(newParams)
}} }}
disabled={!hasSettedApiKey}
/> />
<div className='w-[1px] h-[14px] bg-gray-200'></div> <div className='w-[1px] h-[14px] bg-gray-200'></div>
<Button onClick={() => setShowConfirm(true)} className='shrink-0 mr-2 w-[70px] !h-8 !text-[13px] font-medium'>{t('appDebug.operation.resetConfig')}</Button> <Button onClick={() => setShowConfirm(true)} className='shrink-0 mr-2 w-[70px] !h-8 !text-[13px] font-medium'>{t('appDebug.operation.resetConfig')}</Button>

View File

@ -587,7 +587,7 @@ const StepTwo = ({
</div> </div>
</div> </div>
<div className={s.formFooter}> <div className={s.formFooter}>
<Button type="primary" className={cn(s.button, '!h-8 text-primary-600')} onClick={confirmChangeCustomConfig}>{t('datasetCreation.stepTwo.preview')}</Button> <Button type="primary" className={cn(s.button, '!h-8')} onClick={confirmChangeCustomConfig}>{t('datasetCreation.stepTwo.preview')}</Button>
<Button className={cn(s.button, 'ml-2 !h-8')} onClick={resetRules}>{t('datasetCreation.stepTwo.reset')}</Button> <Button className={cn(s.button, 'ml-2 !h-8')} onClick={resetRules}>{t('datasetCreation.stepTwo.reset')}</Button>
</div> </div>
</div> </div>

View File

@ -61,16 +61,16 @@ export enum ModelStatusEnum {
noPermission = 'no-permission', noPermission = 'no-permission',
} }
export const MODEL_STATUS_TEXT = { export const MODEL_STATUS_TEXT: { [k: string]: TypeWithI18N } = {
[ModelStatusEnum.noConfigure]: { 'no-configure': {
en_US: 'No Configure', en_US: 'No Configure',
zh_Hans: '未配置凭据', zh_Hans: '未配置凭据',
}, },
[ModelStatusEnum.quotaExceeded]: { 'quota-exceeded': {
en_US: 'Quota Exceeded', en_US: 'Quota Exceeded',
zh_Hans: '额度不足', zh_Hans: '额度不足',
}, },
[ModelStatusEnum.noPermission]: { 'no-permission': {
en_US: 'No Permission', en_US: 'No Permission',
zh_Hans: '无使用权限', zh_Hans: '无使用权限',
}, },

View File

@ -4,11 +4,13 @@ import SystemModelSelector from './system-model-selector'
import ProviderAddedCard, { UPDATE_MODEL_PROVIDER_CUSTOM_MODEL_LIST } from './provider-added-card' import ProviderAddedCard, { UPDATE_MODEL_PROVIDER_CUSTOM_MODEL_LIST } from './provider-added-card'
import ProviderCard from './provider-card' import ProviderCard from './provider-card'
import type { import type {
ConfigurateMethodEnum,
CustomConfigrationModelFixedFields, CustomConfigrationModelFixedFields,
ModelProvider, ModelProvider,
} from './declarations' } from './declarations'
import { CustomConfigurationStatusEnum } from './declarations' import {
ConfigurateMethodEnum,
CustomConfigurationStatusEnum,
} from './declarations'
import { import {
useDefaultModel, useDefaultModel,
useUpdateModelProvidersAndModelList, useUpdateModelProvidersAndModelList,
@ -57,7 +59,7 @@ const ModelProviderPage = () => {
onSaveCallback: () => { onSaveCallback: () => {
updateModelProvidersAndModelList() updateModelProvidersAndModelList()
if (customConfigrationModelFixedFields && provider.custom_configuration.status === CustomConfigurationStatusEnum.active) { if (configurateMethod === ConfigurateMethodEnum.customizableModel && provider.custom_configuration.status === CustomConfigurationStatusEnum.active) {
eventEmitter?.emit({ eventEmitter?.emit({
type: UPDATE_MODEL_PROVIDER_CUSTOM_MODEL_LIST, type: UPDATE_MODEL_PROVIDER_CUSTOM_MODEL_LIST,
payload: provider.provider, payload: provider.provider,

View File

@ -21,6 +21,7 @@ type FormProps = {
validating: boolean validating: boolean
validatedSuccess?: boolean validatedSuccess?: boolean
showOnVariableMap: Record<string, string[]> showOnVariableMap: Record<string, string[]>
isEditMode: boolean
} }
const Form: FC<FormProps> = ({ const Form: FC<FormProps> = ({
@ -30,11 +31,15 @@ const Form: FC<FormProps> = ({
validating, validating,
validatedSuccess, validatedSuccess,
showOnVariableMap, showOnVariableMap,
isEditMode,
}) => { }) => {
const language = useLanguage() const language = useLanguage()
const [changeKey, setChangeKey] = useState('') const [changeKey, setChangeKey] = useState('')
const handleFormChange = (key: string, val: string) => { const handleFormChange = (key: string, val: string) => {
if (isEditMode && (key === '__model_type' || key === '__model_name'))
return
setChangeKey(key) setChangeKey(key)
const shouldClearVariable: Record<string, string | undefined> = {} const shouldClearVariable: Record<string, string | undefined> = {}
if (showOnVariableMap[key]?.length) { if (showOnVariableMap[key]?.length) {
@ -58,6 +63,8 @@ const Form: FC<FormProps> = ({
if (show_on.length && !show_on.every(showOnItem => value[showOnItem.variable] === showOnItem.value)) if (show_on.length && !show_on.every(showOnItem => value[showOnItem.variable] === showOnItem.value))
return null return null
const disabed = isEditMode && (variable === '__model_type' || variable === '__model_name')
return ( return (
<div key={variable} className='py-3'> <div key={variable} className='py-3'>
<div className='py-2 text-sm text-gray-900'> <div className='py-2 text-sm text-gray-900'>
@ -69,10 +76,12 @@ const Form: FC<FormProps> = ({
} }
</div> </div>
<Input <Input
className={`${disabed && 'cursor-not-allowed opacity-60'}`}
value={value[variable] as string} value={value[variable] as string}
onChange={val => handleFormChange(variable, val)} onChange={val => handleFormChange(variable, val)}
validated={validatedSuccess} validated={validatedSuccess}
placeholder={placeholder?.[language]} placeholder={placeholder?.[language]}
disabled={disabed}
/> />
{validating && changeKey === variable && <ValidatingTip />} {validating && changeKey === variable && <ValidatingTip />}
</div> </div>
@ -91,6 +100,8 @@ const Form: FC<FormProps> = ({
if (show_on.length && !show_on.every(showOnItem => value[showOnItem.variable] === showOnItem.value)) if (show_on.length && !show_on.every(showOnItem => value[showOnItem.variable] === showOnItem.value))
return null return null
const disabed = isEditMode && (variable === '__model_type' || variable === '__model_name')
return ( return (
<div key={variable} className='py-3'> <div key={variable} className='py-3'>
<div className='py-2 text-sm text-gray-900'> <div className='py-2 text-sm text-gray-900'>
@ -113,6 +124,7 @@ const Form: FC<FormProps> = ({
className={` className={`
flex items-center px-3 py-2 rounded-lg border border-gray-100 bg-gray-25 cursor-pointer flex items-center px-3 py-2 rounded-lg border border-gray-100 bg-gray-25 cursor-pointer
${value[variable] === option.value && 'bg-white border-[1.5px] border-primary-400 shadow-sm'} ${value[variable] === option.value && 'bg-white border-[1.5px] border-primary-400 shadow-sm'}
${disabed && '!cursor-not-allowed opacity-60'}
`} `}
onClick={() => handleFormChange(variable, option.value)} onClick={() => handleFormChange(variable, option.value)}
key={`${variable}-${option.value}`} key={`${variable}-${option.value}`}

View File

@ -7,6 +7,8 @@ type InputProps = {
onFocus?: () => void onFocus?: () => void
placeholder?: string placeholder?: string
validated?: boolean validated?: boolean
className?: string
disabled?: boolean
} }
const Input: FC<InputProps> = ({ const Input: FC<InputProps> = ({
value, value,
@ -14,6 +16,8 @@ const Input: FC<InputProps> = ({
onFocus, onFocus,
placeholder, placeholder,
validated, validated,
className,
disabled,
}) => { }) => {
return ( return (
<div className='relative'> <div className='relative'>
@ -26,11 +30,13 @@ const Input: FC<InputProps> = ({
focus:bg-white focus:border-gray-300 focus:shadow-xs focus:bg-white focus:border-gray-300 focus:shadow-xs
placeholder:text-sm placeholder:text-gray-400 placeholder:text-sm placeholder:text-gray-400
${validated && 'pr-[30px]'} ${validated && 'pr-[30px]'}
${className}
`} `}
placeholder={placeholder || ''} placeholder={placeholder || ''}
onChange={e => onChange(e.target.value)} onChange={e => onChange(e.target.value)}
onFocus={onFocus} onFocus={onFocus}
value={value || ''} value={value || ''}
disabled={disabled}
/> />
{ {
validated && ( validated && (

View File

@ -239,6 +239,7 @@ const ModelModal: FC<ModelModalProps> = ({
validating={validating} validating={validating}
validatedSuccess={validatedStatusState.status === ValidatedStatus.Success} validatedSuccess={validatedStatusState.status === ValidatedStatus.Success}
showOnVariableMap={showOnVariableMap} showOnVariableMap={showOnVariableMap}
isEditMode={isEditMode}
/> />
<div className='sticky bottom-0 flex justify-between items-center py-6 flex-wrap gap-y-2 bg-white'> <div className='sticky bottom-0 flex justify-between items-center py-6 flex-wrap gap-y-2 bg-white'>
{ {
@ -313,7 +314,7 @@ const ModelModal: FC<ModelModalProps> = ({
{ {
showConfirm && ( showConfirm && (
<ConfirmCommon <ConfirmCommon
title='Are you sure?' title={t('common.modelProvider.confirmDelete')}
isShow={showConfirm} isShow={showConfirm}
onCancel={() => setShowConfirm(false)} onCancel={() => setShowConfirm(false)}
onConfirm={handleRemove} onConfirm={handleRemove}

View File

@ -43,13 +43,13 @@ const ModelName: FC<ModelNameProps> = ({
`} `}
> >
<div <div
className='mr-2 truncate' className='mr-1 truncate'
title={modelItem.label[language]} title={modelItem.label[language]}
> >
{modelItem.label[language]} {modelItem.label[language]}
</div> </div>
{ {
showModelType && ( showModelType && modelItem.model_type && (
<ModelBadge className={`mr-0.5 ${modelTypeClassName}`}> <ModelBadge className={`mr-0.5 ${modelTypeClassName}`}>
{modelTypeFormat(modelItem.model_type)} {modelTypeFormat(modelItem.model_type)}
</ModelBadge> </ModelBadge>

View File

@ -1,5 +1,5 @@
import type { FC } from 'react' import type { FC } from 'react'
import { useEffect, useState } from 'react' import { useEffect, useMemo, useState } from 'react'
import useSWR from 'swr' import useSWR from 'swr'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import type { import type {
@ -7,10 +7,17 @@ import type {
FormValue, FormValue,
ModelParameterRule, ModelParameterRule,
} from '../declarations' } from '../declarations'
import {
MODEL_STATUS_TEXT,
ModelStatusEnum,
} from '../declarations'
import ModelIcon from '../model-icon' import ModelIcon from '../model-icon'
import ModelName from '../model-name' import ModelName from '../model-name'
import ModelSelector from '../model-selector' import ModelSelector from '../model-selector'
import { useTextGenerationCurrentProviderAndModelAndModelList } from '../hooks' import {
useLanguage,
useTextGenerationCurrentProviderAndModelAndModelList,
} from '../hooks'
import ParameterItem from './parameter-item' import ParameterItem from './parameter-item'
import type { ParameterValue } from './parameter-item' import type { ParameterValue } from './parameter-item'
import { import {
@ -23,6 +30,8 @@ import { AlertTriangle } from '@/app/components/base/icons/src/vender/line/alert
import { CubeOutline } from '@/app/components/base/icons/src/vender/line/shapes' import { CubeOutline } from '@/app/components/base/icons/src/vender/line/shapes'
import { fetchModelParameterRules } from '@/service/common' import { fetchModelParameterRules } from '@/service/common'
import Loading from '@/app/components/base/loading' import Loading from '@/app/components/base/loading'
import { useProviderContext } from '@/context/provider-context'
import TooltipPlus from '@/app/components/base/tooltip-plus'
type ModelParameterModalProps = { type ModelParameterModalProps = {
isAdvancedMode: boolean isAdvancedMode: boolean
@ -32,7 +41,6 @@ type ModelParameterModalProps = {
setModel: (model: { modelId: string; provider: string; mode?: string; features: string[] }) => void setModel: (model: { modelId: string; provider: string; mode?: string; features: string[] }) => void
completionParams: FormValue completionParams: FormValue
onCompletionParamsChange: (newParams: FormValue) => void onCompletionParamsChange: (newParams: FormValue) => void
disabled: boolean
} }
const stopParameerRule: ModelParameterRule = { const stopParameerRule: ModelParameterRule = {
default: [], default: [],
@ -42,7 +50,7 @@ const stopParameerRule: ModelParameterRule = {
}, },
label: { label: {
en_US: 'Stop sequences', en_US: 'Stop sequences',
zh_Hans: '停止序列 stop_sequences', zh_Hans: '停止序列',
}, },
name: 'stop', name: 'stop',
required: false, required: false,
@ -59,9 +67,10 @@ const ModelParameterModal: FC<ModelParameterModalProps> = ({
setModel, setModel,
completionParams, completionParams,
onCompletionParamsChange, onCompletionParamsChange,
disabled,
}) => { }) => {
const { t } = useTranslation() const { t } = useTranslation()
const language = useLanguage()
const { hasSettedApiKey, modelProviders } = useProviderContext()
const [open, setOpen] = useState(false) const [open, setOpen] = useState(false)
const { data: parameterRulesData, isLoading } = useSWR(`/workspaces/current/model-providers/${provider}/models/parameter-rules?model=${modelId}`, fetchModelParameterRules) const { data: parameterRulesData, isLoading } = useSWR(`/workspaces/current/model-providers/${provider}/models/parameter-rules?model=${modelId}`, fetchModelParameterRules)
const { const {
@ -72,7 +81,13 @@ const ModelParameterModal: FC<ModelParameterModalProps> = ({
{ provider, model: modelId }, { provider, model: modelId },
) )
const parameterRules = parameterRulesData?.data || [] const hasDeprecated = !currentProvider || !currentModel
const modelDisabled = currentModel?.status !== ModelStatusEnum.active
const disabled = !hasSettedApiKey || hasDeprecated || modelDisabled
const parameterRules = useMemo(() => {
return parameterRulesData?.data || []
}, [parameterRulesData])
const handleParamChange = (key: string, value: ParameterValue) => { const handleParamChange = (key: string, value: ParameterValue) => {
onCompletionParamsChange({ onCompletionParamsChange({
@ -92,21 +107,6 @@ const ModelParameterModal: FC<ModelParameterModalProps> = ({
}) })
} }
const handleChangeParams = () => {
const newCompletionParams = parameterRules.reduce((acc, parameter) => {
if (parameter.default !== undefined)
acc[parameter.name] = parameter.default
return acc
}, {} as Record<string, any>)
onCompletionParamsChange(newCompletionParams)
}
useEffect(() => {
handleChangeParams()
}, [parameterRules])
const handleSwitch = (key: string, value: boolean, assignValue: ParameterValue) => { const handleSwitch = (key: string, value: boolean, assignValue: ParameterValue) => {
if (!value) { if (!value) {
const newCompletionParams = { ...completionParams } const newCompletionParams = { ...completionParams }
@ -122,6 +122,22 @@ const ModelParameterModal: FC<ModelParameterModalProps> = ({
} }
} }
const handleInitialParams = () => {
if (parameterRules.length) {
const newCompletionParams = { ...completionParams }
Object.keys(newCompletionParams).forEach((key) => {
if (!parameterRules.find(item => item.name === key))
delete newCompletionParams[key]
})
onCompletionParamsChange(newCompletionParams)
}
}
useEffect(() => {
handleInitialParams()
}, [parameterRules])
return ( return (
<PortalToFollowElem <PortalToFollowElem
open={open} open={open}
@ -149,22 +165,48 @@ const ModelParameterModal: FC<ModelParameterModalProps> = ({
/> />
) )
} }
{
!currentProvider && (
<ModelIcon
className='mr-1.5 !w-5 !h-5'
provider={modelProviders.find(item => item.provider === provider)}
modelName={modelId}
/>
)
}
{ {
currentModel && ( currentModel && (
<ModelName <ModelName
className='mr-1.5 text-gray-900' className='mr-1.5 text-gray-900'
modelItem={currentModel} modelItem={currentModel}
showMode={isAdvancedMode} showMode
modeClassName='!text-[#444CE7] !border-[#A4BCFD]' modeClassName='!text-[#444CE7] !border-[#A4BCFD]'
showFeatures={isAdvancedMode} showFeatures
featuresClassName='!text-[#444CE7] !border-[#A4BCFD]' featuresClassName='!text-[#444CE7] !border-[#A4BCFD]'
/> />
) )
} }
{
!currentModel && (
<div className='mr-1 text-[13px] font-medium text-gray-900 truncate'>
{modelId}
</div>
)
}
{ {
disabled disabled
? ( ? (
<TooltipPlus
popupContent={
hasDeprecated
? t('common.modelProvider.deprecated')
: (modelDisabled && currentModel)
? MODEL_STATUS_TEXT[currentModel.status as string][language]
: ''
}
>
<AlertTriangle className='w-4 h-4 text-[#F79009]' /> <AlertTriangle className='w-4 h-4 text-[#F79009]' />
</TooltipPlus>
) )
: ( : (
<SlidersH className='w-4 h-4 text-indigo-600' /> <SlidersH className='w-4 h-4 text-indigo-600' />
@ -178,7 +220,7 @@ const ModelParameterModal: FC<ModelParameterModalProps> = ({
<CubeOutline className='mr-2 w-4 h-4 text-primary-600' /> <CubeOutline className='mr-2 w-4 h-4 text-primary-600' />
{t('common.modelProvider.modelAndParameters')} {t('common.modelProvider.modelAndParameters')}
</div> </div>
<div className='px-10 pt-4 pb-8'> <div className='max-h-[480px] px-10 pt-4 pb-8 overflow-y-auto'>
<div className='flex items-center justify-between h-8'> <div className='flex items-center justify-between h-8'>
<div className='text-sm font-medium text-gray-900'> <div className='text-sm font-medium text-gray-900'>
{t('common.modelProvider.model')} {t('common.modelProvider.model')}
@ -189,14 +231,18 @@ const ModelParameterModal: FC<ModelParameterModalProps> = ({
onSelect={handleChangeModel} onSelect={handleChangeModel}
/> />
</div> </div>
<div className='my-5 h-[1px] bg-gray-100' />
{ {
isLoading && ( !!parameterRules.length && (
<Loading /> <div className='my-5 h-[1px] bg-gray-100' />
) )
} }
{ {
!isLoading && ( isLoading && (
<div className='mt-5'><Loading /></div>
)
}
{
!isLoading && !!parameterRules.length && (
[ [
...parameterRules, ...parameterRules,
...(isAdvancedMode ? [stopParameerRule] : []), ...(isAdvancedMode ? [stopParameerRule] : []),

View File

@ -29,7 +29,37 @@ const ParameterItem: FC<ParameterItemProps> = ({
const language = useLanguage() const language = useLanguage()
const [localValue, setLocalValue] = useState(value) const [localValue, setLocalValue] = useState(value)
const mergedValue = isNullOrUndefined(value) ? localValue : value const mergedValue = isNullOrUndefined(value) ? localValue : value
const renderValue = mergedValue === undefined ? parameterRule.default : mergedValue
const getDefaultValue = () => {
let defaultValue: ParameterValue
if (parameterRule.type === 'int' || parameterRule.type === 'float') {
if (isNullOrUndefined(parameterRule.default)) {
if (parameterRule.min)
defaultValue = parameterRule.min
else
defaultValue = 0
}
else {
defaultValue = parameterRule.default
}
}
if (parameterRule.type === 'string' && !parameterRule.options?.length)
defaultValue = parameterRule.default || ''
if (parameterRule.type === 'string' && parameterRule.options?.length)
defaultValue = parameterRule.default || ''
if (parameterRule.type === 'boolean')
defaultValue = !isNullOrUndefined(parameterRule.default) ? parameterRule.default : false
if (parameterRule.type === 'tag')
defaultValue = !isNullOrUndefined(parameterRule.default) ? parameterRule.default : []
return defaultValue
}
const renderValue = isNullOrUndefined(mergedValue) ? getDefaultValue() : mergedValue
const handleChange = (v: ParameterValue) => { const handleChange = (v: ParameterValue) => {
setLocalValue(v) setLocalValue(v)
@ -73,22 +103,8 @@ const ParameterItem: FC<ParameterItemProps> = ({
if (onSwitch) { if (onSwitch) {
let assignValue: ParameterValue = localValue let assignValue: ParameterValue = localValue
if (isNullOrUndefined(localValue)) { if (isNullOrUndefined(localValue))
if (parameterRule.type === 'int' || parameterRule.type === 'float') assignValue = getDefaultValue()
assignValue = !isNullOrUndefined(parameterRule.default) ? parameterRule.default : 0
if (parameterRule.type === 'string' && !parameterRule.options?.length)
assignValue = parameterRule.default || ''
if (parameterRule.type === 'string' && parameterRule.options?.length)
assignValue = parameterRule.options[0]
if (parameterRule.type === 'boolean')
assignValue = !isNullOrUndefined(parameterRule.default) ? parameterRule.default : false
if (parameterRule.type === 'tag')
assignValue = !isNullOrUndefined(parameterRule.default) ? parameterRule.default : []
}
onSwitch(checked, assignValue) onSwitch(checked, assignValue)
} }
@ -145,7 +161,7 @@ const ParameterItem: FC<ParameterItemProps> = ({
<div className='flex items-center'> <div className='flex items-center'>
<Slider <Slider
className='w-[120px]' className='w-[120px]'
value={isNullOrUndefined(renderValue) ? 0 : +renderValue!} value={renderValue as number}
min={parameterRule.min} min={parameterRule.min}
max={parameterRule.max} max={parameterRule.max}
step={+`0.${parameterRule.precision || 0}`} step={+`0.${parameterRule.precision || 0}`}
@ -157,7 +173,7 @@ const ParameterItem: FC<ParameterItemProps> = ({
max={parameterRule.max} max={parameterRule.max}
min={parameterRule.min} min={parameterRule.min}
step={+`0.${parameterRule.precision || 0}`} step={+`0.${parameterRule.precision || 0}`}
value={isNullOrUndefined(renderValue) ? 0 : +renderValue!} value={renderValue as string}
onChange={handleNumberInputChange} onChange={handleNumberInputChange}
/> />
</div> </div>
@ -167,7 +183,7 @@ const ParameterItem: FC<ParameterItemProps> = ({
parameterRule.type === 'boolean' && ( parameterRule.type === 'boolean' && (
<Radio.Group <Radio.Group
className='w-[200px] flex items-center' className='w-[200px] flex items-center'
value={isNullOrUndefined(renderValue) ? 1 : 0} value={renderValue ? 1 : 0}
onChange={handleRadioChange} onChange={handleRadioChange}
> >
<Radio value={1} className='!mr-1 w-[94px]'>True</Radio> <Radio value={1} className='!mr-1 w-[94px]'>True</Radio>
@ -180,7 +196,7 @@ const ParameterItem: FC<ParameterItemProps> = ({
<input <input
type='number' type='number'
className='flex items-center px-3 w-[200px] h-8 appearance-none outline-none rounded-lg bg-gray-100 text-[13px] text-gra-900' className='flex items-center px-3 w-[200px] h-8 appearance-none outline-none rounded-lg bg-gray-100 text-[13px] text-gra-900'
value={(isNullOrUndefined(renderValue) ? '' : renderValue) as string} value={renderValue as string}
onChange={handleNumberInputChange} onChange={handleNumberInputChange}
/> />
) )
@ -189,13 +205,13 @@ const ParameterItem: FC<ParameterItemProps> = ({
parameterRule.type === 'string' && !parameterRule.options?.length && ( parameterRule.type === 'string' && !parameterRule.options?.length && (
<input <input
className='flex items-center px-3 w-[200px] h-8 appearance-none outline-none rounded-lg bg-gray-100 text-[13px] text-gra-900' className='flex items-center px-3 w-[200px] h-8 appearance-none outline-none rounded-lg bg-gray-100 text-[13px] text-gra-900'
value={(isNullOrUndefined(renderValue) ? '' : renderValue) as string} value={renderValue as string}
onChange={handleStringInputChange} onChange={handleStringInputChange}
/> />
) )
} }
{ {
parameterRule.type === 'string' && parameterRule?.options?.length && ( parameterRule.type === 'string' && !!parameterRule?.options?.length && (
<SimpleSelect <SimpleSelect
className='!py-0' className='!py-0'
wrapperClassName='!w-[200px] !h-8' wrapperClassName='!w-[200px] !h-8'
@ -209,7 +225,7 @@ const ParameterItem: FC<ParameterItemProps> = ({
parameterRule.type === 'tag' && ( parameterRule.type === 'tag' && (
<div className='w-[200px]'> <div className='w-[200px]'>
<TagInput <TagInput
items={isNullOrUndefined(renderValue) ? [] : (renderValue as string[])} items={renderValue as string[]}
onChange={handleTagChange} onChange={handleTagChange}
customizedConfirmKey='Tab' customizedConfirmKey='Tab'
/> />

View File

@ -0,0 +1,46 @@
import type { FC } from 'react'
import { useTranslation } from 'react-i18next'
import ModelIcon from '../model-icon'
import { AlertTriangle } from '@/app/components/base/icons/src/vender/line/alertsAndFeedback'
import { useProviderContext } from '@/context/provider-context'
import TooltipPlus from '@/app/components/base/tooltip-plus'
type ModelTriggerProps = {
modelName: string
providerName: string
className?: string
}
const ModelTrigger: FC<ModelTriggerProps> = ({
modelName,
providerName,
className,
}) => {
const { t } = useTranslation()
const { modelProviders } = useProviderContext()
const currentProvider = modelProviders.find(provider => provider.provider === providerName)
return (
<div
className={`
group flex items-center px-2 h-8 rounded-lg bg-[#FFFAEB] cursor-pointer
${className}
`}
>
<ModelIcon
className='shrink-0 mr-1.5'
provider={currentProvider}
modelName={modelName}
/>
<div className='mr-1 text-[13px] font-medium text-gray-800 truncate'>
{modelName}
</div>
<div className='shrink-0 flex items-center justify-center w-4 h-4'>
<TooltipPlus popupContent={t('common.modelProvider.deprecated')}>
<AlertTriangle className='w-4 h-4 text-[#F79009]' />
</TooltipPlus>
</div>
</div>
)
}
export default ModelTrigger

View File

@ -6,10 +6,10 @@ import {
ModelFeatureTextEnum, ModelFeatureTextEnum,
} from '../declarations' } from '../declarations'
import { import {
MagicBox, // MagicBox,
MagicEyes, MagicEyes,
MagicWand, // MagicWand,
Robot, // Robot,
} from '@/app/components/base/icons/src/vender/solid/mediaAndDevices' } from '@/app/components/base/icons/src/vender/solid/mediaAndDevices'
import TooltipPlus from '@/app/components/base/tooltip-plus' import TooltipPlus from '@/app/components/base/tooltip-plus'
@ -23,41 +23,41 @@ const FeatureIcon: FC<FeatureIconProps> = ({
}) => { }) => {
const { t } = useTranslation() const { t } = useTranslation()
if (feature === ModelFeatureEnum.agentThought) { // if (feature === ModelFeatureEnum.agentThought) {
return ( // return (
<TooltipPlus // <TooltipPlus
popupContent={t('common.modelProvider.featureSupported', { feature: ModelFeatureTextEnum.agentThought })} // popupContent={t('common.modelProvider.featureSupported', { feature: ModelFeatureTextEnum.agentThought })}
> // >
<ModelBadge className={`mr-0.5 !px-0 w-[18px] justify-center text-gray-500 ${className}`}> // <ModelBadge className={`mr-0.5 !px-0 w-[18px] justify-center text-gray-500 ${className}`}>
<Robot className='w-3 h-3' /> // <Robot className='w-3 h-3' />
</ModelBadge> // </ModelBadge>
</TooltipPlus> // </TooltipPlus>
) // )
} // }
if (feature === ModelFeatureEnum.toolCall) { // if (feature === ModelFeatureEnum.toolCall) {
return ( // return (
<TooltipPlus // <TooltipPlus
popupContent={t('common.modelProvider.featureSupported', { feature: ModelFeatureTextEnum.toolCall })} // popupContent={t('common.modelProvider.featureSupported', { feature: ModelFeatureTextEnum.toolCall })}
> // >
<ModelBadge className={`mr-0.5 !px-0 w-[18px] justify-center text-gray-500 ${className}`}> // <ModelBadge className={`mr-0.5 !px-0 w-[18px] justify-center text-gray-500 ${className}`}>
<MagicWand className='w-3 h-3' /> // <MagicWand className='w-3 h-3' />
</ModelBadge> // </ModelBadge>
</TooltipPlus> // </TooltipPlus>
) // )
} // }
if (feature === ModelFeatureEnum.multiToolCall) { // if (feature === ModelFeatureEnum.multiToolCall) {
return ( // return (
<TooltipPlus // <TooltipPlus
popupContent={t('common.modelProvider.featureSupported', { feature: ModelFeatureTextEnum.multiToolCall })} // popupContent={t('common.modelProvider.featureSupported', { feature: ModelFeatureTextEnum.multiToolCall })}
> // >
<ModelBadge className={`mr-0.5 !px-0 w-[18px] justify-center text-gray-500 ${className}`}> // <ModelBadge className={`mr-0.5 !px-0 w-[18px] justify-center text-gray-500 ${className}`}>
<MagicBox className='w-3 h-3' /> // <MagicBox className='w-3 h-3' />
</ModelBadge> // </ModelBadge>
</TooltipPlus> // </TooltipPlus>
) // )
} // }
if (feature === ModelFeatureEnum.vision) { if (feature === ModelFeatureEnum.vision) {
return ( return (

View File

@ -8,6 +8,7 @@ import type {
import { useCurrentProviderAndModel } from '../hooks' import { useCurrentProviderAndModel } from '../hooks'
import ModelTrigger from './model-trigger' import ModelTrigger from './model-trigger'
import EmptyTrigger from './empty-trigger' import EmptyTrigger from './empty-trigger'
import DeprecatedModelTrigger from './deprecated-model-trigger'
import Popup from './popup' import Popup from './popup'
import { import {
PortalToFollowElem, PortalToFollowElem,
@ -77,7 +78,16 @@ const ModelSelector: FC<ModelSelectorProps> = ({
) )
} }
{ {
!currentModel && ( !currentModel && defaultModel && (
<DeprecatedModelTrigger
modelName={defaultModel?.model || ''}
providerName={defaultModel?.provider || ''}
className={triggerClassName}
/>
)
}
{
!defaultModel && (
<EmptyTrigger <EmptyTrigger
open={open} open={open}
className={triggerClassName} className={triggerClassName}

View File

@ -3,10 +3,16 @@ import type {
Model, Model,
ModelItem, ModelItem,
} from '../declarations' } from '../declarations'
import {
MODEL_STATUS_TEXT,
ModelStatusEnum,
} from '../declarations'
import { useLanguage } from '../hooks'
import ModelIcon from '../model-icon' import ModelIcon from '../model-icon'
import ModelName from '../model-name' import ModelName from '../model-name'
// import { AlertTriangle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback' import { AlertTriangle } from '@/app/components/base/icons/src/vender/line/alertsAndFeedback'
import { ChevronDown } from '@/app/components/base/icons/src/vender/line/arrows' import { ChevronDown } from '@/app/components/base/icons/src/vender/line/arrows'
import TooltipPlus from '@/app/components/base/tooltip-plus'
type ModelTriggerProps = { type ModelTriggerProps = {
open: boolean open: boolean
@ -20,12 +26,15 @@ const ModelTrigger: FC<ModelTriggerProps> = ({
model, model,
className, className,
}) => { }) => {
const language = useLanguage()
return ( return (
<div <div
className={` className={`
group flex items-center px-2 h-8 rounded-lg bg-gray-100 hover:bg-gray-200 cursor-pointer group flex items-center px-2 h-8 rounded-lg bg-gray-100 hover:bg-gray-200 cursor-pointer
${className} ${className}
${open && '!bg-gray-200'} ${open && '!bg-gray-200'}
${model.status !== ModelStatusEnum.active && '!bg-[#FFFAEB]'}
`} `}
> >
<ModelIcon <ModelIcon
@ -40,9 +49,19 @@ const ModelTrigger: FC<ModelTriggerProps> = ({
showFeatures showFeatures
/> />
<div className='shrink-0 flex items-center justify-center w-4 h-4'> <div className='shrink-0 flex items-center justify-center w-4 h-4'>
{
model.status !== ModelStatusEnum.active
? (
<TooltipPlus popupContent={MODEL_STATUS_TEXT[model.status][language]}>
<AlertTriangle className='w-4 h-4 text-[#F79009]' />
</TooltipPlus>
)
: (
<ChevronDown <ChevronDown
className='w-3.5 h-3.5 text-gray-500' className='w-3.5 h-3.5 text-gray-500'
/> />
)
}
</div> </div>
</div> </div>
) )

View File

@ -102,7 +102,7 @@ const PopupItem: FC<PopupItemProps> = ({
showFeatures showFeatures
/> />
{ {
defaultModel?.model === modelItem.model && ( defaultModel?.model === modelItem.model && defaultModel.provider === currentProvider.provider && (
<Check className='shrink-0 w-4 h-4 text-primary-600' /> <Check className='shrink-0 w-4 h-4 text-primary-600' />
) )
} }

View File

@ -21,6 +21,7 @@ import { ChevronDownDouble } from '@/app/components/base/icons/src/vender/line/a
import { Loading02 } from '@/app/components/base/icons/src/vender/line/general' import { Loading02 } from '@/app/components/base/icons/src/vender/line/general'
import { fetchModelProviderModelList } from '@/service/common' import { fetchModelProviderModelList } from '@/service/common'
import { useEventEmitterContextContext } from '@/context/event-emitter' import { useEventEmitterContextContext } from '@/context/event-emitter'
import { IS_CE_EDITION } from '@/config'
export const UPDATE_MODEL_PROVIDER_CUSTOM_MODEL_LIST = 'UPDATE_MODEL_PROVIDER_CUSTOM_MODEL_LIST' export const UPDATE_MODEL_PROVIDER_CUSTOM_MODEL_LIST = 'UPDATE_MODEL_PROVIDER_CUSTOM_MODEL_LIST'
type ProviderAddedCardProps = { type ProviderAddedCardProps = {
@ -40,7 +41,7 @@ const ProviderAddedCard: FC<ProviderAddedCardProps> = ({
const configurateMethods = provider.configurate_methods.filter(method => method !== ConfigurateMethodEnum.fetchFromRemote) const configurateMethods = provider.configurate_methods.filter(method => method !== ConfigurateMethodEnum.fetchFromRemote)
const systemConfig = provider.system_configuration const systemConfig = provider.system_configuration
const hasModelList = fetched && !!modelList.length const hasModelList = fetched && !!modelList.length
const showQuota = systemConfig.enabled || ['minimax', 'spark', 'zhipuai', 'anthropic'].includes(provider.provider) const showQuota = systemConfig.enabled && ['minimax', 'spark', 'zhipuai', 'anthropic', 'openai'].includes(provider.provider) && !IS_CE_EDITION
const getModelList = async (providerName: string) => { const getModelList = async (providerName: string) => {
if (loading) if (loading)

View File

@ -78,9 +78,9 @@ const ModelList: FC<ModelListProps> = ({
className={` className={`
group flex items-center pl-2 pr-2.5 h-8 rounded-lg group flex items-center pl-2 pr-2.5 h-8 rounded-lg
${canCustomConfig && 'hover:bg-gray-50'} ${canCustomConfig && 'hover:bg-gray-50'}
${model.deprecated && 'opacity-60'}
`} `}
> >
<div className='shrink-0 mr-2' style={{ background: provider.icon_small[language] }} />
<ModelIcon <ModelIcon
className='shrink-0 mr-2' className='shrink-0 mr-2'
provider={provider} provider={provider}

View File

@ -14,6 +14,7 @@ import {
import PriorityUseTip from './priority-use-tip' import PriorityUseTip from './priority-use-tip'
import { InfoCircle } from '@/app/components/base/icons/src/vender/line/general' import { InfoCircle } from '@/app/components/base/icons/src/vender/line/general'
import Button from '@/app/components/base/button' import Button from '@/app/components/base/button'
import TooltipPlus from '@/app/components/base/tooltip-plus'
type QuotaPanelProps = { type QuotaPanelProps = {
provider: ModelProvider provider: ModelProvider
@ -32,12 +33,19 @@ const QuotaPanel: FC<QuotaPanelProps> = ({
const priorityUseType = provider.preferred_provider_type const priorityUseType = provider.preferred_provider_type
const systemConfig = provider.system_configuration const systemConfig = provider.system_configuration
const currentQuota = systemConfig.enabled && systemConfig.quota_configurations.find(item => item.quota_type === systemConfig.current_quota_type) const currentQuota = systemConfig.enabled && systemConfig.quota_configurations.find(item => item.quota_type === systemConfig.current_quota_type)
const openaiOrAnthropic = ['openai', 'anthropic'].includes(provider.provider)
return ( return (
<div className='group relative shrink-0 min-w-[112px] px-3 py-2 rounded-lg bg-white/[0.3] border-[0.5px] border-black/5'> <div className='group relative shrink-0 min-w-[112px] px-3 py-2 rounded-lg bg-white/[0.3] border-[0.5px] border-black/5'>
<div className='flex items-center mb-2 h-4 text-xs font-medium text-gray-500'> <div className='flex items-center mb-2 h-4 text-xs font-medium text-gray-500'>
{t('common.modelProvider.quota')} {t('common.modelProvider.quota')}
<TooltipPlus popupContent={
openaiOrAnthropic
? t('common.modelProvider.card.tip')
: t('common.modelProvider.quotaTip')
}>
<InfoCircle className='ml-0.5 w-3 h-3 text-gray-400' /> <InfoCircle className='ml-0.5 w-3 h-3 text-gray-400' />
</TooltipPlus>
</div> </div>
{ {
currentQuota && ( currentQuota && (

View File

@ -21,6 +21,7 @@ import s from './index.module.css'
import { Plus, Settings01 } from '@/app/components/base/icons/src/vender/line/general' import { Plus, Settings01 } from '@/app/components/base/icons/src/vender/line/general'
import { CoinsStacked01 } from '@/app/components/base/icons/src/vender/line/financeAndECommerce' import { CoinsStacked01 } from '@/app/components/base/icons/src/vender/line/financeAndECommerce'
import Button from '@/app/components/base/button' import Button from '@/app/components/base/button'
import { IS_CE_EDITION } from '@/config'
type ProviderCardProps = { type ProviderCardProps = {
provider: ModelProvider provider: ModelProvider
@ -54,7 +55,7 @@ const ProviderCard: FC<ProviderCardProps> = ({
} }
const handleFreeQuota = useFreeQuota(handleFreeQuotaSuccess) const handleFreeQuota = useFreeQuota(handleFreeQuotaSuccess)
const configurateMethods = provider.configurate_methods.filter(method => method !== ConfigurateMethodEnum.fetchFromRemote) const configurateMethods = provider.configurate_methods.filter(method => method !== ConfigurateMethodEnum.fetchFromRemote)
const canGetFreeQuota = ['mininmax', 'spark', 'zhipuai'].includes(provider.provider) const canGetFreeQuota = ['mininmax', 'spark', 'zhipuai'].includes(provider.provider) && !IS_CE_EDITION
return ( return (
<div <div
@ -135,7 +136,7 @@ const ProviderCard: FC<ProviderCardProps> = ({
}) })
} }
{ {
provider.provider === 'anthropic' && ( provider.provider === 'anthropic' && !IS_CE_EDITION && (
<Button <Button
className='h-7 text-xs text-gray-700' className='h-7 text-xs text-gray-700'
onClick={handlePay} onClick={handlePay}

View File

@ -43,6 +43,7 @@ import Confirm from '@/app/components/base/confirm'
import type { VisionFile, VisionSettings } from '@/types/app' import type { VisionFile, VisionSettings } from '@/types/app'
import { Resolution, TransferMethod } from '@/types/app' import { Resolution, TransferMethod } from '@/types/app'
import { fetchFileUploadConfig } from '@/service/common' import { fetchFileUploadConfig } from '@/service/common'
import type { Annotation as AnnotationType } from '@/models/log'
export type IMainProps = { export type IMainProps = {
isInstalledApp?: boolean isInstalledApp?: boolean
@ -582,12 +583,30 @@ const Main: FC<IMainProps> = ({
} }
setResponsingFalse() setResponsingFalse()
}, },
onMessageEnd: isInstalledApp onMessageEnd: (messageEnd) => {
? (messageEnd) => { if (messageEnd.metadata?.annotation_reply) {
responseItem.id = messageEnd.id
responseItem.annotation = ({
id: messageEnd.metadata.annotation_reply.id,
authorName: messageEnd.metadata.annotation_reply.account.name,
} as AnnotationType)
const newListWithAnswer = produce(
getChatList().filter(item => item.id !== responseItem.id && item.id !== placeholderAnswerId),
(draft) => {
if (!draft.find(item => item.id === questionId))
draft.push({ ...questionItem })
draft.push({
...responseItem,
})
})
setChatList(newListWithAnswer)
return
}
// not support show citation
// responseItem.citation = messageEnd.retriever_resources
if (!isInstalledApp) if (!isInstalledApp)
return return
responseItem.citation = messageEnd.retriever_resources
const newListWithAnswer = produce( const newListWithAnswer = produce(
getChatList().filter(item => item.id !== responseItem.id && item.id !== placeholderAnswerId), getChatList().filter(item => item.id !== responseItem.id && item.id !== placeholderAnswerId),
(draft) => { (draft) => {
@ -597,8 +616,7 @@ const Main: FC<IMainProps> = ({
draft.push({ ...responseItem }) draft.push({ ...responseItem })
}) })
setChatList(newListWithAnswer) setChatList(newListWithAnswer)
} },
: undefined,
onMessageReplace: (messageReplace) => { onMessageReplace: (messageReplace) => {
if (isInstalledApp) { if (isInstalledApp) {
responseItem.content = messageReplace.answer responseItem.content = messageReplace.answer
@ -615,22 +633,6 @@ const Main: FC<IMainProps> = ({
)) ))
} }
}, },
onAnnotationReply: (annotationReply) => {
responseItem.content = annotationReply.answer
const newListWithAnswer = produce(
getChatList().filter(item => item.id !== responseItem.id && item.id !== placeholderAnswerId),
(draft) => {
if (!draft.find(item => item.id === questionId))
draft.push({ ...questionItem })
draft.push({
...responseItem,
id: annotationReply.id,
})
})
setChatList(newListWithAnswer)
tempNewConversationId = annotationReply.conversation_id
},
onError() { onError() {
setResponsingFalse() setResponsingFalse()
// role back placeholder answer // role back placeholder answer

View File

@ -300,6 +300,9 @@ const translation = {
buyQuota: 'Buy Quota', buyQuota: 'Buy Quota',
getFreeTokens: 'Get free Tokens', getFreeTokens: 'Get free Tokens',
priorityUsing: 'Prioritize using', priorityUsing: 'Prioritize using',
deprecated: 'Deprecated',
confirmDelete: 'confirm deletion?',
quotaTip: 'Remaining available free tokens',
}, },
dataSource: { dataSource: {
add: 'Add a data source', add: 'Add a data source',

View File

@ -300,6 +300,9 @@ const translation = {
buyQuota: '购买额度', buyQuota: '购买额度',
getFreeTokens: '获得免费 Tokens', getFreeTokens: '获得免费 Tokens',
priorityUsing: '优先使用', priorityUsing: '优先使用',
deprecated: '已弃用',
confirmDelete: '确认删除?',
quotaTip: '剩余免费额度',
}, },
dataSource: { dataSource: {
add: '添加数据源', add: '添加数据源',

View File

@ -47,7 +47,6 @@ type IOtherOptions = {
onThought?: IOnThought onThought?: IOnThought
onMessageEnd?: IOnMessageEnd onMessageEnd?: IOnMessageEnd
onMessageReplace?: IOnMessageReplace onMessageReplace?: IOnMessageReplace
onAnnotationReply?: IOnAnnotationReply
onError?: IOnError onError?: IOnError
onCompleted?: IOnCompleted // for stream onCompleted?: IOnCompleted // for stream
getAbortController?: (abortController: AbortController) => void getAbortController?: (abortController: AbortController) => void
@ -81,7 +80,7 @@ export function format(text: string) {
return res.replaceAll('\n', '<br/>').replaceAll('```', '') return res.replaceAll('\n', '<br/>').replaceAll('```', '')
} }
const handleStream = (response: Response, onData: IOnData, onCompleted?: IOnCompleted, onThought?: IOnThought, onMessageEnd?: IOnMessageEnd, onMessageReplace?: IOnMessageReplace, onAnnotationReply?: IOnAnnotationReply) => { const handleStream = (response: Response, onData: IOnData, onCompleted?: IOnCompleted, onThought?: IOnThought, onMessageEnd?: IOnMessageEnd, onMessageReplace?: IOnMessageReplace) => {
if (!response.ok) if (!response.ok)
throw new Error('Network response was not ok') throw new Error('Network response was not ok')
@ -137,14 +136,12 @@ const handleStream = (response: Response, onData: IOnData, onCompleted?: IOnComp
onThought?.(bufferObj as ThoughtItem) onThought?.(bufferObj as ThoughtItem)
} }
else if (bufferObj.event === 'message_end') { else if (bufferObj.event === 'message_end') {
console.log(bufferObj)
onMessageEnd?.(bufferObj as MessageEnd) onMessageEnd?.(bufferObj as MessageEnd)
} }
else if (bufferObj.event === 'message_replace') { else if (bufferObj.event === 'message_replace') {
onMessageReplace?.(bufferObj as MessageReplace) onMessageReplace?.(bufferObj as MessageReplace)
} }
else if (bufferObj.event === 'annotation') {
onAnnotationReply?.(bufferObj as AnnotationReply)
}
} }
}) })
buffer = lines[lines.length - 1] buffer = lines[lines.length - 1]
@ -350,7 +347,7 @@ export const upload = (options: any, isPublicAPI?: boolean, url?: string): Promi
}) })
} }
export const ssePost = (url: string, fetchOptions: FetchOptionType, { isPublicAPI = false, onData, onCompleted, onThought, onMessageEnd, onMessageReplace, onAnnotationReply, onError, getAbortController }: IOtherOptions) => { export const ssePost = (url: string, fetchOptions: FetchOptionType, { isPublicAPI = false, onData, onCompleted, onThought, onMessageEnd, onMessageReplace, onError, getAbortController }: IOtherOptions) => {
const abortController = new AbortController() const abortController = new AbortController()
const options = Object.assign({}, baseOptions, { const options = Object.assign({}, baseOptions, {
@ -382,14 +379,13 @@ export const ssePost = (url: string, fetchOptions: FetchOptionType, { isPublicAP
} }
return handleStream(res, (str: string, isFirstMessage: boolean, moreInfo: IOnDataMoreInfo) => { return handleStream(res, (str: string, isFirstMessage: boolean, moreInfo: IOnDataMoreInfo) => {
if (moreInfo.errorMessage) { if (moreInfo.errorMessage) {
// debugger
onError?.(moreInfo.errorMessage, moreInfo.errorCode) onError?.(moreInfo.errorMessage, moreInfo.errorCode)
if (moreInfo.errorMessage !== 'AbortError: The user aborted a request.') if (moreInfo.errorMessage !== 'AbortError: The user aborted a request.')
Toast.notify({ type: 'error', message: moreInfo.errorMessage }) Toast.notify({ type: 'error', message: moreInfo.errorMessage })
return return
} }
onData?.(str, isFirstMessage, moreInfo) onData?.(str, isFirstMessage, moreInfo)
}, onCompleted, onThought, onMessageEnd, onMessageReplace, onAnnotationReply) }, onCompleted, onThought, onMessageEnd, onMessageReplace)
}).catch((e) => { }).catch((e) => {
if (e.toString() !== 'AbortError: The user aborted a request.') if (e.toString() !== 'AbortError: The user aborted a request.')
Toast.notify({ type: 'error', message: e }) Toast.notify({ type: 'error', message: e })

View File

@ -1,4 +1,4 @@
import type { IOnAnnotationReply, IOnCompleted, IOnData, IOnError, IOnMessageEnd, IOnMessageReplace } from './base' import type { IOnCompleted, IOnData, IOnError, IOnMessageEnd, IOnMessageReplace } from './base'
import { get, post, ssePost } from './base' import { get, post, ssePost } from './base'
import type { ChatPromptConfig, CompletionPromptConfig } from '@/models/debug' import type { ChatPromptConfig, CompletionPromptConfig } from '@/models/debug'
import type { ModelModeType } from '@/types/app' import type { ModelModeType } from '@/types/app'
@ -10,12 +10,11 @@ export type AutomaticRes = {
opening_statement: string opening_statement: string
} }
export const sendChatMessage = async (appId: string, body: Record<string, any>, { onData, onCompleted, onError, getAbortController, onMessageEnd, onMessageReplace, onAnnotationReply }: { export const sendChatMessage = async (appId: string, body: Record<string, any>, { onData, onCompleted, onError, getAbortController, onMessageEnd, onMessageReplace }: {
onData: IOnData onData: IOnData
onCompleted: IOnCompleted onCompleted: IOnCompleted
onMessageEnd: IOnMessageEnd onMessageEnd: IOnMessageEnd
onMessageReplace: IOnMessageReplace onMessageReplace: IOnMessageReplace
onAnnotationReply: IOnAnnotationReply
onError: IOnError onError: IOnError
getAbortController?: (abortController: AbortController) => void getAbortController?: (abortController: AbortController) => void
}) => { }) => {
@ -24,7 +23,7 @@ export const sendChatMessage = async (appId: string, body: Record<string, any>,
...body, ...body,
response_mode: 'streaming', response_mode: 'streaming',
}, },
}, { onData, onCompleted, onError, getAbortController, onMessageEnd, onMessageReplace, onAnnotationReply }) }, { onData, onCompleted, onError, getAbortController, onMessageEnd, onMessageReplace })
} }
export const stopChatMessageResponding = async (appId: string, taskId: string) => { export const stopChatMessageResponding = async (appId: string, taskId: string) => {

View File

@ -1,4 +1,4 @@
import type { IOnAnnotationReply, IOnCompleted, IOnData, IOnError, IOnMessageEnd, IOnMessageReplace } from './base' import type { IOnCompleted, IOnData, IOnError, IOnMessageEnd, IOnMessageReplace } from './base'
import { import {
del as consoleDel, get as consoleGet, patch as consolePatch, post as consolePost, del as consoleDel, get as consoleGet, patch as consolePatch, post as consolePost,
delPublic as del, getPublic as get, patchPublic as patch, postPublic as post, ssePost, delPublic as del, getPublic as get, patchPublic as patch, postPublic as post, ssePost,
@ -22,13 +22,12 @@ function getUrl(url: string, isInstalledApp: boolean, installedAppId: string) {
return isInstalledApp ? `installed-apps/${installedAppId}/${url.startsWith('/') ? url.slice(1) : url}` : url return isInstalledApp ? `installed-apps/${installedAppId}/${url.startsWith('/') ? url.slice(1) : url}` : url
} }
export const sendChatMessage = async (body: Record<string, any>, { onData, onCompleted, onError, getAbortController, onMessageEnd, onMessageReplace, onAnnotationReply }: { export const sendChatMessage = async (body: Record<string, any>, { onData, onCompleted, onError, getAbortController, onMessageEnd, onMessageReplace }: {
onData: IOnData onData: IOnData
onCompleted: IOnCompleted onCompleted: IOnCompleted
onError: IOnError onError: IOnError
onMessageEnd?: IOnMessageEnd onMessageEnd?: IOnMessageEnd
onMessageReplace?: IOnMessageReplace onMessageReplace?: IOnMessageReplace
onAnnotationReply: IOnAnnotationReply
getAbortController?: (abortController: AbortController) => void getAbortController?: (abortController: AbortController) => void
}, isInstalledApp: boolean, installedAppId = '') => { }, isInstalledApp: boolean, installedAppId = '') => {
return ssePost(getUrl('chat-messages', isInstalledApp, installedAppId), { return ssePost(getUrl('chat-messages', isInstalledApp, installedAppId), {
@ -36,7 +35,7 @@ export const sendChatMessage = async (body: Record<string, any>, { onData, onCom
...body, ...body,
response_mode: 'streaming', response_mode: 'streaming',
}, },
}, { onData, onCompleted, isPublicAPI: !isInstalledApp, onError, getAbortController, onMessageEnd, onMessageReplace, onAnnotationReply }) }, { onData, onCompleted, isPublicAPI: !isInstalledApp, onError, getAbortController, onMessageEnd, onMessageReplace })
} }
export const stopChatMessageResponding = async (appId: string, taskId: string, isInstalledApp: boolean, installedAppId = '') => { export const stopChatMessageResponding = async (appId: string, taskId: string, isInstalledApp: boolean, installedAppId = '') => {

File diff suppressed because it is too large Load Diff