Co-authored-by: Joel <iamjoel007@gmail.com>tags/0.4.0
| 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} |
| } | } | ||||
| }, | }, | ||||
| 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 = ({ | ||||
| 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 |
| ...visionConfig, | ...visionConfig, | ||||
| enabled: supportVision, | enabled: supportVision, | ||||
| }, true) | }, true) | ||||
| setCompletionParams({}) | |||||
| } | } | ||||
| const isShowVisionConfig = !!currModel?.features?.includes(ModelFeatureEnum.vision) | const isShowVisionConfig = !!currModel?.features?.includes(ModelFeatureEnum.vision) | ||||
| 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> |
| </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> |
| noPermission = 'no-permission', | noPermission = 'no-permission', | ||||
| } | } | ||||
| export const MODEL_STATUS_TEXT = { | |||||
| [ModelStatusEnum.noConfigure]: { | |||||
| export const MODEL_STATUS_TEXT: { [k: string]: TypeWithI18N } = { | |||||
| '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: '无使用权限', | ||||
| }, | }, |
| 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, | ||||
| 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, |
| 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> = ({ | ||||
| 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) { | ||||
| 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'> | ||||
| } | } | ||||
| </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> | ||||
| 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'> | ||||
| 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}`} |
| 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, | ||||
| onFocus, | onFocus, | ||||
| placeholder, | placeholder, | ||||
| validated, | validated, | ||||
| className, | |||||
| disabled, | |||||
| }) => { | }) => { | ||||
| return ( | return ( | ||||
| <div className='relative'> | <div className='relative'> | ||||
| 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 && ( |
| 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'> | ||||
| { | { | ||||
| { | { | ||||
| 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} |
| `} | `} | ||||
| > | > | ||||
| <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> |
| 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 { | ||||
| 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 { | ||||
| 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 | ||||
| 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: [], | ||||
| }, | }, | ||||
| label: { | label: { | ||||
| en_US: 'Stop sequences', | en_US: 'Stop sequences', | ||||
| zh_Hans: '停止序列 stop_sequences', | |||||
| zh_Hans: '停止序列', | |||||
| }, | }, | ||||
| name: 'stop', | name: 'stop', | ||||
| required: false, | required: false, | ||||
| 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 { | ||||
| { 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({ | ||||
| }) | }) | ||||
| } | } | ||||
| 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 } | ||||
| } | } | ||||
| } | } | ||||
| 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} | ||||
| /> | /> | ||||
| ) | ) | ||||
| } | } | ||||
| { | |||||
| !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 | ||||
| ? ( | ? ( | ||||
| <AlertTriangle className='w-4 h-4 text-[#F79009]' /> | |||||
| <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]' /> | |||||
| </TooltipPlus> | |||||
| ) | ) | ||||
| : ( | : ( | ||||
| <SlidersH className='w-4 h-4 text-indigo-600' /> | <SlidersH className='w-4 h-4 text-indigo-600' /> | ||||
| <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')} | ||||
| onSelect={handleChangeModel} | onSelect={handleChangeModel} | ||||
| /> | /> | ||||
| </div> | </div> | ||||
| <div className='my-5 h-[1px] bg-gray-100' /> | |||||
| { | |||||
| !!parameterRules.length && ( | |||||
| <div className='my-5 h-[1px] bg-gray-100' /> | |||||
| ) | |||||
| } | |||||
| { | { | ||||
| isLoading && ( | isLoading && ( | ||||
| <Loading /> | |||||
| <div className='mt-5'><Loading /></div> | |||||
| ) | ) | ||||
| } | } | ||||
| { | { | ||||
| !isLoading && ( | |||||
| !isLoading && !!parameterRules.length && ( | |||||
| [ | [ | ||||
| ...parameterRules, | ...parameterRules, | ||||
| ...(isAdvancedMode ? [stopParameerRule] : []), | ...(isAdvancedMode ? [stopParameerRule] : []), |
| 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) | ||||
| if (onSwitch) { | if (onSwitch) { | ||||
| let assignValue: ParameterValue = localValue | let assignValue: ParameterValue = localValue | ||||
| if (isNullOrUndefined(localValue)) { | |||||
| if (parameterRule.type === 'int' || parameterRule.type === 'float') | |||||
| 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 : [] | |||||
| } | |||||
| if (isNullOrUndefined(localValue)) | |||||
| assignValue = getDefaultValue() | |||||
| onSwitch(checked, assignValue) | onSwitch(checked, assignValue) | ||||
| } | } | ||||
| <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}`} | ||||
| 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> | ||||
| 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> | ||||
| <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} | ||||
| /> | /> | ||||
| ) | ) | ||||
| 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' | ||||
| 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' | ||||
| /> | /> |
| 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 |
| ModelFeatureTextEnum, | ModelFeatureTextEnum, | ||||
| } from '../declarations' | } from '../declarations' | ||||
| import { | import { | ||||
| MagicBox, | |||||
| // MagicBox, | |||||
| MagicEyes, | MagicEyes, | ||||
| MagicWand, | |||||
| Robot, | |||||
| // MagicWand, | |||||
| // 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' | ||||
| }) => { | }) => { | ||||
| const { t } = useTranslation() | const { t } = useTranslation() | ||||
| if (feature === ModelFeatureEnum.agentThought) { | |||||
| return ( | |||||
| <TooltipPlus | |||||
| popupContent={t('common.modelProvider.featureSupported', { feature: ModelFeatureTextEnum.agentThought })} | |||||
| > | |||||
| <ModelBadge className={`mr-0.5 !px-0 w-[18px] justify-center text-gray-500 ${className}`}> | |||||
| <Robot className='w-3 h-3' /> | |||||
| </ModelBadge> | |||||
| </TooltipPlus> | |||||
| ) | |||||
| } | |||||
| // if (feature === ModelFeatureEnum.agentThought) { | |||||
| // return ( | |||||
| // <TooltipPlus | |||||
| // popupContent={t('common.modelProvider.featureSupported', { feature: ModelFeatureTextEnum.agentThought })} | |||||
| // > | |||||
| // <ModelBadge className={`mr-0.5 !px-0 w-[18px] justify-center text-gray-500 ${className}`}> | |||||
| // <Robot className='w-3 h-3' /> | |||||
| // </ModelBadge> | |||||
| // </TooltipPlus> | |||||
| // ) | |||||
| // } | |||||
| if (feature === ModelFeatureEnum.toolCall) { | |||||
| return ( | |||||
| <TooltipPlus | |||||
| popupContent={t('common.modelProvider.featureSupported', { feature: ModelFeatureTextEnum.toolCall })} | |||||
| > | |||||
| <ModelBadge className={`mr-0.5 !px-0 w-[18px] justify-center text-gray-500 ${className}`}> | |||||
| <MagicWand className='w-3 h-3' /> | |||||
| </ModelBadge> | |||||
| </TooltipPlus> | |||||
| ) | |||||
| } | |||||
| // if (feature === ModelFeatureEnum.toolCall) { | |||||
| // return ( | |||||
| // <TooltipPlus | |||||
| // popupContent={t('common.modelProvider.featureSupported', { feature: ModelFeatureTextEnum.toolCall })} | |||||
| // > | |||||
| // <ModelBadge className={`mr-0.5 !px-0 w-[18px] justify-center text-gray-500 ${className}`}> | |||||
| // <MagicWand className='w-3 h-3' /> | |||||
| // </ModelBadge> | |||||
| // </TooltipPlus> | |||||
| // ) | |||||
| // } | |||||
| if (feature === ModelFeatureEnum.multiToolCall) { | |||||
| return ( | |||||
| <TooltipPlus | |||||
| popupContent={t('common.modelProvider.featureSupported', { feature: ModelFeatureTextEnum.multiToolCall })} | |||||
| > | |||||
| <ModelBadge className={`mr-0.5 !px-0 w-[18px] justify-center text-gray-500 ${className}`}> | |||||
| <MagicBox className='w-3 h-3' /> | |||||
| </ModelBadge> | |||||
| </TooltipPlus> | |||||
| ) | |||||
| } | |||||
| // if (feature === ModelFeatureEnum.multiToolCall) { | |||||
| // return ( | |||||
| // <TooltipPlus | |||||
| // popupContent={t('common.modelProvider.featureSupported', { feature: ModelFeatureTextEnum.multiToolCall })} | |||||
| // > | |||||
| // <ModelBadge className={`mr-0.5 !px-0 w-[18px] justify-center text-gray-500 ${className}`}> | |||||
| // <MagicBox className='w-3 h-3' /> | |||||
| // </ModelBadge> | |||||
| // </TooltipPlus> | |||||
| // ) | |||||
| // } | |||||
| if (feature === ModelFeatureEnum.vision) { | if (feature === ModelFeatureEnum.vision) { | ||||
| return ( | return ( |
| 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, | ||||
| ) | ) | ||||
| } | } | ||||
| { | { | ||||
| !currentModel && ( | |||||
| !currentModel && defaultModel && ( | |||||
| <DeprecatedModelTrigger | |||||
| modelName={defaultModel?.model || ''} | |||||
| providerName={defaultModel?.provider || ''} | |||||
| className={triggerClassName} | |||||
| /> | |||||
| ) | |||||
| } | |||||
| { | |||||
| !defaultModel && ( | |||||
| <EmptyTrigger | <EmptyTrigger | ||||
| open={open} | open={open} | ||||
| className={triggerClassName} | className={triggerClassName} |
| 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 | ||||
| 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 | ||||
| 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'> | ||||
| <ChevronDown | |||||
| className='w-3.5 h-3.5 text-gray-500' | |||||
| /> | |||||
| { | |||||
| model.status !== ModelStatusEnum.active | |||||
| ? ( | |||||
| <TooltipPlus popupContent={MODEL_STATUS_TEXT[model.status][language]}> | |||||
| <AlertTriangle className='w-4 h-4 text-[#F79009]' /> | |||||
| </TooltipPlus> | |||||
| ) | |||||
| : ( | |||||
| <ChevronDown | |||||
| className='w-3.5 h-3.5 text-gray-500' | |||||
| /> | |||||
| ) | |||||
| } | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| ) | ) |
| 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' /> | ||||
| ) | ) | ||||
| } | } |
| 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 = { | ||||
| 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) |
| 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} |
| 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 | ||||
| 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')} | ||||
| <InfoCircle className='ml-0.5 w-3 h-3 text-gray-400' /> | |||||
| <TooltipPlus popupContent={ | |||||
| openaiOrAnthropic | |||||
| ? t('common.modelProvider.card.tip') | |||||
| : t('common.modelProvider.quotaTip') | |||||
| }> | |||||
| <InfoCircle className='ml-0.5 w-3 h-3 text-gray-400' /> | |||||
| </TooltipPlus> | |||||
| </div> | </div> | ||||
| { | { | ||||
| currentQuota && ( | currentQuota && ( |
| 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 | ||||
| } | } | ||||
| 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 | ||||
| }) | }) | ||||
| } | } | ||||
| { | { | ||||
| 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} |
| 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 | ||||
| } | } | ||||
| setResponsingFalse() | setResponsingFalse() | ||||
| }, | }, | ||||
| onMessageEnd: isInstalledApp | |||||
| ? (messageEnd) => { | |||||
| if (!isInstalledApp) | |||||
| return | |||||
| responseItem.citation = messageEnd.retriever_resources | |||||
| onMessageEnd: (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( | 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) => { | ||||
| if (!draft.find(item => item.id === questionId)) | if (!draft.find(item => item.id === questionId)) | ||||
| draft.push({ ...questionItem }) | draft.push({ ...questionItem }) | ||||
| draft.push({ ...responseItem }) | |||||
| draft.push({ | |||||
| ...responseItem, | |||||
| }) | |||||
| }) | }) | ||||
| setChatList(newListWithAnswer) | setChatList(newListWithAnswer) | ||||
| return | |||||
| } | } | ||||
| : undefined, | |||||
| // not support show citation | |||||
| // responseItem.citation = messageEnd.retriever_resources | |||||
| if (!isInstalledApp) | |||||
| return | |||||
| 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) | |||||
| }, | |||||
| onMessageReplace: (messageReplace) => { | onMessageReplace: (messageReplace) => { | ||||
| if (isInstalledApp) { | if (isInstalledApp) { | ||||
| responseItem.content = messageReplace.answer | responseItem.content = messageReplace.answer | ||||
| )) | )) | ||||
| } | } | ||||
| }, | }, | ||||
| 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 |
| 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', |
| buyQuota: '购买额度', | buyQuota: '购买额度', | ||||
| getFreeTokens: '获得免费 Tokens', | getFreeTokens: '获得免费 Tokens', | ||||
| priorityUsing: '优先使用', | priorityUsing: '优先使用', | ||||
| deprecated: '已弃用', | |||||
| confirmDelete: '确认删除?', | |||||
| quotaTip: '剩余免费额度', | |||||
| }, | }, | ||||
| dataSource: { | dataSource: { | ||||
| add: '添加数据源', | add: '添加数据源', |
| 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 | ||||
| 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') | ||||
| 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] | ||||
| }) | }) | ||||
| } | } | ||||
| 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, { | ||||
| } | } | ||||
| 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 }) |
| 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' | ||||
| 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 | ||||
| }) => { | }) => { | ||||
| ...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) => { |
| 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, | ||||
| 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), { | ||||
| ...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 = '') => { |