From a65b1a5ab0b1077a5982ea5344f47e0b9487c609 Mon Sep 17 00:00:00 2001 From: StyleZhang Date: Thu, 7 Dec 2023 09:52:13 +0800 Subject: [PATCH 01/46] feat: new model provider --- .../model-provider-page/declarations.ts | 127 ++++++++++++++++++ .../model-provider-page/index.tsx | 39 ++++++ .../model-provider-page/model-badge/index.tsx | 19 +++ .../provider-added-card/credential-panel.tsx | 26 ++++ .../provider-added-card/index.tsx | 64 +++++++++ .../provider-added-card/model-list.tsx | 80 +++++++++++ .../provider-added-card/quota-panel.tsx | 25 ++++ .../provider-added-card/tab.tsx | 45 +++++++ .../provider-card/index.tsx | 58 ++++++++ .../model-provider-page/utils.ts | 7 + web/service/common.ts | 7 +- 11 files changed, 494 insertions(+), 3 deletions(-) create mode 100644 web/app/components/header/account-setting/model-provider-page/declarations.ts create mode 100644 web/app/components/header/account-setting/model-provider-page/index.tsx create mode 100644 web/app/components/header/account-setting/model-provider-page/model-badge/index.tsx create mode 100644 web/app/components/header/account-setting/model-provider-page/provider-added-card/credential-panel.tsx create mode 100644 web/app/components/header/account-setting/model-provider-page/provider-added-card/index.tsx create mode 100644 web/app/components/header/account-setting/model-provider-page/provider-added-card/model-list.tsx create mode 100644 web/app/components/header/account-setting/model-provider-page/provider-added-card/quota-panel.tsx create mode 100644 web/app/components/header/account-setting/model-provider-page/provider-added-card/tab.tsx create mode 100644 web/app/components/header/account-setting/model-provider-page/provider-card/index.tsx create mode 100644 web/app/components/header/account-setting/model-provider-page/utils.ts diff --git a/web/app/components/header/account-setting/model-provider-page/declarations.ts b/web/app/components/header/account-setting/model-provider-page/declarations.ts new file mode 100644 index 00000000000000..32e213962720b7 --- /dev/null +++ b/web/app/components/header/account-setting/model-provider-page/declarations.ts @@ -0,0 +1,127 @@ +export type TypeWithI18N = { + 'en_US': T + 'zh_Hans': T +} + +export enum FormTypeEnum { + textInput = 'text-input', + secretInput = 'secret-input', + select = 'select', + radio = 'radio', +} + +export type FormOption = { + label: TypeWithI18N + value: string +} + +export enum ModelTypeEnum { + textGeneration = 'text-generation', + textEmbedding = 'text-embedding', + rerank = 'rerank', + speech2text = 'speech2text', + moderation = 'moderation', +} + +export enum ConfigurateMethodEnum { + predefinedModel = 'predefined-model', + customizableModel = 'customizable-model', + fetchFromRemote = 'fetch-from-remote', +} + +export enum ModelFeature { + toolCall = 'tool-call', + multiToolCall = 'multi-tool-call', + agentThought = 'agent_thought', + vision = 'vision', +} + +export enum ModelStatus { + active = 'active', + noConfigure = 'no-configure', + quotaExceeded = 'quota-exceeded', + noPermission = 'no-permission', +} + +export type FormShowOnObject = { + variable: string + value: string +} + +export type CredentialFormSchemaMap = { + [FormTypeEnum.textInput]: { max_length: number; placeholder: TypeWithI18N } + [FormTypeEnum.select]: { options: FormOption[] } + [FormTypeEnum.radio]: { options: FormOption[] } + [FormTypeEnum.secretInput]: {} +} + +export type CredentialFormSchema = { + variable: string + label: TypeWithI18N + type: FormTypeEnum + required: boolean + default: string + show_on: FormShowOnObject[] +} & CredentialFormSchemaMap[FormTypeEnum] + +export type Model = { + model: string + label: TypeWithI18N + model_type: ModelTypeEnum + features: ModelFeature[] + configurate_method: ConfigurateMethodEnum + status: ModelStatus +} + +export enum PreferredProviderTypeEnum { + system = 'system', + custom = 'custom', +} + +export enum CurrentSystemQuotaTypeEnum { + trial = 'trial', + free = 'free', + paid = 'paid', +} + +export enum QuotaUnitEnum { + times = 'times', + tokens = 'tokens', +} + +export type QuotaConfiguration = { + quota_type: CurrentSystemQuotaTypeEnum + quota_unit: QuotaUnitEnum + quota_limit: number + quota_used: number + last_used: number + is_valid: boolean +} + +export type ModelProvider = { + provider: string + label: TypeWithI18N + description?: TypeWithI18N + help_url: TypeWithI18N + icon_small: TypeWithI18N + icon_large: TypeWithI18N + background?: string + supported_models_types: ModelTypeEnum[] + configurate_method: ConfigurateMethodEnum + provider_credential_schema: { + credential_form_schemas: CredentialFormSchema[] + } + model_credential_schema: { + model: { + label: TypeWithI18N + placeholder: TypeWithI18N + } + crenential_form_schemas: CredentialFormSchema[] + } + preferred_provider_type: PreferredProviderTypeEnum + system_configuration: { + enabled: boolean + current_system_quota_type: CurrentSystemQuotaTypeEnum + quota_configurations: QuotaConfiguration[] + } +} diff --git a/web/app/components/header/account-setting/model-provider-page/index.tsx b/web/app/components/header/account-setting/model-provider-page/index.tsx new file mode 100644 index 00000000000000..aaf4a04ef3c1f6 --- /dev/null +++ b/web/app/components/header/account-setting/model-provider-page/index.tsx @@ -0,0 +1,39 @@ +import useSWR from 'swr' +import { useTranslation } from 'react-i18next' +import SystemModel from '../model-page/system-model' +import { fetchModelProviders } from '@/service/common' +import { useProviderContext } from '@/context/provider-context' +import { AlertTriangle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback' + +const ModelProviderPage = () => { + const { t } = useTranslation() + const { + updateModelList, + textGenerationDefaultModel, + embeddingsDefaultModel, + speech2textDefaultModel, + rerankDefaultModel, + } = useProviderContext() + const { data: providers, mutate: mutateProviders } = useSWR('/workspaces/current/model-providers', fetchModelProviders) + const defaultModelNotConfigured = !textGenerationDefaultModel && !embeddingsDefaultModel && !speech2textDefaultModel && !rerankDefaultModel + + return ( +
+
+ { + defaultModelNotConfigured + ? ( +
+ + {t('common.modelProvider.notConfigured')} +
+ ) + :
{t('common.modelProvider.models')}
+ } + mutateProviders()} /> +
+
+ ) +} + +export default ModelProviderPage diff --git a/web/app/components/header/account-setting/model-provider-page/model-badge/index.tsx b/web/app/components/header/account-setting/model-provider-page/model-badge/index.tsx new file mode 100644 index 00000000000000..342c71ec2811a5 --- /dev/null +++ b/web/app/components/header/account-setting/model-provider-page/model-badge/index.tsx @@ -0,0 +1,19 @@ +import type { FC } from 'react' + +type ModelBadgeProps = { + text: string +} +const ModelBadge: FC = ({ + text, +}) => { + return ( +
+ {text} +
+ ) +} + +export default ModelBadge diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/credential-panel.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/credential-panel.tsx new file mode 100644 index 00000000000000..25205086da3e3a --- /dev/null +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/credential-panel.tsx @@ -0,0 +1,26 @@ +import { useTranslation } from 'react-i18next' +import Indicator from '@/app/components/header/indicator' +import { Settings01 } from '@/app/components/base/icons/src/vender/line/general' + +const CredentialPanel = () => { + const { t } = useTranslation() + + return ( +
+
+ API-KEY + +
+
+ + {t('common.operation.setup')} +
+
+ ) +} + +export default CredentialPanel diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/index.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/index.tsx new file mode 100644 index 00000000000000..3fb2d12c42f342 --- /dev/null +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/index.tsx @@ -0,0 +1,64 @@ +import type { FC } from 'react' +import { useState } from 'react' +import { useContext } from 'use-context-selector' +import type { ModelProvider } from '../declarations' +import { languageMaps } from '../utils' +import ModelBadge from '../model-badge' +import CredentialPanel from './credential-panel' +import QuotaPanel from './quota-panel' +import ModelList from './model-list' +import I18n from '@/context/i18n' + +type ProviderAddedCardProps = { + provider: ModelProvider +} +const ProviderAddedCard: FC = ({ + provider, +}) => { + const { locale } = useContext(I18n) + const language = languageMaps[locale] + const [collapsed, setCollapsed] = useState(false) + + return ( +
+
+
+
+
+ { + provider.supported_models_types.map(modelType => ( + + )) + } +
+
+ + +
+ { + collapsed && ( +
setCollapsed(false)} + > + 5 Models +
+ ) + } + { + !collapsed && ( + setCollapsed(true)} + /> + ) + } +
+ ) +} + +export default ProviderAddedCard diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-list.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-list.tsx new file mode 100644 index 00000000000000..ac37ebc91d01c3 --- /dev/null +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-list.tsx @@ -0,0 +1,80 @@ +import type { FC } from 'react' +import { useContext } from 'use-context-selector' +import type { Model, ModelProvider } from '../declarations' +import { languageMaps } from '../utils' +import ModelBadge from '../model-badge' +import Tab from './tab' +import Indicator from '@/app/components/header/indicator' +import { Plus, Settings01 } from '@/app/components/base/icons/src/vender/line/general' +import { ChevronDownDouble } from '@/app/components/base/icons/src/vender/line/arrows' +import I18n from '@/context/i18n' + +type ModelListProps = { + provider: ModelProvider + models: Model[] + onCollapse: () => void +} +const ModelList: FC = ({ + provider, + models, + onCollapse, +}) => { + const { locale } = useContext(I18n) + const language = languageMaps[locale] + + return ( +
+
+
+ + 2 Models + onCollapse()} + > + + Collapse + + + + {}} /> + + + + Add Model + +
+ { + models.map(model => ( +
+
+ {model.label[language]} +
+ { + model.features.map(feature => ( + + )) + } +
+
+
+ + Config +
+ +
+
+ )) + } +
+
+ ) +} + +export default ModelList diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/quota-panel.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/quota-panel.tsx new file mode 100644 index 00000000000000..dd08d29474509d --- /dev/null +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/quota-panel.tsx @@ -0,0 +1,25 @@ +import { InfoCircle } from '@/app/components/base/icons/src/vender/line/general' + +const QuotaPanel = () => { + return ( +
+
+ QUOTA + +
+
+ 200 + Call times +
+ +
+ ) +} + +export default QuotaPanel diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/tab.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/tab.tsx new file mode 100644 index 00000000000000..5a533947d213eb --- /dev/null +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/tab.tsx @@ -0,0 +1,45 @@ +import type { FC } from 'react' + +type TabProps = { + active: string + onSelect: (active: string) => void +} +const Tab: FC = ({ + active, + onSelect, +}) => { + const tabs = [ + { + key: 'all', + text: 'All', + }, + { + key: 'added', + text: 'Added', + }, + { + key: 'build-in', + text: 'Build-in', + }, + ] + return ( +
+ { + tabs.map(tab => ( +
onSelect(tab.key)} + > + {tab.text} +
+ )) + } +
+ ) +} + +export default Tab diff --git a/web/app/components/header/account-setting/model-provider-page/provider-card/index.tsx b/web/app/components/header/account-setting/model-provider-page/provider-card/index.tsx new file mode 100644 index 00000000000000..77cd0fd946f2af --- /dev/null +++ b/web/app/components/header/account-setting/model-provider-page/provider-card/index.tsx @@ -0,0 +1,58 @@ +import type { FC } from 'react' +import { useTranslation } from 'react-i18next' +import { useContext } from 'use-context-selector' +import type { ModelProvider } from '../declarations' +import { languageMaps } from '../utils' +import ModelBadge from '../model-badge' +import I18n from '@/context/i18n' +import { Settings01 } from '@/app/components/base/icons/src/vender/line/general' + +type ProviderCardProps = { + provider: ModelProvider +} +const ProviderCard: FC = ({ + provider, +}) => { + const { t } = useTranslation() + const { locale } = useContext(I18n) + const language = languageMaps[locale] + + return ( +
+
+
+
+
+
+ { + provider.description && ( +
{provider.description[language]}
+ ) + } +
+ { + provider.supported_models_types.map(modelType => ( + + )) + } +
+ +
+ ) +} + +export default ProviderCard diff --git a/web/app/components/header/account-setting/model-provider-page/utils.ts b/web/app/components/header/account-setting/model-provider-page/utils.ts new file mode 100644 index 00000000000000..c87e5119a290eb --- /dev/null +++ b/web/app/components/header/account-setting/model-provider-page/utils.ts @@ -0,0 +1,7 @@ +export const languageMaps = { + 'en': 'en_US', + 'zh-Hans': 'zh_Hans', +} as { + 'en': 'en_US' + 'zh-Hans': 'zh_Hans' +} diff --git a/web/service/common.ts b/web/service/common.ts index e6c4e65a710499..e5c6b3f9297ded 100644 --- a/web/service/common.ts +++ b/web/service/common.ts @@ -25,7 +25,8 @@ import type { UpdateOpenAIKeyResponse, ValidateOpenAIKeyResponse, } from '@/models/app' -import type { BackendModel, ProviderMap } from '@/app/components/header/account-setting/model-page/declarations' +import type { BackendModel } from '@/app/components/header/account-setting/model-page/declarations' +import type { ModelProvider } from '@/app/components/header/account-setting/model-provider-page/declarations' import type { RETRIEVE_METHOD } from '@/types/app' export const login: Fetcher }> = ({ url, body }) => { @@ -142,8 +143,8 @@ export const activateMember: Fetcher return post(url, { body }) } -export const fetchModelProviders: Fetcher = (url) => { - return get(url) +export const fetchModelProviders: Fetcher = (url) => { + return get(url) } export const fetchModelList: Fetcher = (url) => { From bf7c83ea4c85ed9a53ee5764b1112854ac00266f Mon Sep 17 00:00:00 2001 From: StyleZhang Date: Mon, 11 Dec 2023 16:15:38 +0800 Subject: [PATCH 02/46] feat: new model provider --- web/app/components/base/button/index.tsx | 8 +- .../financeAndECommerce/coins-stacked-01.svg | 5 + .../vender/solid/general/plus-circle.svg | 5 + .../financeAndECommerce/CoinsStacked01.json | 39 ++++ .../financeAndECommerce/CoinsStacked01.tsx | 16 ++ .../vender/line/financeAndECommerce/index.ts | 1 + .../src/vender/solid/general/PlusCircle.json | 38 ++++ .../src/vender/solid/general/PlusCircle.tsx | 16 ++ .../icons/src/vender/solid/general/index.ts | 1 + .../header/account-setting/index.tsx | 4 +- .../model-provider-page/declarations.ts | 20 ++- .../model-provider-page/index.tsx | 44 +++++ .../model-provider-page/model-modal/Form.tsx | 149 ++++++++++++++++ .../model-provider-page/model-modal/Input.tsx | 46 +++++ .../model-provider-page/model-modal/index.tsx | 168 ++++++++++++++++++ .../provider-added-card/add-model-button.tsx | 26 +++ .../provider-added-card/credential-panel.tsx | 38 +++- .../provider-added-card/index.tsx | 34 +++- .../provider-added-card/model-list.tsx | 19 +- .../provider-added-card/priority-selector.tsx | 71 ++++++++ .../provider-added-card/quota-panel.tsx | 8 +- .../provider-card/index.tsx | 64 ++++--- .../model-provider-page/utils.ts | 2 + web/app/styles/globals.css | 30 ++++ 24 files changed, 785 insertions(+), 67 deletions(-) create mode 100644 web/app/components/base/icons/assets/vender/line/financeAndECommerce/coins-stacked-01.svg create mode 100644 web/app/components/base/icons/assets/vender/solid/general/plus-circle.svg create mode 100644 web/app/components/base/icons/src/vender/line/financeAndECommerce/CoinsStacked01.json create mode 100644 web/app/components/base/icons/src/vender/line/financeAndECommerce/CoinsStacked01.tsx create mode 100644 web/app/components/base/icons/src/vender/solid/general/PlusCircle.json create mode 100644 web/app/components/base/icons/src/vender/solid/general/PlusCircle.tsx create mode 100644 web/app/components/header/account-setting/model-provider-page/model-modal/Form.tsx create mode 100644 web/app/components/header/account-setting/model-provider-page/model-modal/Input.tsx create mode 100644 web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx create mode 100644 web/app/components/header/account-setting/model-provider-page/provider-added-card/add-model-button.tsx create mode 100644 web/app/components/header/account-setting/model-provider-page/provider-added-card/priority-selector.tsx diff --git a/web/app/components/base/button/index.tsx b/web/app/components/base/button/index.tsx index f28f9b47858a53..e617a5d12d4f0a 100644 --- a/web/app/components/base/button/index.tsx +++ b/web/app/components/base/button/index.tsx @@ -24,19 +24,19 @@ const Button: FC = ({ let style = 'cursor-pointer' switch (type) { case 'primary': - style = (disabled || loading) ? 'bg-primary-200 cursor-not-allowed text-white' : 'bg-primary-600 hover:bg-primary-600/75 hover:shadow-md cursor-pointer text-white hover:shadow-sm' + style = (disabled || loading) ? 'btn-primary-disabled' : 'btn-primary' break case 'warning': - style = (disabled || loading) ? 'bg-red-600/75 cursor-not-allowed text-white' : 'bg-red-600 hover:bg-red-600/75 hover:shadow-md cursor-pointer text-white hover:shadow-sm' + style = (disabled || loading) ? 'btn-warning-disabled' : 'btn-warning' break default: - style = disabled ? 'border-solid border border-gray-200 bg-gray-200 cursor-not-allowed text-gray-800' : 'border-solid border border-gray-200 cursor-pointer text-gray-500 hover:bg-white hover:shadow-sm hover:border-gray-300' + style = disabled ? 'btn-default-disabled' : 'btn-default' break } return (
diff --git a/web/app/components/base/icons/assets/vender/line/financeAndECommerce/coins-stacked-01.svg b/web/app/components/base/icons/assets/vender/line/financeAndECommerce/coins-stacked-01.svg new file mode 100644 index 00000000000000..00f371a83bf833 --- /dev/null +++ b/web/app/components/base/icons/assets/vender/line/financeAndECommerce/coins-stacked-01.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/web/app/components/base/icons/assets/vender/solid/general/plus-circle.svg b/web/app/components/base/icons/assets/vender/solid/general/plus-circle.svg new file mode 100644 index 00000000000000..b169bd122443fe --- /dev/null +++ b/web/app/components/base/icons/assets/vender/solid/general/plus-circle.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/web/app/components/base/icons/src/vender/line/financeAndECommerce/CoinsStacked01.json b/web/app/components/base/icons/src/vender/line/financeAndECommerce/CoinsStacked01.json new file mode 100644 index 00000000000000..8a971909c872e0 --- /dev/null +++ b/web/app/components/base/icons/src/vender/line/financeAndECommerce/CoinsStacked01.json @@ -0,0 +1,39 @@ +{ + "icon": { + "type": "element", + "isRootNode": true, + "name": "svg", + "attributes": { + "width": "24", + "height": "24", + "viewBox": "0 0 24 24", + "fill": "none", + "xmlns": "http://www.w3.org/2000/svg" + }, + "children": [ + { + "type": "element", + "name": "g", + "attributes": { + "id": "coins-stacked-01" + }, + "children": [ + { + "type": "element", + "name": "path", + "attributes": { + "id": "Icon", + "d": "M12 17C12 19.7614 14.2386 22 17 22C19.7614 22 22 19.7614 22 17C22 14.2386 19.7614 12 17 12C14.2386 12 12 14.2386 12 17ZM12 17C12 15.8742 12.3721 14.8353 13 13.9995V5M12 17C12 17.8254 12.2 18.604 12.5541 19.2901C11.7117 20.0018 9.76584 20.5 7.5 20.5C4.46243 20.5 2 19.6046 2 18.5V5M13 5C13 6.10457 10.5376 7 7.5 7C4.46243 7 2 6.10457 2 5M13 5C13 3.89543 10.5376 3 7.5 3C4.46243 3 2 3.89543 2 5M2 14C2 15.1046 4.46243 16 7.5 16C9.689 16 11.5793 15.535 12.4646 14.8618M13 9.5C13 10.6046 10.5376 11.5 7.5 11.5C4.46243 11.5 2 10.6046 2 9.5", + "stroke": "currentColor", + "stroke-width": "2", + "stroke-linecap": "round", + "stroke-linejoin": "round" + }, + "children": [] + } + ] + } + ] + }, + "name": "CoinsStacked01" +} \ No newline at end of file diff --git a/web/app/components/base/icons/src/vender/line/financeAndECommerce/CoinsStacked01.tsx b/web/app/components/base/icons/src/vender/line/financeAndECommerce/CoinsStacked01.tsx new file mode 100644 index 00000000000000..0c550a0c077d1e --- /dev/null +++ b/web/app/components/base/icons/src/vender/line/financeAndECommerce/CoinsStacked01.tsx @@ -0,0 +1,16 @@ +// GENERATE BY script +// DON NOT EDIT IT MANUALLY + +import * as React from 'react' +import data from './CoinsStacked01.json' +import IconBase from '@/app/components/base/icons/IconBase' +import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase' + +const Icon = React.forwardRef, Omit>(( + props, + ref, +) => ) + +Icon.displayName = 'CoinsStacked01' + +export default Icon diff --git a/web/app/components/base/icons/src/vender/line/financeAndECommerce/index.ts b/web/app/components/base/icons/src/vender/line/financeAndECommerce/index.ts index 6925c75cc0a674..a9ee6418b50492 100644 --- a/web/app/components/base/icons/src/vender/line/financeAndECommerce/index.ts +++ b/web/app/components/base/icons/src/vender/line/financeAndECommerce/index.ts @@ -1,2 +1,3 @@ +export { default as CoinsStacked01 } from './CoinsStacked01' export { default as GoldCoin } from './GoldCoin' export { default as ReceiptList } from './ReceiptList' diff --git a/web/app/components/base/icons/src/vender/solid/general/PlusCircle.json b/web/app/components/base/icons/src/vender/solid/general/PlusCircle.json new file mode 100644 index 00000000000000..005a7ba5bfd84e --- /dev/null +++ b/web/app/components/base/icons/src/vender/solid/general/PlusCircle.json @@ -0,0 +1,38 @@ +{ + "icon": { + "type": "element", + "isRootNode": true, + "name": "svg", + "attributes": { + "width": "24", + "height": "24", + "viewBox": "0 0 24 24", + "fill": "none", + "xmlns": "http://www.w3.org/2000/svg" + }, + "children": [ + { + "type": "element", + "name": "g", + "attributes": { + "id": "plus-circle" + }, + "children": [ + { + "type": "element", + "name": "path", + "attributes": { + "id": "Solid", + "fill-rule": "evenodd", + "clip-rule": "evenodd", + "d": "M12 1C5.92487 1 1 5.92487 1 12C1 18.0751 5.92487 23 12 23C18.0751 23 23 18.0751 23 12C23 5.92487 18.0751 1 12 1ZM12 7C12.5523 7 13 7.44772 13 8V11H16C16.5523 11 17 11.4477 17 12C17 12.5523 16.5523 13 16 13H13V16C13 16.5523 12.5523 17 12 17C11.4477 17 11 16.5523 11 16V13H8C7.44772 13 7 12.5523 7 12C7 11.4477 7.44772 11 8 11H11V8C11 7.44772 11.4477 7 12 7Z", + "fill": "currentColor" + }, + "children": [] + } + ] + } + ] + }, + "name": "PlusCircle" +} \ No newline at end of file diff --git a/web/app/components/base/icons/src/vender/solid/general/PlusCircle.tsx b/web/app/components/base/icons/src/vender/solid/general/PlusCircle.tsx new file mode 100644 index 00000000000000..01364c7c196c67 --- /dev/null +++ b/web/app/components/base/icons/src/vender/solid/general/PlusCircle.tsx @@ -0,0 +1,16 @@ +// GENERATE BY script +// DON NOT EDIT IT MANUALLY + +import * as React from 'react' +import data from './PlusCircle.json' +import IconBase from '@/app/components/base/icons/IconBase' +import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase' + +const Icon = React.forwardRef, Omit>(( + props, + ref, +) => ) + +Icon.displayName = 'PlusCircle' + +export default Icon diff --git a/web/app/components/base/icons/src/vender/solid/general/index.ts b/web/app/components/base/icons/src/vender/solid/general/index.ts index dba2667c63a810..f2dc95bf3bcaaa 100644 --- a/web/app/components/base/icons/src/vender/solid/general/index.ts +++ b/web/app/components/base/icons/src/vender/solid/general/index.ts @@ -3,6 +3,7 @@ export { default as CheckDone01 } from './CheckDone01' export { default as Download02 } from './Download02' export { default as Eye } from './Eye' export { default as MessageClockCircle } from './MessageClockCircle' +export { default as PlusCircle } from './PlusCircle' export { default as Target04 } from './Target04' export { default as Tool03 } from './Tool03' export { default as XCircle } from './XCircle' diff --git a/web/app/components/header/account-setting/index.tsx b/web/app/components/header/account-setting/index.tsx index cdbb789e2e9af2..90fe1ba4251e49 100644 --- a/web/app/components/header/account-setting/index.tsx +++ b/web/app/components/header/account-setting/index.tsx @@ -11,7 +11,7 @@ import LanguagePage from './language-page' import PluginPage from './plugin-page' import ApiBasedExtensionPage from './api-based-extension-page' import DataSourcePage from './data-source-page' -import ModelPage from './model-page' +import ModelProviderPage from './model-provider-page' import s from './index.module.css' import BillingPage from '@/app/components/billing/billing-page' import Modal from '@/app/components/base/modal' @@ -202,7 +202,7 @@ export default function AccountSetting({ {activeMenu === 'billing' && } {activeMenu === 'integrations' && } {activeMenu === 'language' && } - {activeMenu === 'provider' && } + {activeMenu === 'provider' && } {activeMenu === 'data-source' && } {activeMenu === 'plugin' && } {activeMenu === 'api-based-extension' && } diff --git a/web/app/components/header/account-setting/model-provider-page/declarations.ts b/web/app/components/header/account-setting/model-provider-page/declarations.ts index 32e213962720b7..a1bccc539f593b 100644 --- a/web/app/components/header/account-setting/model-provider-page/declarations.ts +++ b/web/app/components/header/account-setting/model-provider-page/declarations.ts @@ -1,3 +1,5 @@ +export type FormValue = Record + export type TypeWithI18N = { 'en_US': T 'zh_Hans': T @@ -52,17 +54,23 @@ export type CredentialFormSchemaMap = { [FormTypeEnum.textInput]: { max_length: number; placeholder: TypeWithI18N } [FormTypeEnum.select]: { options: FormOption[] } [FormTypeEnum.radio]: { options: FormOption[] } - [FormTypeEnum.secretInput]: {} + [FormTypeEnum.secretInput]: { placeholder: TypeWithI18N } } -export type CredentialFormSchema = { +export type CredentialFormSchemaBase = { variable: string label: TypeWithI18N type: FormTypeEnum required: boolean default: string show_on: FormShowOnObject[] -} & CredentialFormSchemaMap[FormTypeEnum] +} + +export type CredentialFormSchemaTextInput = CredentialFormSchemaBase & { max_length: number; placeholder: TypeWithI18N } +export type CredentialFormSchemaSelect = CredentialFormSchemaBase & { options: FormOption[] } +export type CredentialFormSchemaRadio = CredentialFormSchemaBase & { options: FormOption[] } +export type CredentialFormSchemaSecretInput = CredentialFormSchemaBase & { placeholder: TypeWithI18N } +export type CredentialFormSchema = CredentialFormSchemaTextInput | CredentialFormSchemaSelect | CredentialFormSchemaRadio | CredentialFormSchemaSecretInput export type Model = { model: string @@ -103,11 +111,12 @@ export type ModelProvider = { label: TypeWithI18N description?: TypeWithI18N help_url: TypeWithI18N + help_text: TypeWithI18N icon_small: TypeWithI18N icon_large: TypeWithI18N background?: string supported_models_types: ModelTypeEnum[] - configurate_method: ConfigurateMethodEnum + configurate_methods: ConfigurateMethodEnum[] provider_credential_schema: { credential_form_schemas: CredentialFormSchema[] } @@ -119,6 +128,9 @@ export type ModelProvider = { crenential_form_schemas: CredentialFormSchema[] } preferred_provider_type: PreferredProviderTypeEnum + custom_configuration: { + status: 'active' | 'no_configure' + } system_configuration: { enabled: boolean current_system_quota_type: CurrentSystemQuotaTypeEnum diff --git a/web/app/components/header/account-setting/model-provider-page/index.tsx b/web/app/components/header/account-setting/model-provider-page/index.tsx index aaf4a04ef3c1f6..50b15da2acae4e 100644 --- a/web/app/components/header/account-setting/model-provider-page/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/index.tsx @@ -1,10 +1,44 @@ import useSWR from 'swr' import { useTranslation } from 'react-i18next' import SystemModel from '../model-page/system-model' +import ProviderAddedCard from './provider-added-card' +import ProviderCard from './provider-card' +import type { ModelProvider } from './declarations' +import { + ConfigurateMethodEnum, + ModelTypeEnum, +} from './declarations' import { fetchModelProviders } from '@/service/common' import { useProviderContext } from '@/context/provider-context' import { AlertTriangle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback' +const provider: ModelProvider = { + provider: 'openai', + label: { + zh_Hans: 'OpenAI', + en_US: 'OpenAI', + }, + icon_small: { + zh_Hans: '', + en_US: '', + }, + icon_large: { + zh_Hans: '', + en_US: '', + }, + background: '#FFF8DC', + supported_models_types: [ + ModelTypeEnum.textEmbedding, + ModelTypeEnum.textGeneration, + ModelTypeEnum.speech2text, + ModelTypeEnum.rerank, + ], + configurate_methods: [ + ConfigurateMethodEnum.predefinedModel, + ConfigurateMethodEnum.customizableModel, + ], +} + const ModelProviderPage = () => { const { t } = useTranslation() const { @@ -32,6 +66,16 @@ const ModelProviderPage = () => { } mutateProviders()} />
+
+ +
+
+ + ADD MORE MODEL PROVIDER + +
+
+ +
) } diff --git a/web/app/components/header/account-setting/model-provider-page/model-modal/Form.tsx b/web/app/components/header/account-setting/model-provider-page/model-modal/Form.tsx new file mode 100644 index 00000000000000..065573f41d5201 --- /dev/null +++ b/web/app/components/header/account-setting/model-provider-page/model-modal/Form.tsx @@ -0,0 +1,149 @@ +import { useState } from 'react' +import type { FC } from 'react' +import { useContext } from 'use-context-selector' +import { ValidatingTip } from '../../key-validator/ValidateStatus' +import type { + CredentialFormSchema, + CredentialFormSchemaRadio, + CredentialFormSchemaSecretInput, + CredentialFormSchemaSelect, + CredentialFormSchemaTextInput, + FormValue, +} from '../declarations' +import { FormTypeEnum } from '../declarations' +import { languageMaps } from '../utils' +import Input from './Input' +import I18n from '@/context/i18n' +import { SimpleSelect } from '@/app/components/base/select' + +type FormProps = { + value: FormValue + onChange: (val: FormValue) => void + formSchemas: CredentialFormSchema[] + isEditMode: boolean + initialFormValueCleared: boolean + onInitialFormValueCleared: (val: boolean) => void + validating: boolean + validatedSuccess?: boolean +} + +const Form: FC = ({ + value, + onChange, + formSchemas, + isEditMode, + initialFormValueCleared, + onInitialFormValueCleared, + validating, + validatedSuccess, +}) => { + const { locale } = useContext(I18n) + const language = languageMaps[locale] + const [changeKey, setChangeKey] = useState('') + + const handleClearInitialFormValue = () => { + const needClearInitialFormValues = formSchemas.filter(formSchema => formSchema.type === (FormTypeEnum.secretInput || formSchema.type === FormTypeEnum.textInput)) + const newValue: Record = {} + needClearInitialFormValues?.forEach((field) => { + newValue[field.variable] = '' + }) + onChange({ ...value, ...newValue }) + onInitialFormValueCleared(true) + } + + const handleFocus = () => { + if (isEditMode && !initialFormValueCleared) + handleClearInitialFormValue() + } + + const handleFormChange = (key: string, val: string) => { + setChangeKey(key) + onChange({ ...value, [key]: val }) + } + + const renderField = (formSchema: CredentialFormSchema) => { + if (formSchema.type === FormTypeEnum.textInput || formSchema.type === FormTypeEnum.secretInput) { + const { + variable, + label, + } = formSchema as (CredentialFormSchemaTextInput | CredentialFormSchemaSecretInput) + return ( +
+
{label[language]}
+ handleFormChange(variable, val)} + onFocus={handleFocus} + validated={validatedSuccess} + /> + {validating && changeKey === variable && } +
+ ) + } + + if (formSchema.type === FormTypeEnum.radio) { + const { + options, + variable, + label, + } = formSchema as CredentialFormSchemaRadio + + return ( +
+
{label[language]}
+
+ { + options?.map(option => ( +
handleFormChange(variable, option.value)} + key={`${variable}-${option.value}`} + > +
+
{option.label[language]}
+
+ )) + } +
+ {validating && changeKey === variable && } +
+ ) + } + + if (formSchema.type === 'select') { + const { + options, + variable, + label, + } = formSchema as CredentialFormSchemaSelect + + return ( +
+
{label[language]}
+ ({ value: option.value, name: option.label[language] }))} + onSelect={item => handleFormChange(variable, item.value as string)} + /> + {validating && changeKey === variable && } +
+ ) + } + } + + return ( +
+ { + formSchemas.map(formSchema => renderField(formSchema)) + } +
+ ) +} + +export default Form diff --git a/web/app/components/header/account-setting/model-provider-page/model-modal/Input.tsx b/web/app/components/header/account-setting/model-provider-page/model-modal/Input.tsx new file mode 100644 index 00000000000000..4b0e575dd87fda --- /dev/null +++ b/web/app/components/header/account-setting/model-provider-page/model-modal/Input.tsx @@ -0,0 +1,46 @@ +import type { FC } from 'react' +import { CheckCircle } from '@/app/components/base/icons/src/vender/solid/general' + +type InputProps = { + value?: string + onChange: (v: string) => void + onFocus: () => void + placeholder?: string + validated?: boolean +} +const Input: FC = ({ + value, + onChange, + onFocus, + placeholder, + validated, +}) => { + return ( +
+ onChange(e.target.value)} + onFocus={onFocus} + value={value || ''} + /> + { + validated && ( +
+ +
+ ) + } +
+ ) +} + +export default Input diff --git a/web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx b/web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx new file mode 100644 index 00000000000000..c29f9a0225681b --- /dev/null +++ b/web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx @@ -0,0 +1,168 @@ +import type { FC } from 'react' +import { useMemo, useState } from 'react' +import { useTranslation } from 'react-i18next' +import { useContext } from 'use-context-selector' +import type { + CredentialFormSchema, + FormValue, + ModelProvider, +} from '../declarations' +import { ConfigurateMethodEnum } from '../declarations' +import { languageMaps } from '../utils' +import { useValidate } from '../../key-validator/hooks' +import { ValidatedStatus } from '../../key-validator/declarations' +import Form from './Form' +import I18n from '@/context/i18n' +import Button from '@/app/components/base/button' +import { Lock01 } from '@/app/components/base/icons/src/vender/solid/security' +import { LinkExternal02 } from '@/app/components/base/icons/src/vender/line/general' +import { AlertCircle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback' +import { + PortalToFollowElem, + PortalToFollowElemContent, +} from '@/app/components/base/portal-to-follow-elem' + +type ModelModalProps = { + provider: ModelProvider + formSchemas: CredentialFormSchema[] + configurateMethod: ConfigurateMethodEnum + onCancel: () => void + onSave: (v: FormValue) => void +} + +const ModelModal: FC = ({ + provider, + formSchemas, + configurateMethod, + onCancel, + onSave, +}) => { + const { t } = useTranslation() + const { locale } = useContext(I18n) + const language = languageMaps[locale] + const [loading, setLoading] = useState(false) + const [initialFormValueCleared, setInitialFormValueCleared] = useState(false) + + const initialFormValue = useMemo(() => { + return formSchemas.reduce((acc: FormValue, cur) => { + acc[cur.variable] = cur.default + return acc + }, {}) + }, [formSchemas]) + const [value, setValue] = useState(initialFormValue) + const [validate, validating, validatedStatusState] = useValidate(value) + const isEditMode = useMemo(() => { + return formSchemas.every(formSchema => formSchema.required && initialFormValue[formSchema.variable]) + }, [initialFormValue]) + + const handleSave = () => { + onSave(value) + + const validateKeys = formSchemas.filter(formSchema => formSchema.required).map(formSchema => formSchema.variable) + if (validateKeys.length) { + validate({ + before: () => { + for (let i = 0; i < validateKeys.length; i++) { + if (!value[validateKeys[i]]) + return false + } + return true + }, + run: () => { + return new Promise((resolve, reject) => { + setLoading(true) + setTimeout(() => { + setLoading(false) + resolve({}) + }, 1000) + }) + }, + }) + } + } + + const renderTitlePrefix = () => { + let prefix + if (isEditMode) + prefix = t('common.operation.edit') + else + prefix = configurateMethod === ConfigurateMethodEnum.customizableModel ? t('common.operation.create') : t('common.operation.setup') + + return `${prefix} ${provider.label[language]}` + } + + return ( + + +
+
+
+
+
{renderTitlePrefix()}
+
+
+
setValue(val)} + formSchemas={formSchemas} + isEditMode={isEditMode} + initialFormValueCleared={initialFormValueCleared} + onInitialFormValueCleared={val => setInitialFormValueCleared(val)} + validating={validating} + validatedSuccess={validatedStatusState.status === ValidatedStatus.Success} + /> +
+ + {provider.help_text[language]} + + +
+ + +
+
+
+
+ { + (validatedStatusState.status === ValidatedStatus.Error && validatedStatusState.message) + ? ( +
+ + {validatedStatusState.message} +
+ ) + : ( +
+ + {t('common.modelProvider.encrypted.front')} + + PKCS1_OAEP + + {t('common.modelProvider.encrypted.back')} +
+ ) + } +
+
+
+ + + ) +} + +export default ModelModal diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/add-model-button.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/add-model-button.tsx new file mode 100644 index 00000000000000..3395b1849581f3 --- /dev/null +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/add-model-button.tsx @@ -0,0 +1,26 @@ +import type { FC } from 'react' +import { PlusCircle } from '@/app/components/base/icons/src/vender/solid/general' + +type AddModelButtonProps = { + className?: string + onClick: () => void +} +const AddModelButton: FC = ({ + className, + onClick, +}) => { + return ( + + + Add Model + + ) +} + +export default AddModelButton diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/credential-panel.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/credential-panel.tsx index 25205086da3e3a..5459035bc5ce51 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/credential-panel.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/credential-panel.tsx @@ -1,24 +1,44 @@ import { useTranslation } from 'react-i18next' +import PrioritySelector from './priority-selector' import Indicator from '@/app/components/header/indicator' import { Settings01 } from '@/app/components/base/icons/src/vender/line/general' +import { ChevronDownDouble } from '@/app/components/base/icons/src/vender/line/arrows' +import Button from '@/app/components/base/button' +import Tooltip from '@/app/components/base/tooltip' +import { IS_CE_EDITION } from '@/config' const CredentialPanel = () => { const { t } = useTranslation() return ( -
-
+
+
API-KEY
-
- - {t('common.operation.setup')} +
+ + { + !IS_CE_EDITION && ( + {}} + value='custom' + /> + ) + }
+ { + !IS_CE_EDITION && ( + +
+ +
+
+ ) + }
) } diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/index.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/index.tsx index 3fb2d12c42f342..44e7508de03a02 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/index.tsx @@ -2,12 +2,17 @@ import type { FC } from 'react' import { useState } from 'react' import { useContext } from 'use-context-selector' import type { ModelProvider } from '../declarations' -import { languageMaps } from '../utils' +import { + DEFAULT_BACKGROUND_COLOR, + languageMaps, +} from '../utils' import ModelBadge from '../model-badge' import CredentialPanel from './credential-panel' import QuotaPanel from './quota-panel' import ModelList from './model-list' +import AddModelButton from './add-model-button' import I18n from '@/context/i18n' +import { ChevronDownDouble } from '@/app/components/base/icons/src/vender/line/arrows' type ProviderAddedCardProps = { provider: ModelProvider @@ -17,13 +22,16 @@ const ProviderAddedCard: FC = ({ }) => { const { locale } = useContext(I18n) const language = languageMaps[locale] - const [collapsed, setCollapsed] = useState(false) + const [collapsed, setCollapsed] = useState(true) return ( -
+
-
+
{ provider.supported_models_types.map(modelType => ( @@ -40,11 +48,19 @@ const ProviderAddedCard: FC = ({
{ collapsed && ( -
setCollapsed(false)} - > - 5 Models +
+
5 Models
+
setCollapsed(false)} + > + + Show 2 Models +
+ {}} + className='hidden group-hover:flex group-hover:text-primary-600' + />
) } diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-list.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-list.tsx index ac37ebc91d01c3..3c3e077e714c39 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-list.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-list.tsx @@ -4,8 +4,9 @@ import type { Model, ModelProvider } from '../declarations' import { languageMaps } from '../utils' import ModelBadge from '../model-badge' import Tab from './tab' +import AddModelButton from './add-model-button' import Indicator from '@/app/components/header/indicator' -import { Plus, Settings01 } from '@/app/components/base/icons/src/vender/line/general' +import { Settings01 } from '@/app/components/base/icons/src/vender/line/general' import { ChevronDownDouble } from '@/app/components/base/icons/src/vender/line/arrows' import I18n from '@/context/i18n' @@ -26,23 +27,23 @@ const ModelList: FC = ({
- - 2 Models + + 2 Models {}} /> - - - Add Model - + {}} />
{ models.map(model => ( diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/priority-selector.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/priority-selector.tsx new file mode 100644 index 00000000000000..ba807df14759f6 --- /dev/null +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/priority-selector.tsx @@ -0,0 +1,71 @@ +import { Fragment } from 'react' +import type { FC } from 'react' +import { Popover, Transition } from '@headlessui/react' +import { useTranslation } from 'react-i18next' +import { Check, DotsHorizontal } from '@/app/components/base/icons/src/vender/line/general' +import Button from '@/app/components/base/button' + +type SelectorProps = { + value?: string + onSelect: (v: Record) => void +} +const Selector: FC = ({ + value, + onSelect, +}) => { + const { t } = useTranslation() + const options = [ + { + key: 'custom', + text: 'API', + }, + { + key: 'system', + text: t('common.modelProvider.quota'), + }, + ] + + return ( + + + { + ({ open }) => ( + + ) + } + + + +
+
{t('common.modelProvider.card.priorityUse')}
+ { + options.map(option => ( + +
onSelect({ type: 'priority', value: option.key })} + > +
{option.text}
+ {value === option.key && } +
+
+ )) + } +
+
+
+
+ ) +} + +export default Selector diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/quota-panel.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/quota-panel.tsx index dd08d29474509d..9db986b124a12a 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/quota-panel.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/quota-panel.tsx @@ -2,19 +2,19 @@ import { InfoCircle } from '@/app/components/base/icons/src/vender/line/general' const QuotaPanel = () => { return ( -
-
+
+
QUOTA
-
+
200 Call times
diff --git a/web/app/components/header/account-setting/model-provider-page/provider-card/index.tsx b/web/app/components/header/account-setting/model-provider-page/provider-card/index.tsx index 77cd0fd946f2af..f183d7da302bf7 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-card/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-card/index.tsx @@ -2,10 +2,15 @@ import type { FC } from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import type { ModelProvider } from '../declarations' -import { languageMaps } from '../utils' +import { + DEFAULT_BACKGROUND_COLOR, + languageMaps, +} from '../utils' import ModelBadge from '../model-badge' import I18n from '@/context/i18n' -import { 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 Button from '@/app/components/base/button' type ProviderCardProps = { provider: ModelProvider @@ -19,37 +24,44 @@ const ProviderCard: FC = ({ return (
-
+
-
- { - provider.description && ( -
{provider.description[language]}
- ) - } -
{ - provider.supported_models_types.map(modelType => ( - - )) + provider.description && ( +
{provider.description[language]}
+ ) }
- ) diff --git a/web/app/components/header/account-setting/model-provider-page/utils.ts b/web/app/components/header/account-setting/model-provider-page/utils.ts index c87e5119a290eb..1d45465bb545a4 100644 --- a/web/app/components/header/account-setting/model-provider-page/utils.ts +++ b/web/app/components/header/account-setting/model-provider-page/utils.ts @@ -5,3 +5,5 @@ export const languageMaps = { 'en': 'en_US' 'zh-Hans': 'zh_Hans' } + +export const DEFAULT_BACKGROUND_COLOR = '#F3F4F6' diff --git a/web/app/styles/globals.css b/web/app/styles/globals.css index 8c8989082e3f61..772d8945501ce1 100644 --- a/web/app/styles/globals.css +++ b/web/app/styles/globals.css @@ -145,4 +145,34 @@ button:focus-within { right: 0; top: 0; bottom: 0; +} + +@layer components { + .btn { + @apply inline-flex justify-center items-center content-center h-9 leading-5 rounded-lg px-4 py-2 text-base; + } + + .btn-default { + @apply border-solid border border-gray-200 cursor-pointer text-gray-500 hover:bg-white hover:shadow-sm hover:border-gray-300; + } + + .btn-default-disabled { + @apply border-solid border border-gray-200 bg-gray-200 cursor-not-allowed text-gray-800; + } + + .btn-primary { + @apply bg-primary-600 hover:bg-primary-600/75 cursor-pointer text-white hover:shadow-sm; + } + + .btn-primary-disabled { + @apply bg-primary-200 cursor-not-allowed text-white; + } + + .btn-warning { + @apply bg-red-600 hover:bg-red-600/75 cursor-pointer text-white hover:shadow-sm; + } + + .btn-warning-disabled { + @apply bg-red-600/75 cursor-not-allowed text-white; + } } \ No newline at end of file From e629262c360d2ca06df604c4a654c60face7852c Mon Sep 17 00:00:00 2001 From: StyleZhang Date: Wed, 20 Dec 2023 10:19:03 +0800 Subject: [PATCH 03/46] feat: model-provider --- .../model-provider-page/declarations.ts | 33 +++- .../model-provider-page/index.tsx | 110 +++++++----- .../model-provider-page/model-modal/index.tsx | 157 ++++++++++-------- .../provider-added-card/index.tsx | 2 +- .../provider-card/index.tsx | 61 +++++-- web/context/provider-context.tsx | 64 +++---- web/service/common.ts | 18 +- 7 files changed, 276 insertions(+), 169 deletions(-) diff --git a/web/app/components/header/account-setting/model-provider-page/declarations.ts b/web/app/components/header/account-setting/model-provider-page/declarations.ts index a1bccc539f593b..b72f6b90b36704 100644 --- a/web/app/components/header/account-setting/model-provider-page/declarations.ts +++ b/web/app/components/header/account-setting/model-provider-page/declarations.ts @@ -18,7 +18,7 @@ export type FormOption = { } export enum ModelTypeEnum { - textGeneration = 'text-generation', + textGeneration = 'llm', textEmbedding = 'text-embedding', rerank = 'rerank', speech2text = 'speech2text', @@ -31,20 +31,25 @@ export enum ConfigurateMethodEnum { fetchFromRemote = 'fetch-from-remote', } -export enum ModelFeature { +export enum ModelFeatureEnum { toolCall = 'tool-call', multiToolCall = 'multi-tool-call', agentThought = 'agent_thought', vision = 'vision', } -export enum ModelStatus { +export enum ModelStatusEnum { active = 'active', noConfigure = 'no-configure', quotaExceeded = 'quota-exceeded', noPermission = 'no-permission', } +export enum CustomConfigurationEnum { + active = 'active', + noConfigure = 'no-configure', +} + export type FormShowOnObject = { variable: string value: string @@ -72,13 +77,14 @@ export type CredentialFormSchemaRadio = CredentialFormSchemaBase & { options: Fo export type CredentialFormSchemaSecretInput = CredentialFormSchemaBase & { placeholder: TypeWithI18N } export type CredentialFormSchema = CredentialFormSchemaTextInput | CredentialFormSchemaSelect | CredentialFormSchemaRadio | CredentialFormSchemaSecretInput -export type Model = { +export type ModelItem = { model: string label: TypeWithI18N model_type: ModelTypeEnum - features: ModelFeature[] - configurate_method: ConfigurateMethodEnum - status: ModelStatus + features: ModelFeatureEnum[] + fetch_from: ConfigurateMethodEnum + status: ModelStatusEnum + deprecated: boolean } export enum PreferredProviderTypeEnum { @@ -115,7 +121,7 @@ export type ModelProvider = { icon_small: TypeWithI18N icon_large: TypeWithI18N background?: string - supported_models_types: ModelTypeEnum[] + supported_model_types: ModelTypeEnum[] configurate_methods: ConfigurateMethodEnum[] provider_credential_schema: { credential_form_schemas: CredentialFormSchema[] @@ -129,7 +135,7 @@ export type ModelProvider = { } preferred_provider_type: PreferredProviderTypeEnum custom_configuration: { - status: 'active' | 'no_configure' + status: CustomConfigurationEnum } system_configuration: { enabled: boolean @@ -137,3 +143,12 @@ export type ModelProvider = { quota_configurations: QuotaConfiguration[] } } + +export type Model = { + provider: string + icon_large: TypeWithI18N + icon_small: TypeWithI18N + label: TypeWithI18N + models: ModelItem[] + status: CustomConfigurationEnum +} diff --git a/web/app/components/header/account-setting/model-provider-page/index.tsx b/web/app/components/header/account-setting/model-provider-page/index.tsx index 50b15da2acae4e..6dbdff22eb049d 100644 --- a/web/app/components/header/account-setting/model-provider-page/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/index.tsx @@ -1,55 +1,46 @@ +import { useState } from 'react' import useSWR from 'swr' import { useTranslation } from 'react-i18next' import SystemModel from '../model-page/system-model' import ProviderAddedCard from './provider-added-card' import ProviderCard from './provider-card' -import type { ModelProvider } from './declarations' -import { +import ModelModal from './model-modal' +import type { ConfigurateMethodEnum, - ModelTypeEnum, + ModelProvider, +} from './declarations' +import { + CustomConfigurationEnum, } from './declarations' import { fetchModelProviders } from '@/service/common' import { useProviderContext } from '@/context/provider-context' import { AlertTriangle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback' -const provider: ModelProvider = { - provider: 'openai', - label: { - zh_Hans: 'OpenAI', - en_US: 'OpenAI', - }, - icon_small: { - zh_Hans: '', - en_US: '', - }, - icon_large: { - zh_Hans: '', - en_US: '', - }, - background: '#FFF8DC', - supported_models_types: [ - ModelTypeEnum.textEmbedding, - ModelTypeEnum.textGeneration, - ModelTypeEnum.speech2text, - ModelTypeEnum.rerank, - ], - configurate_methods: [ - ConfigurateMethodEnum.predefinedModel, - ConfigurateMethodEnum.customizableModel, - ], -} - const ModelProviderPage = () => { const { t } = useTranslation() const { - updateModelList, textGenerationDefaultModel, embeddingsDefaultModel, speech2textDefaultModel, rerankDefaultModel, } = useProviderContext() - const { data: providers, mutate: mutateProviders } = useSWR('/workspaces/current/model-providers', fetchModelProviders) + const [currentProvider, setCurrentProvider] = useState(null) + const [currentConfigurateMethod, setCurrentConfigurateMethod] = useState(null) + const { data: providersData, mutate: mutateProviders } = useSWR('/workspaces/current/model-providers', fetchModelProviders) const defaultModelNotConfigured = !textGenerationDefaultModel && !embeddingsDefaultModel && !speech2textDefaultModel && !rerankDefaultModel + const providers = providersData ? providersData.data : [] + const configedProviders = providers.filter(provider => provider.custom_configuration.status === CustomConfigurationEnum.active) + const notConfigedProviders = providers.filter(provider => provider.custom_configuration.status === CustomConfigurationEnum.noConfigure) + + const handleOpenModal = (provider: ModelProvider, configurateMethod: ConfigurateMethodEnum) => { + setCurrentProvider(provider) + setCurrentConfigurateMethod(configurateMethod) + } + + const handleCancelModelModal = () => { + setCurrentProvider(null) + setCurrentConfigurateMethod(null) + } return (
@@ -66,16 +57,51 @@ const ModelProviderPage = () => { } mutateProviders()} />
-
- -
-
- + ADD MORE MODEL PROVIDER - -
-
- -
+ { + !!configedProviders?.length && ( +
+ { + configedProviders?.map(provider => ( + + )) + } +
+ ) + } + { + !!notConfigedProviders?.length && ( + <> +
+ + ADD MORE MODEL PROVIDER + +
+
+ { + notConfigedProviders?.map(provider => ( + handleOpenModal(provider, configurateMethod)} + /> + )) + } +
+ + ) + } + { + !!currentProvider && !!currentConfigurateMethod && ( + {}} + /> + ) + }
) } diff --git a/web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx b/web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx index c29f9a0225681b..81420332e1bf69 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx @@ -1,9 +1,9 @@ import type { FC } from 'react' import { useMemo, useState } from 'react' +import useSWR from 'swr' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import type { - CredentialFormSchema, FormValue, ModelProvider, } from '../declarations' @@ -21,10 +21,11 @@ import { PortalToFollowElem, PortalToFollowElemContent, } from '@/app/components/base/portal-to-follow-elem' +import { fetchModelProviderCredentials } from '@/service/common' +import Loading from '@/app/components/base/loading' type ModelModalProps = { provider: ModelProvider - formSchemas: CredentialFormSchema[] configurateMethod: ConfigurateMethodEnum onCancel: () => void onSave: (v: FormValue) => void @@ -32,7 +33,6 @@ type ModelModalProps = { const ModelModal: FC = ({ provider, - formSchemas, configurateMethod, onCancel, onSave, @@ -42,23 +42,27 @@ const ModelModal: FC = ({ const language = languageMaps[locale] const [loading, setLoading] = useState(false) const [initialFormValueCleared, setInitialFormValueCleared] = useState(false) - + const { data: formSchemasData, isLoading } = useSWR(`/workspaces/current/model-providers/${provider.provider}/credentials`, fetchModelProviderCredentials) const initialFormValue = useMemo(() => { - return formSchemas.reduce((acc: FormValue, cur) => { + if (!formSchemasData) + return {} + return formSchemasData.credenntials.reduce((acc: FormValue, cur) => { acc[cur.variable] = cur.default return acc }, {}) - }, [formSchemas]) + }, [formSchemasData]) const [value, setValue] = useState(initialFormValue) const [validate, validating, validatedStatusState] = useValidate(value) const isEditMode = useMemo(() => { - return formSchemas.every(formSchema => formSchema.required && initialFormValue[formSchema.variable]) - }, [initialFormValue]) + if (!formSchemasData) + return false + return formSchemasData.credenntials.every(formSchema => formSchema.required && initialFormValue[formSchema.variable]) + }, [initialFormValue, formSchemasData]) const handleSave = () => { onSave(value) - const validateKeys = formSchemas.filter(formSchema => formSchema.required).map(formSchema => formSchema.variable) + const validateKeys = formSchemasData?.credenntials.filter(formSchema => formSchema.required).map(formSchema => formSchema.variable) || [] if (validateKeys.length) { validate({ before: () => { @@ -94,72 +98,81 @@ const ModelModal: FC = ({ return ( -
-
-
-
-
{renderTitlePrefix()}
-
-
- setValue(val)} - formSchemas={formSchemas} - isEditMode={isEditMode} - initialFormValueCleared={initialFormValueCleared} - onInitialFormValueCleared={val => setInitialFormValueCleared(val)} - validating={validating} - validatedSuccess={validatedStatusState.status === ValidatedStatus.Success} - /> -
- - {provider.help_text[language]} - - -
- - -
-
-
-
- { - (validatedStatusState.status === ValidatedStatus.Error && validatedStatusState.message) - ? ( -
- - {validatedStatusState.message} -
- ) - : ( -
- - {t('common.modelProvider.encrypted.front')} - + ) + } + { + !isLoading && ( +
+
+
+ +
+ { + (validatedStatusState.status === ValidatedStatus.Error && validatedStatusState.message) + ? ( +
+ + {validatedStatusState.message} +
+ ) + : ( +
+ + {t('common.modelProvider.encrypted.front')} + + PKCS1_OAEP + + {t('common.modelProvider.encrypted.back')} +
+ ) + } +
+
-
-
+ ) + } ) diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/index.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/index.tsx index 44e7508de03a02..29b63221f655b3 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/index.tsx @@ -34,7 +34,7 @@ const ProviderAddedCard: FC = ({
{ - provider.supported_models_types.map(modelType => ( + provider.supported_model_types.map(modelType => ( void } const ProviderCard: FC = ({ provider, + onOpenModal, }) => { const { t } = useTranslation() const { locale } = useContext(I18n) const language = languageMaps[locale] + const configurateMethods = provider.configurate_methods.filter(method => method !== ConfigurateMethodEnum.fetchFromRemote) return (
= ({ >
-
+ { + provider.icon_large[language] + ? ( +
+ ) + : ( +
+
+ +
+ Default Model Provider Image +
+ ) + }
{ provider.description && ( @@ -40,7 +61,7 @@ const ProviderCard: FC = ({
{ - provider.supported_models_types.map(modelType => ( + provider.supported_model_types.map(modelType => ( = ({ )) }
-
- - +
+ { + configurateMethods.map((method) => { + if (method === ConfigurateMethodEnum.predefinedModel) { + return ( + + ) + } + return ( + + ) + }) + } {/*
{ !!configedProviders?.length && ( @@ -65,6 +78,7 @@ const ModelProviderPage = () => { handleOpenModal(provider, configurateMethod)} /> )) } @@ -98,7 +112,7 @@ const ModelProviderPage = () => { provider={currentProvider} configurateMethod={currentConfigurateMethod} onCancel={handleCancelModelModal} - onSave={() => {}} + onSave={() => mutateProviders()} /> ) } diff --git a/web/app/components/header/account-setting/model-provider-page/model-modal/Form.tsx b/web/app/components/header/account-setting/model-provider-page/model-modal/Form.tsx index 065573f41d5201..ac4350dfbd6b92 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-modal/Form.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-modal/Form.tsx @@ -66,15 +66,25 @@ const Form: FC = ({ const { variable, label, + placeholder, + required, } = formSchema as (CredentialFormSchemaTextInput | CredentialFormSchemaSecretInput) return (
-
{label[language]}
+
+ {label[language]} + { + required && ( + * + ) + } +
handleFormChange(variable, val)} onFocus={handleFocus} validated={validatedSuccess} + placeholder={placeholder[language]} /> {validating && changeKey === variable && }
@@ -127,7 +137,7 @@ const Form: FC = ({
{label[language]}
({ value: option.value, name: option.label[language] }))} onSelect={item => handleFormChange(variable, item.value as string)} /> diff --git a/web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx b/web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx index 81420332e1bf69..6db67e46eb8efa 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx @@ -1,5 +1,5 @@ import type { FC } from 'react' -import { useMemo, useState } from 'react' +import { useState } from 'react' import useSWR from 'swr' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' @@ -8,7 +8,11 @@ import type { ModelProvider, } from '../declarations' import { ConfigurateMethodEnum } from '../declarations' -import { languageMaps } from '../utils' +import { + languageMaps, + saveCredentials, + validateCredentials, +} from '../utils' import { useValidate } from '../../key-validator/hooks' import { ValidatedStatus } from '../../key-validator/declarations' import Form from './Form' @@ -23,12 +27,13 @@ import { } from '@/app/components/base/portal-to-follow-elem' import { fetchModelProviderCredentials } from '@/service/common' import Loading from '@/app/components/base/loading' +import { useToastContext } from '@/app/components/base/toast' type ModelModalProps = { provider: ModelProvider configurateMethod: ConfigurateMethodEnum onCancel: () => void - onSave: (v: FormValue) => void + onSave: () => void } const ModelModal: FC = ({ @@ -38,52 +43,69 @@ const ModelModal: FC = ({ onSave, }) => { const { t } = useTranslation() + const { notify } = useToastContext() const { locale } = useContext(I18n) const language = languageMaps[locale] const [loading, setLoading] = useState(false) const [initialFormValueCleared, setInitialFormValueCleared] = useState(false) - const { data: formSchemasData, isLoading } = useSWR(`/workspaces/current/model-providers/${provider.provider}/credentials`, fetchModelProviderCredentials) - const initialFormValue = useMemo(() => { - if (!formSchemasData) - return {} - return formSchemasData.credenntials.reduce((acc: FormValue, cur) => { - acc[cur.variable] = cur.default - return acc - }, {}) - }, [formSchemasData]) - const [value, setValue] = useState(initialFormValue) + const { data: formSchemasValue, isLoading } = useSWR(`/workspaces/current/model-providers/${provider.provider}/credentials`, fetchModelProviderCredentials) + const initialFormSchemasValue = formSchemasValue?.credenntials || {} + const [value, setValue] = useState(initialFormSchemasValue) const [validate, validating, validatedStatusState] = useValidate(value) - const isEditMode = useMemo(() => { - if (!formSchemasData) - return false - return formSchemasData.credenntials.every(formSchema => formSchema.required && initialFormValue[formSchema.variable]) - }, [initialFormValue, formSchemasData]) + const isEditMode = Object.keys(initialFormSchemasValue).length !== 0 + const formSchemas = provider.provider_credential_schema.credential_form_schemas + const formRequiredValueAllCompleted = formSchemas.filter(formSchema => formSchema.required).every(formSchema => value[formSchema.variable]) + const providerFormSchemaPredefined = configurateMethod === ConfigurateMethodEnum.predefinedModel - const handleSave = () => { - onSave(value) + const handleValueChange = (v: FormValue) => { + setValue(v) - const validateKeys = formSchemasData?.credenntials.filter(formSchema => formSchema.required).map(formSchema => formSchema.variable) || [] + const validateKeys = formSchemas.filter(formSchema => formSchema.required).map(formSchema => formSchema.variable) || [] if (validateKeys.length) { validate({ before: () => { for (let i = 0; i < validateKeys.length; i++) { - if (!value[validateKeys[i]]) + if (!v[validateKeys[i]]) return false } return true }, run: () => { - return new Promise((resolve, reject) => { - setLoading(true) - setTimeout(() => { - setLoading(false) - resolve({}) - }, 1000) - }) + return validateCredentials( + providerFormSchemaPredefined, + provider.provider, + { + credentials: v, + }, + ) }, }) } } + const handleSave = async () => { + setLoading(true) + try { + const res = await saveCredentials( + providerFormSchemaPredefined, + provider.provider, + { + credentials: value, + }, + ) + if (res.result === 'success') { + notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') }) + onSave() + onCancel() + } + } + finally { + setLoading(false) + } + } + + const handleRemove = () => { + + } const renderTitlePrefix = () => { let prefix @@ -114,8 +136,8 @@ const ModelModal: FC = ({
setValue(val)} - formSchemas={formSchemasData?.credenntials || []} + onChange={handleValueChange} + formSchemas={formSchemas} isEditMode={isEditMode} initialFormValueCleared={initialFormValueCleared} onInitialFormValueCleared={val => setInitialFormValueCleared(val)} @@ -123,21 +145,43 @@ const ModelModal: FC = ({ validatedSuccess={validatedStatusState.status === ValidatedStatus.Success} />
- - {provider.help_text[language]} - - + { + (provider.help && (provider.help.title || provider.help.url)) + ? ( + !provider.help.url && e.preventDefault()} + > + {provider.help.title?.[language] || provider.help.url[language]} + + + ) + :
+ }
- + { + isEditMode && ( + + ) + } + diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/credential-panel.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/credential-panel.tsx index 5459035bc5ce51..ac79cc5c03b9b4 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/credential-panel.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/credential-panel.tsx @@ -1,42 +1,57 @@ +import type { FC } from 'react' import { useTranslation } from 'react-i18next' +import type { ModelProvider } from '../declarations' +import { + CustomConfigurationEnum, + PreferredProviderTypeEnum, +} from '../declarations' import PrioritySelector from './priority-selector' +import PriorityUseTip from './priority-use-tip' import Indicator from '@/app/components/header/indicator' import { Settings01 } from '@/app/components/base/icons/src/vender/line/general' -import { ChevronDownDouble } from '@/app/components/base/icons/src/vender/line/arrows' import Button from '@/app/components/base/button' -import Tooltip from '@/app/components/base/tooltip' -import { IS_CE_EDITION } from '@/config' -const CredentialPanel = () => { +type CredentialPanelProps = { + provider: ModelProvider + onSetup: () => void +} +const CredentialPanel: FC = ({ + provider, + onSetup, +}) => { const { t } = useTranslation() + const customConfig = provider.custom_configuration + const systemConfig = provider.system_configuration + const priorityUseType = provider.preferred_provider_type + + const handleChangePriority = () => {} return (
API-KEY - +
- { - !IS_CE_EDITION && ( + systemConfig.enabled && ( {}} - value='custom' + onSelect={handleChangePriority} + value={priorityUseType} /> ) }
{ - !IS_CE_EDITION && ( - -
- -
-
+ priorityUseType === PreferredProviderTypeEnum.custom && systemConfig.enabled && ( + ) }
diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/index.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/index.tsx index 29b63221f655b3..5d724efa4b46dc 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/index.tsx @@ -1,7 +1,11 @@ import type { FC } from 'react' import { useState } from 'react' import { useContext } from 'use-context-selector' -import type { ModelProvider } from '../declarations' +import type { + ModelItem, + ModelProvider, +} from '../declarations' +import { ConfigurateMethodEnum } from '../declarations' import { DEFAULT_BACKGROUND_COLOR, languageMaps, @@ -13,16 +17,44 @@ import ModelList from './model-list' import AddModelButton from './add-model-button' import I18n from '@/context/i18n' import { ChevronDownDouble } from '@/app/components/base/icons/src/vender/line/arrows' +import { Loading02 } from '@/app/components/base/icons/src/vender/line/general' +import { fetchModelProviderModelList } from '@/service/common' type ProviderAddedCardProps = { provider: ModelProvider + onOpenModal: (configurateMethod: ConfigurateMethodEnum) => void } const ProviderAddedCard: FC = ({ provider, + onOpenModal, }) => { const { locale } = useContext(I18n) const language = languageMaps[locale] + const [fetched, setFetched] = useState(false) + const [loading, setLoading] = useState(false) const [collapsed, setCollapsed] = useState(true) + const [modelList, setModelList] = useState([]) + const configurateMethods = provider.configurate_methods.filter(method => method !== ConfigurateMethodEnum.fetchFromRemote) + const systemConfig = provider.system_configuration + const hasModelList = fetched && !!modelList.length + + const handleOpenModelList = async () => { + if (fetched) { + setCollapsed(false) + return + } + + try { + setLoading(true) + const modelsData = await fetchModelProviderModelList(`/workspaces/current/model-providers/${provider.provider}/models`) + setModelList(modelsData.data) + setCollapsed(false) + setFetched(true) + } + finally { + setLoading(false) + } + } return (
= ({ }
- - + { + systemConfig.enabled && ( + + ) + } + { + configurateMethods.includes(ConfigurateMethodEnum.predefinedModel) && ( + onOpenModal(ConfigurateMethodEnum.predefinedModel)} + provider={provider} + /> + ) + }
{ collapsed && (
-
5 Models
+
+ { + hasModelList + ? `${modelList.length} Models` + : 'Show Models' + } +
setCollapsed(false)} + onClick={handleOpenModelList} > - Show 2 Models + { + hasModelList + ? `Show ${modelList.length} Models` + : 'Show Models' + } + { + loading && ( + + ) + }
- {}} - className='hidden group-hover:flex group-hover:text-primary-600' - /> + { + configurateMethods.includes(ConfigurateMethodEnum.customizableModel) && ( + {}} + className='hidden group-hover:flex group-hover:text-primary-600' + /> + ) + }
) } @@ -68,7 +132,7 @@ const ProviderAddedCard: FC = ({ !collapsed && ( setCollapsed(true)} /> ) diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-list.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-list.tsx index 3c3e077e714c39..2ad7645fd4570d 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-list.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-list.tsx @@ -1,18 +1,27 @@ import type { FC } from 'react' import { useContext } from 'use-context-selector' -import type { Model, ModelProvider } from '../declarations' -import { languageMaps } from '../utils' +import type { ModelItem, ModelProvider } from '../declarations' +import { + ConfigurateMethodEnum, + ModelStatusEnum, +} from '../declarations' +import { + languageMaps, + modelTypeFormat, + sizeFormat, +} from '../utils' import ModelBadge from '../model-badge' import Tab from './tab' import AddModelButton from './add-model-button' import Indicator from '@/app/components/header/indicator' import { Settings01 } from '@/app/components/base/icons/src/vender/line/general' import { ChevronDownDouble } from '@/app/components/base/icons/src/vender/line/arrows' +import Button from '@/app/components/base/button' import I18n from '@/context/i18n' type ModelListProps = { provider: ModelProvider - models: Model[] + models: ModelItem[] onCollapse: () => void } const ModelList: FC = ({ @@ -22,13 +31,18 @@ const ModelList: FC = ({ }) => { const { locale } = useContext(I18n) const language = languageMaps[locale] + const configurateMethods = provider.configurate_methods.filter(method => method !== ConfigurateMethodEnum.fetchFromRemote) + const canCustomConfig = configurateMethods.includes(ConfigurateMethodEnum.customizableModel) + const canSystemConfig = configurateMethods.includes(ConfigurateMethodEnum.predefinedModel) return (
- 2 Models + + {models.length} Models + - - {}} /> - - {}} /> + { + canCustomConfig && canSystemConfig && ( + + {}} /> + + ) + } + { + canCustomConfig && ( + {}} /> + ) + }
{ models.map(model => (
- {model.label[language]} + + {model.label[language]} +
+ { - model.features.map(feature => ( - - )) + model.model_properties.mode && ( + + ) + } + { + model.model_properties.context_size && ( + + ) }
-
- - Config -
- + { + canCustomConfig && ( + + ) + } +
)) diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/priority-selector.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/priority-selector.tsx index ba807df14759f6..a5d84ada0e0658 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/priority-selector.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/priority-selector.tsx @@ -2,6 +2,7 @@ import { Fragment } from 'react' import type { FC } from 'react' import { Popover, Transition } from '@headlessui/react' import { useTranslation } from 'react-i18next' +import { PreferredProviderTypeEnum } from '../declarations' import { Check, DotsHorizontal } from '@/app/components/base/icons/src/vender/line/general' import Button from '@/app/components/base/button' @@ -16,11 +17,11 @@ const Selector: FC = ({ const { t } = useTranslation() const options = [ { - key: 'custom', + key: PreferredProviderTypeEnum.custom, text: 'API', }, { - key: 'system', + key: PreferredProviderTypeEnum.system, text: t('common.modelProvider.quota'), }, ] diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/priority-use-tip.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/priority-use-tip.tsx new file mode 100644 index 00000000000000..e46543f92db39e --- /dev/null +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/priority-use-tip.tsx @@ -0,0 +1,14 @@ +import { ChevronDownDouble } from '@/app/components/base/icons/src/vender/line/arrows' +import Tooltip from '@/app/components/base/tooltip' + +const PriorityUseTip = () => { + return ( + +
+ +
+
+ ) +} + +export default PriorityUseTip diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/quota-panel.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/quota-panel.tsx index 9db986b124a12a..8ead8f927e9968 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/quota-panel.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/quota-panel.tsx @@ -1,6 +1,21 @@ +import type { FC } from 'react' +import type { ModelProvider } from '../declarations' +import { + CustomConfigurationEnum, + PreferredProviderTypeEnum, +} from '../declarations' +import PriorityUseTip from './priority-use-tip' import { InfoCircle } from '@/app/components/base/icons/src/vender/line/general' -const QuotaPanel = () => { +type QuotaPanelProps = { + provider: ModelProvider +} +const QuotaPanel: FC = ({ + provider, +}) => { + const customConfig = provider.custom_configuration + const priorityUseType = provider.preferred_provider_type + return (
@@ -18,6 +33,11 @@ const QuotaPanel = () => { `}> Buy Quota
+ { + priorityUseType === PreferredProviderTypeEnum.system && customConfig.status === CustomConfigurationEnum.active && ( + + ) + }
) } diff --git a/web/app/components/header/account-setting/model-provider-page/provider-card/index.tsx b/web/app/components/header/account-setting/model-provider-page/provider-card/index.tsx index 8bf3c56f4c127f..1f9a003ea65b96 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-card/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-card/index.tsx @@ -6,6 +6,7 @@ import { ConfigurateMethodEnum } from '../declarations' import { DEFAULT_BACKGROUND_COLOR, languageMaps, + modelTypeFormat, } from '../utils' import ModelBadge from '../model-badge' import I18n from '@/context/i18n' @@ -64,7 +65,7 @@ const ProviderCard: FC = ({ provider.supported_model_types.map(modelType => ( )) } diff --git a/web/app/components/header/account-setting/model-provider-page/utils.ts b/web/app/components/header/account-setting/model-provider-page/utils.ts index 1d45465bb545a4..e8676e3d9da586 100644 --- a/web/app/components/header/account-setting/model-provider-page/utils.ts +++ b/web/app/components/header/account-setting/model-provider-page/utils.ts @@ -1,3 +1,11 @@ +import { ValidatedStatus } from '../key-validator/declarations' +import type { FormValue } from './declarations' +import { ModelTypeEnum } from './declarations' +import { + setModelProvider, + validateModelProvider, +} from '@/service/common' + export const languageMaps = { 'en': 'en_US', 'zh-Hans': 'zh_Hans', @@ -7,3 +15,77 @@ export const languageMaps = { } export const DEFAULT_BACKGROUND_COLOR = '#F3F4F6' + +type validateModelProviderBody = { + model?: string + model_type?: ModelTypeEnum + credentials: FormValue +} +export const validateCredentials = async (predefined: boolean, provider: string, v: validateModelProviderBody) => { + let body, url + + if (predefined) { + const { credentials } = v + body = { + credentials, + } + url = `/workspaces/current/model-providers/${provider}/credentials/validate` + } + else { + const { model, model_type, credentials } = v + body = { + model, + model_type, + credentials, + } + url = `/workspaces/current/model-providers/${provider}/models/credentials/validate` + } + try { + const res = await validateModelProvider({ url, body }) + if (res.result === 'success') + return Promise.resolve({ status: ValidatedStatus.Success }) + else + return Promise.resolve({ status: ValidatedStatus.Error, message: res.error }) + } + catch (e: any) { + return Promise.resolve({ status: ValidatedStatus.Error, message: e.message }) + } +} + +export const saveCredentials = async (predefined: boolean, provider: string, v: validateModelProviderBody) => { + let body, url + + if (predefined) { + const { credentials } = v + body = { + credentials, + } + url = `/workspaces/current/model-providers/${provider}` + } + else { + const { model, model_type, credentials } = v + body = { + model, + model_type, + credentials, + } + url = `/workspaces/current/model-providers/${provider}/models` + } + + return setModelProvider({ url, body }) +} + +export const sizeFormat = (size: number) => { + const remainder = Math.floor(size / 1000) + if (remainder < 1) + return `${size}` + else + return `${remainder}K` +} + +export const modelTypeFormat = (modelType: ModelTypeEnum) => { + if (modelType === ModelTypeEnum.textEmbedding) + return 'TEXT EMBEDDING' + + return modelType.toLocaleUpperCase() +} diff --git a/web/service/common.ts b/web/service/common.ts index 15838550361bad..1cb54d5c192132 100644 --- a/web/service/common.ts +++ b/web/service/common.ts @@ -27,8 +27,8 @@ import type { } from '@/models/app' import type { BackendModel } from '@/app/components/header/account-setting/model-page/declarations' import type { - CredentialFormSchema, Model, + ModelItem, ModelProvider, } from '@/app/components/header/account-setting/model-provider-page/declarations' import type { RETRIEVE_METHOD } from '@/types/app' @@ -155,8 +155,12 @@ export const fetchModelProviders: Fetcher<{ data: ModelProvider[] }, string> = ( return get<{ data: ModelProvider[] }>(url) } -export const fetchModelProviderCredentials: Fetcher<{ credenntials: CredentialFormSchema[] }, string> = (url) => { - return get<{ credenntials: CredentialFormSchema[] }>(url) +export const fetchModelProviderCredentials: Fetcher<{ credenntials?: Record }, string> = (url) => { + return get<{ credenntials?: Record }>(url) +} + +export const fetchModelProviderModelList: Fetcher<{ data: ModelItem[] }, string> = (url) => { + return get<{ data: ModelItem[] }>(url) } export const fetchModelList: Fetcher<{ data: Model[] }, string> = (url) => { From 41fdb9c985430f15baf8e1f7e33d5e3c892acf8a Mon Sep 17 00:00:00 2001 From: StyleZhang Date: Wed, 20 Dec 2023 19:31:28 +0800 Subject: [PATCH 05/46] feat: model provider --- .../model-provider-page/model-badge/index.tsx | 11 ++- .../model-provider-page/model-modal/Form.tsx | 22 ----- .../model-provider-page/model-modal/Input.tsx | 2 +- .../model-provider-page/model-modal/index.tsx | 99 ++++++++++++++----- .../model-selector/empty-trigger.tsx | 36 +++++++ .../model-selector/index.tsx | 30 ++++++ .../model-selector/model-trigger.tsx | 55 +++++++++++ .../model-selector/popup.tsx | 7 ++ .../model-selector/rerank-trigger.tsx | 25 +++++ .../provider-added-card/index.tsx | 8 +- .../provider-added-card/model-list.tsx | 12 ++- .../provider-card/index.tsx | 7 +- .../model-provider-page/utils.ts | 22 +++++ web/service/common.ts | 8 +- 14 files changed, 279 insertions(+), 65 deletions(-) create mode 100644 web/app/components/header/account-setting/model-provider-page/model-selector/empty-trigger.tsx create mode 100644 web/app/components/header/account-setting/model-provider-page/model-selector/index.tsx create mode 100644 web/app/components/header/account-setting/model-provider-page/model-selector/model-trigger.tsx create mode 100644 web/app/components/header/account-setting/model-provider-page/model-selector/popup.tsx create mode 100644 web/app/components/header/account-setting/model-provider-page/model-selector/rerank-trigger.tsx diff --git a/web/app/components/header/account-setting/model-provider-page/model-badge/index.tsx b/web/app/components/header/account-setting/model-provider-page/model-badge/index.tsx index 342c71ec2811a5..e0ea74abe0eb36 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-badge/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-badge/index.tsx @@ -1,17 +1,20 @@ -import type { FC } from 'react' +import type { FC, ReactNode } from 'react' type ModelBadgeProps = { - text: string + className?: string + children?: ReactNode } const ModelBadge: FC = ({ - text, + className, + children, }) => { return (
- {text} + {children}
) } diff --git a/web/app/components/header/account-setting/model-provider-page/model-modal/Form.tsx b/web/app/components/header/account-setting/model-provider-page/model-modal/Form.tsx index ac4350dfbd6b92..6a3e8c21c0d9d1 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-modal/Form.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-modal/Form.tsx @@ -20,9 +20,6 @@ type FormProps = { value: FormValue onChange: (val: FormValue) => void formSchemas: CredentialFormSchema[] - isEditMode: boolean - initialFormValueCleared: boolean - onInitialFormValueCleared: (val: boolean) => void validating: boolean validatedSuccess?: boolean } @@ -31,9 +28,6 @@ const Form: FC = ({ value, onChange, formSchemas, - isEditMode, - initialFormValueCleared, - onInitialFormValueCleared, validating, validatedSuccess, }) => { @@ -41,21 +35,6 @@ const Form: FC = ({ const language = languageMaps[locale] const [changeKey, setChangeKey] = useState('') - const handleClearInitialFormValue = () => { - const needClearInitialFormValues = formSchemas.filter(formSchema => formSchema.type === (FormTypeEnum.secretInput || formSchema.type === FormTypeEnum.textInput)) - const newValue: Record = {} - needClearInitialFormValues?.forEach((field) => { - newValue[field.variable] = '' - }) - onChange({ ...value, ...newValue }) - onInitialFormValueCleared(true) - } - - const handleFocus = () => { - if (isEditMode && !initialFormValueCleared) - handleClearInitialFormValue() - } - const handleFormChange = (key: string, val: string) => { setChangeKey(key) onChange({ ...value, [key]: val }) @@ -82,7 +61,6 @@ const Form: FC = ({ handleFormChange(variable, val)} - onFocus={handleFocus} validated={validatedSuccess} placeholder={placeholder[language]} /> diff --git a/web/app/components/header/account-setting/model-provider-page/model-modal/Input.tsx b/web/app/components/header/account-setting/model-provider-page/model-modal/Input.tsx index 4b0e575dd87fda..9fe8fd0b2eb6d1 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-modal/Input.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-modal/Input.tsx @@ -4,7 +4,7 @@ import { CheckCircle } from '@/app/components/base/icons/src/vender/solid/genera type InputProps = { value?: string onChange: (v: string) => void - onFocus: () => void + onFocus?: () => void placeholder?: string validated?: boolean } diff --git a/web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx b/web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx index 6db67e46eb8efa..48cbfff6fab7eb 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx @@ -1,15 +1,20 @@ import type { FC } from 'react' -import { useState } from 'react' +import { useEffect, useMemo, useState } from 'react' import useSWR from 'swr' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import type { + CredentialFormSchema, FormValue, ModelProvider, } from '../declarations' -import { ConfigurateMethodEnum } from '../declarations' +import { + ConfigurateMethodEnum, + FormTypeEnum, +} from '../declarations' import { languageMaps, + removeCredentials, saveCredentials, validateCredentials, } from '../utils' @@ -28,6 +33,7 @@ import { import { fetchModelProviderCredentials } from '@/service/common' import Loading from '@/app/components/base/loading' import { useToastContext } from '@/app/components/base/toast' +import ConfirmCommon from '@/app/components/base/confirm/common' type ModelModalProps = { provider: ModelProvider @@ -47,25 +53,43 @@ const ModelModal: FC = ({ const { locale } = useContext(I18n) const language = languageMaps[locale] const [loading, setLoading] = useState(false) - const [initialFormValueCleared, setInitialFormValueCleared] = useState(false) + const [showConfirm, setShowConfirm] = useState(false) const { data: formSchemasValue, isLoading } = useSWR(`/workspaces/current/model-providers/${provider.provider}/credentials`, fetchModelProviderCredentials) - const initialFormSchemasValue = formSchemasValue?.credenntials || {} + const initialFormSchemasValue = useMemo(() => { + return formSchemasValue?.credentials || {} + }, [formSchemasValue]) const [value, setValue] = useState(initialFormSchemasValue) + useEffect(() => { + setValue(initialFormSchemasValue) + }, [initialFormSchemasValue]) const [validate, validating, validatedStatusState] = useValidate(value) - const isEditMode = Object.keys(initialFormSchemasValue).length !== 0 + const isEditMode = !!formSchemasValue?.credentials const formSchemas = provider.provider_credential_schema.credential_form_schemas - const formRequiredValueAllCompleted = formSchemas.filter(formSchema => formSchema.required).every(formSchema => value[formSchema.variable]) const providerFormSchemaPredefined = configurateMethod === ConfigurateMethodEnum.predefinedModel + const [requiredFormSchemas, secretFormSchemas] = useMemo(() => { + const requiredFormSchemas: CredentialFormSchema[] = [] + const secretFormSchemas: CredentialFormSchema[] = [] + + formSchemas.forEach((formSchema) => { + if (formSchema.required) + requiredFormSchemas.push(formSchema) + + if (formSchema.type === FormTypeEnum.secretInput) + secretFormSchemas.push(formSchema) + }) + + return [requiredFormSchemas, secretFormSchemas] + }, [formSchemas]) + const formRequiredValueAllCompleted = requiredFormSchemas.every(formSchema => value[formSchema.variable]) const handleValueChange = (v: FormValue) => { setValue(v) - const validateKeys = formSchemas.filter(formSchema => formSchema.required).map(formSchema => formSchema.variable) || [] - if (validateKeys.length) { + if (requiredFormSchemas.length) { validate({ before: () => { - for (let i = 0; i < validateKeys.length; i++) { - if (!v[validateKeys[i]]) + for (let i = 0; i < requiredFormSchemas.length; i++) { + if (!v[requiredFormSchemas[i].variable]) return false } return true @@ -83,13 +107,23 @@ const ModelModal: FC = ({ } } const handleSave = async () => { - setLoading(true) try { + setLoading(true) + const secretValues = secretFormSchemas.reduce((prev, next) => { + if (value[next.variable] === initialFormSchemasValue[next.variable]) + prev[next.variable] = '[__HIDDEN__]' + + return prev + }, {} as Record) + const res = await saveCredentials( providerFormSchemaPredefined, provider.provider, { - credentials: value, + credentials: { + ...value, + ...secretValues, + }, }, ) if (res.result === 'success') { @@ -103,16 +137,27 @@ const ModelModal: FC = ({ } } - const handleRemove = () => { + const handleRemove = async () => { + try { + setLoading(true) + const res = await removeCredentials( + providerFormSchemaPredefined, + provider.provider, + ) + if (res.result === 'success') { + notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') }) + onSave() + onCancel() + } + } + finally { + setLoading(false) + } } const renderTitlePrefix = () => { - let prefix - if (isEditMode) - prefix = t('common.operation.edit') - else - prefix = configurateMethod === ConfigurateMethodEnum.customizableModel ? t('common.operation.create') : t('common.operation.setup') + const prefix = configurateMethod === ConfigurateMethodEnum.customizableModel ? t('common.operation.add') : t('common.operation.setup') return `${prefix} ${provider.label[language]}` } @@ -138,9 +183,6 @@ const ModelModal: FC = ({ value={value} onChange={handleValueChange} formSchemas={formSchemas} - isEditMode={isEditMode} - initialFormValueCleared={initialFormValueCleared} - onInitialFormValueCleared={val => setInitialFormValueCleared(val)} validating={validating} validatedSuccess={validatedStatusState.status === ValidatedStatus.Success} /> @@ -165,7 +207,7 @@ const ModelModal: FC = ({ isEditMode && ( @@ -181,7 +223,7 @@ const ModelModal: FC = ({ className='h-9 text-sm font-medium' type='primary' onClick={handleSave} - disabled={loading || (isEditMode && !initialFormValueCleared) || validating || !formRequiredValueAllCompleted} + disabled={loading || validating || !formRequiredValueAllCompleted} > {t('common.operation.save')} @@ -214,6 +256,17 @@ const ModelModal: FC = ({ }
+ { + showConfirm && ( + setShowConfirm(false)} + onConfirm={handleRemove} + confirmWrapperClassName='z-[70]' + /> + ) + }
) } diff --git a/web/app/components/header/account-setting/model-provider-page/model-selector/empty-trigger.tsx b/web/app/components/header/account-setting/model-provider-page/model-selector/empty-trigger.tsx new file mode 100644 index 00000000000000..2337ab9d66464a --- /dev/null +++ b/web/app/components/header/account-setting/model-provider-page/model-selector/empty-trigger.tsx @@ -0,0 +1,36 @@ +import type { FC } from 'react' +import { ChevronDown } from '@/app/components/base/icons/src/vender/line/arrows' +import { CubeOutline } from '@/app/components/base/icons/src/vender/line/shapes' + +type ModelTriggerProps = { + open: boolean +} +const ModelTrigger: FC = ({ + open, +}) => { + return ( +
+
+
+ +
+
+ Select model +
+
+
+ +
+
+ ) +} + +export default ModelTrigger diff --git a/web/app/components/header/account-setting/model-provider-page/model-selector/index.tsx b/web/app/components/header/account-setting/model-provider-page/model-selector/index.tsx new file mode 100644 index 00000000000000..ae4ab27e358652 --- /dev/null +++ b/web/app/components/header/account-setting/model-provider-page/model-selector/index.tsx @@ -0,0 +1,30 @@ +import { useState } from 'react' +import { + PortalToFollowElem, + PortalToFollowElemContent, + PortalToFollowElemTrigger, +} from '@/app/components/base/portal-to-follow-elem' + +const ModelSelector = () => { + const [open, setOpen] = useState(false) + + return ( + +
+ setOpen(v => !v)} + > + + + +
+
+ ) +} + +export default ModelSelector diff --git a/web/app/components/header/account-setting/model-provider-page/model-selector/model-trigger.tsx b/web/app/components/header/account-setting/model-provider-page/model-selector/model-trigger.tsx new file mode 100644 index 00000000000000..12c363dba84679 --- /dev/null +++ b/web/app/components/header/account-setting/model-provider-page/model-selector/model-trigger.tsx @@ -0,0 +1,55 @@ +import type { FC } from 'react' +import { useContext } from 'use-context-selector' +import type { ModelItem } from '../declarations' +import { + languageMaps, +} from '../utils' +import ModelBadge from '../model-badge' +import I18n from '@/context/i18n' +// import { AlertTriangle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback' +import { ChevronDown } from '@/app/components/base/icons/src/vender/line/arrows' + +type ModelTriggerProps = { + open: boolean + model: ModelItem +} +const ModelTrigger: FC = ({ + open, + model, +}) => { + const { locale } = useContext(I18n) + const language = languageMaps[locale] + + return ( +
+
+
+
+ {model.label[language]} +
+ { + model.model_properties.mode && ( + + {(model.model_properties.mode as string).toLocaleUpperCase()} + + ) + } +
+
+ +
+
+ ) +} + +export default ModelTrigger diff --git a/web/app/components/header/account-setting/model-provider-page/model-selector/popup.tsx b/web/app/components/header/account-setting/model-provider-page/model-selector/popup.tsx new file mode 100644 index 00000000000000..566a0056aea755 --- /dev/null +++ b/web/app/components/header/account-setting/model-provider-page/model-selector/popup.tsx @@ -0,0 +1,7 @@ +const Popup = () => { + return ( +
+ ) +} + +export default Popup diff --git a/web/app/components/header/account-setting/model-provider-page/model-selector/rerank-trigger.tsx b/web/app/components/header/account-setting/model-provider-page/model-selector/rerank-trigger.tsx new file mode 100644 index 00000000000000..251201f21ec725 --- /dev/null +++ b/web/app/components/header/account-setting/model-provider-page/model-selector/rerank-trigger.tsx @@ -0,0 +1,25 @@ +import { CubeOutline } from '@/app/components/base/icons/src/vender/line/shapes' +import { LinkExternal01 } from '@/app/components/base/icons/src/vender/line/general' + +const ModelTrigger = () => { + return ( +
+
+
+ +
+
+ Please setup the Rerank model +
+
+
+ +
+
+ ) +} + +export default ModelTrigger diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/index.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/index.tsx index 5d724efa4b46dc..0fd09a1ff2ba85 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/index.tsx @@ -9,6 +9,7 @@ import { ConfigurateMethodEnum } from '../declarations' import { DEFAULT_BACKGROUND_COLOR, languageMaps, + modelTypeFormat, } from '../utils' import ModelBadge from '../model-badge' import CredentialPanel from './credential-panel' @@ -67,10 +68,9 @@ const ProviderAddedCard: FC = ({
{ provider.supported_model_types.map(modelType => ( - + + {modelTypeFormat(modelType)} + )) }
diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-list.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-list.tsx index 2ad7645fd4570d..73ac096f8a88cc 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-list.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-list.tsx @@ -84,15 +84,21 @@ const ModelList: FC = ({ {model.label[language]}
- + + {modelTypeFormat(model.model_type)} + { model.model_properties.mode && ( - + + {(model.model_properties.mode as string).toLocaleUpperCase()} + ) } { model.model_properties.context_size && ( - + + {sizeFormat(model.model_properties.context_size as number)} + ) }
diff --git a/web/app/components/header/account-setting/model-provider-page/provider-card/index.tsx b/web/app/components/header/account-setting/model-provider-page/provider-card/index.tsx index 1f9a003ea65b96..45e1cc6098e0b8 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-card/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-card/index.tsx @@ -63,10 +63,9 @@ const ProviderCard: FC = ({
{ provider.supported_model_types.map(modelType => ( - + + {modelTypeFormat(modelType)} + )) }
diff --git a/web/app/components/header/account-setting/model-provider-page/utils.ts b/web/app/components/header/account-setting/model-provider-page/utils.ts index e8676e3d9da586..9a0d2fd6efbce5 100644 --- a/web/app/components/header/account-setting/model-provider-page/utils.ts +++ b/web/app/components/header/account-setting/model-provider-page/utils.ts @@ -2,6 +2,7 @@ import { ValidatedStatus } from '../key-validator/declarations' import type { FormValue } from './declarations' import { ModelTypeEnum } from './declarations' import { + deleteModelProvider, setModelProvider, validateModelProvider, } from '@/service/common' @@ -75,6 +76,27 @@ export const saveCredentials = async (predefined: boolean, provider: string, v: return setModelProvider({ url, body }) } +export const removeCredentials = async (predefined: boolean, provider: string, v?: Pick) => { + let url = '' + let body + + if (predefined) { + url = `/workspaces/current/model-providers/${provider}` + } + else { + if (v) { + const { model, model_type } = v + body = { + model, + model_type, + } + url = `/workspaces/current/model-providers/${provider}/models` + } + } + + return deleteModelProvider({ url, body }) +} + export const sizeFormat = (size: number) => { const remainder = Math.floor(size / 1000) if (remainder < 1) diff --git a/web/service/common.ts b/web/service/common.ts index 1cb54d5c192132..cb7209e1cfbaa9 100644 --- a/web/service/common.ts +++ b/web/service/common.ts @@ -155,8 +155,8 @@ export const fetchModelProviders: Fetcher<{ data: ModelProvider[] }, string> = ( return get<{ data: ModelProvider[] }>(url) } -export const fetchModelProviderCredentials: Fetcher<{ credenntials?: Record }, string> = (url) => { - return get<{ credenntials?: Record }>(url) +export const fetchModelProviderCredentials: Fetcher<{ credentials?: Record }, string> = (url) => { + return get<{ credentials?: Record }>(url) } export const fetchModelProviderModelList: Fetcher<{ data: ModelItem[] }, string> = (url) => { @@ -175,8 +175,8 @@ export const setModelProvider: Fetcher(url, { body }) } -export const deleteModelProvider: Fetcher = ({ url }) => { - return del(url) +export const deleteModelProvider: Fetcher = ({ url, body }) => { + return del(url, { body }) } export const changeModelProviderPriority: Fetcher = ({ url, body }) => { From b68bcc21b3e30048b3ceeb3f9426263cca99a92e Mon Sep 17 00:00:00 2001 From: StyleZhang Date: Thu, 21 Dec 2023 16:33:48 +0800 Subject: [PATCH 06/46] feat: model provider --- .../app/configuration/config-model/index.tsx | 12 +- .../components/app/configuration/index.tsx | 50 ++--- .../toolbox/annotation/config-param-modal.tsx | 6 +- .../app/overview/apikey-info-panel/index.tsx | 66 +------ .../solid/mediaAndDevices/magic-box.svg | 9 + .../solid/mediaAndDevices/magic-eyes.svg | 5 + .../solid/mediaAndDevices/magic-wand.svg | 10 + .../vender/solid/mediaAndDevices/robot.svg | 5 + .../solid/mediaAndDevices/MagicBox.json | 64 +++++++ .../vender/solid/mediaAndDevices/MagicBox.tsx | 16 ++ .../solid/mediaAndDevices/MagicEyes.json | 38 ++++ .../solid/mediaAndDevices/MagicEyes.tsx | 16 ++ .../solid/mediaAndDevices/MagicWand.json | 73 ++++++++ .../solid/mediaAndDevices/MagicWand.tsx | 16 ++ .../vender/solid/mediaAndDevices/Robot.json | 38 ++++ .../vender/solid/mediaAndDevices/Robot.tsx | 16 ++ .../src/vender/solid/mediaAndDevices/index.ts | 4 + .../model-provider-page/declarations.ts | 14 +- .../model-provider-page/hooks.ts | 35 ++++ .../model-provider-page/index.tsx | 10 +- .../model-provider-page/model-modal/index.tsx | 3 +- .../model-selector/empty-trigger.tsx | 4 +- .../model-selector/feature-icon.tsx | 52 ++++++ .../model-selector/index.tsx | 49 ++++- .../model-selector/model-trigger.tsx | 33 +++- .../model-selector/popup-item.tsx | 106 +++++++++++ .../model-selector/popup.tsx | 79 +++++++- .../provider-added-card/credential-panel.tsx | 4 +- .../provider-added-card/index.tsx | 6 +- .../provider-added-card/quota-panel.tsx | 4 +- .../provider-card/index.tsx | 20 +- .../provider-icon/index.tsx | 38 ++++ .../system-model-selector/index.tsx | 172 ++++++++++++++++++ web/context/provider-context.tsx | 38 ++-- web/service/common.ts | 6 +- 35 files changed, 950 insertions(+), 167 deletions(-) create mode 100644 web/app/components/base/icons/assets/vender/solid/mediaAndDevices/magic-box.svg create mode 100644 web/app/components/base/icons/assets/vender/solid/mediaAndDevices/magic-eyes.svg create mode 100644 web/app/components/base/icons/assets/vender/solid/mediaAndDevices/magic-wand.svg create mode 100644 web/app/components/base/icons/assets/vender/solid/mediaAndDevices/robot.svg create mode 100644 web/app/components/base/icons/src/vender/solid/mediaAndDevices/MagicBox.json create mode 100644 web/app/components/base/icons/src/vender/solid/mediaAndDevices/MagicBox.tsx create mode 100644 web/app/components/base/icons/src/vender/solid/mediaAndDevices/MagicEyes.json create mode 100644 web/app/components/base/icons/src/vender/solid/mediaAndDevices/MagicEyes.tsx create mode 100644 web/app/components/base/icons/src/vender/solid/mediaAndDevices/MagicWand.json create mode 100644 web/app/components/base/icons/src/vender/solid/mediaAndDevices/MagicWand.tsx create mode 100644 web/app/components/base/icons/src/vender/solid/mediaAndDevices/Robot.json create mode 100644 web/app/components/base/icons/src/vender/solid/mediaAndDevices/Robot.tsx create mode 100644 web/app/components/header/account-setting/model-provider-page/hooks.ts create mode 100644 web/app/components/header/account-setting/model-provider-page/model-selector/feature-icon.tsx create mode 100644 web/app/components/header/account-setting/model-provider-page/model-selector/popup-item.tsx create mode 100644 web/app/components/header/account-setting/model-provider-page/provider-icon/index.tsx create mode 100644 web/app/components/header/account-setting/model-provider-page/system-model-selector/index.tsx diff --git a/web/app/components/app/configuration/config-model/index.tsx b/web/app/components/app/configuration/config-model/index.tsx index 549bc704ad99ce..1e17edd1a6eced 100644 --- a/web/app/components/app/configuration/config-model/index.tsx +++ b/web/app/components/app/configuration/config-model/index.tsx @@ -33,8 +33,8 @@ export type IConfigModelProps = { isAdvancedMode: boolean mode: string modelId: string - provider: ProviderEnum - setModel: (model: { id: string; provider: ProviderEnum; mode: ModelModeType; features: string[] }) => void + provider: string + setModel: (model: { id: string; provider: string; mode: ModelModeType; features: string[] }) => void completionParams: CompletionParams onCompletionParamsChange: (newParams: CompletionParams) => void disabled: boolean @@ -89,7 +89,7 @@ const ConfigModel: FC = ({ const selectedModel = { name: modelId } // options.find(option => option.id === modelId) - const ensureModelParamLoaded = (provider: ProviderEnum, modelId: string) => { + const ensureModelParamLoaded = (provider: string, modelId: string) => { return new Promise((resolve) => { if (getAllParams()[provider]?.[modelId]) { resolve() @@ -126,13 +126,13 @@ const ConfigModel: FC = ({ return adjustedValue } - const handleSelectModel = ({ id, provider: nextProvider, mode, features }: { id: string; provider: ProviderEnum; mode: ModelModeType; features: string[] }) => { + const handleSelectModel = ({ id, provider: nextProvider, mode, features }: { id: string; provider: string; mode: ModelModeType; features: string[] }) => { return async () => { const prevParamsRule = getAllParams()[provider]?.[modelId] setModel({ id, - provider: nextProvider || ProviderEnum.openai, + provider: nextProvider || 'openai', mode, features, }) @@ -269,7 +269,7 @@ const ConfigModel: FC = ({ const max = currParams.max_tokens.max const isSupportMaxToken = currParams.max_tokens.enabled - if (isSupportMaxToken && currModel?.model_provider.provider_name !== ProviderEnum.anthropic && completionParams.max_tokens > max * 2 / 3) + if (isSupportMaxToken && currModel?.model_provider.provider_name !== 'anthropic' && completionParams.max_tokens > max * 2 / 3) setMaxTokenSettingTipVisible(true) else setMaxTokenSettingTipVisible(false) diff --git a/web/app/components/app/configuration/index.tsx b/web/app/components/app/configuration/index.tsx index 083adb333dcc0d..dc480fbe9743ed 100644 --- a/web/app/components/app/configuration/index.tsx +++ b/web/app/components/app/configuration/index.tsx @@ -33,7 +33,7 @@ import ConfigModel from '@/app/components/app/configuration/config-model' import Config from '@/app/components/app/configuration/config' import Debug from '@/app/components/app/configuration/debug' import Confirm from '@/app/components/base/confirm' -import { ModelFeature, ProviderEnum } from '@/app/components/header/account-setting/model-page/declarations' +import { ModelFeatureEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' import { ToastContext } from '@/app/components/base/toast' import { fetchAppDetail, updateAppModelConfig } from '@/service/apps' import { promptVariablesToUserInputsForm, userInputsFormToPromptVariables } from '@/utils/model-config' @@ -133,7 +133,7 @@ const Configuration: FC = () => { } const [modelConfig, doSetModelConfig] = useState({ - provider: ProviderEnum.openai, + provider: 'openai', model_id: 'gpt-3.5-turbo', mode: ModelModeType.unset, configs: { @@ -223,28 +223,16 @@ const Configuration: FC = () => { }) } - const { textGenerationModelList } = useProviderContext() - const currModel = textGenerationModelList.find(item => item.model_name === modelConfig.model_id) - const hasSetCustomAPIKEY = !!textGenerationModelList?.find(({ model_provider: provider }) => { - if (provider.provider_type === 'system' && provider.quota_type === 'paid') - return true - - if (provider.provider_type === 'custom') - return true - - return false - }) - const isTrailFinished = !hasSetCustomAPIKEY && textGenerationModelList - .filter(({ model_provider: provider }) => provider.quota_type === 'trial') - .every(({ model_provider: provider }) => { - const { quota_used, quota_limit } = provider - return quota_used === quota_limit - }) + const { + hasSettedApiKey, + textGenerationModelList, + } = useProviderContext() + const currModel = textGenerationModelList.find(item => item.provider === modelConfig.provider)?.models.find(model => model.model === modelConfig.model_id) // Fill old app data missing model mode. useEffect(() => { if (hasFetchedDetail && !modelModeType) { - const mode = textGenerationModelList.find(({ model_name }) => model_name === modelConfig.model_id)?.model_mode + const mode = currModel?.model_properties.mode as (ModelModeType | undefined) if (mode) { const newModelConfig = produce(modelConfig, (draft: ModelConfig) => { draft.mode = mode @@ -252,9 +240,7 @@ const Configuration: FC = () => { setModelConfig(newModelConfig) } } - }, [textGenerationModelList, hasFetchedDetail]) - - const hasSetAPIKEY = hasSetCustomAPIKEY || !isTrailFinished + }, [textGenerationModelList, hasFetchedDetail, modelModeType, currModel, modelConfig]) const [promptMode, doSetPromptMode] = useState(PromptMode.simple) const isAdvancedMode = promptMode === PromptMode.advanced @@ -299,7 +285,7 @@ const Configuration: FC = () => { provider, mode: modeMode, features, - }: { id: string; provider: ProviderEnum; mode: ModelModeType; features: string[] }) => { + }: { id: string; provider: string; mode: ModelModeType; features: string[] }) => { if (isAdvancedMode) { const appMode = mode @@ -325,7 +311,7 @@ const Configuration: FC = () => { }) setModelConfig(newModelConfig) - const supportVision = features && features.includes(ModelFeature.vision) + const supportVision = features && features.includes(ModelFeatureEnum.vision) // eslint-disable-next-line @typescript-eslint/no-use-before-define setVisionConfig({ // eslint-disable-next-line @typescript-eslint/no-use-before-define @@ -334,7 +320,7 @@ const Configuration: FC = () => { }, true) } - const isShowVisionConfig = !!currModel?.features.includes(ModelFeature.vision) + const isShowVisionConfig = !!currModel?.features.includes(ModelFeatureEnum.vision) const [visionConfig, doSetVisionConfig] = useState({ enabled: false, number_limits: 2, @@ -566,8 +552,8 @@ const Configuration: FC = () => { return ( { { setCompletionParams(newParams) }} - disabled={!hasSetAPIKEY} + disabled={!hasSettedApiKey} />
@@ -689,7 +675,7 @@ const Configuration: FC = () => {
{!isMobile &&
setShowAccountSettingModal({ payload: 'provider' })} inputs={inputs} /> @@ -744,7 +730,7 @@ const Configuration: FC = () => { {isMobile && ( setShowAccountSettingModal({ payload: 'provider' })} inputs={inputs} /> diff --git a/web/app/components/app/configuration/toolbox/annotation/config-param-modal.tsx b/web/app/components/app/configuration/toolbox/annotation/config-param-modal.tsx index c273ba03d6d0b8..95234e52a1e861 100644 --- a/web/app/components/app/configuration/toolbox/annotation/config-param-modal.tsx +++ b/web/app/components/app/configuration/toolbox/annotation/config-param-modal.tsx @@ -47,8 +47,8 @@ const ConfigParamModal: FC = ({ } : (embeddingsDefaultModel ? { - providerName: embeddingsDefaultModel.model_provider.provider_name, - modelName: embeddingsDefaultModel.model_name, + providerName: embeddingsDefaultModel.provider.provider, + modelName: embeddingsDefaultModel.model, } : undefined)) const onHide = () => { @@ -57,7 +57,7 @@ const ConfigParamModal: FC = ({ } const handleSave = async () => { - if (!embeddingModel || !embeddingModel.modelName || (embeddingModel.modelName === embeddingsDefaultModel?.model_name && !isEmbeddingsDefaultModelValid)) { + if (!embeddingModel || !embeddingModel.modelName || (embeddingModel.modelName === embeddingsDefaultModel?.model && !isEmbeddingsDefaultModelValid)) { Toast.notify({ message: t('common.modelProvider.embeddingModel.required'), type: 'error', diff --git a/web/app/components/app/overview/apikey-info-panel/index.tsx b/web/app/components/app/overview/apikey-info-panel/index.tsx index d07c2af48413a8..99155de4ff2294 100644 --- a/web/app/components/app/overview/apikey-info-panel/index.tsx +++ b/web/app/components/app/overview/apikey-info-panel/index.tsx @@ -3,78 +3,35 @@ import type { FC } from 'react' import React, { useState } from 'react' import { useTranslation } from 'react-i18next' import cn from 'classnames' -import { useContext } from 'use-context-selector' -import Progress from './progress' import Button from '@/app/components/base/button' import { LinkExternal02, XClose } from '@/app/components/base/icons/src/vender/line/general' import { IS_CE_EDITION } from '@/config' import { useProviderContext } from '@/context/provider-context' -import { formatNumber } from '@/utils/format' -import I18n from '@/context/i18n' -import ProviderConfig from '@/app/components/header/account-setting/model-page/configs' import { useModalContext } from '@/context/modal-context' const APIKeyInfoPanel: FC = () => { const isCloud = !IS_CE_EDITION - const { locale } = useContext(I18n) - const { textGenerationModelList } = useProviderContext() + const { hasSettedApiKey } = useProviderContext() const { setShowAccountSettingModal } = useModalContext() const { t } = useTranslation() const [isShow, setIsShow] = useState(true) - const hasSetAPIKEY = !!textGenerationModelList?.find(({ model_provider: provider }) => { - if (provider.provider_type === 'system' && provider.quota_type === 'paid') - return true - - if (provider.provider_type === 'custom') - return true - - return false - }) - if (hasSetAPIKEY) + if (hasSettedApiKey) return null - // first show in trail and not used exhausted, else find the exhausted - const [used, total, unit, providerName] = (() => { - if (!textGenerationModelList || !isCloud) - return [0, 0, '', ''] - - let used = 0 - let total = 0 - let unit = 'times' - let trailProviderName = '' - let hasFoundNotExhausted = false - textGenerationModelList?.filter(({ model_provider: provider }) => { - return provider.quota_type === 'trial' - }).forEach(({ model_provider: provider }) => { - if (hasFoundNotExhausted) - return - const { provider_name, quota_used, quota_limit, quota_unit } = provider - if (quota_limit !== quota_used) - hasFoundNotExhausted = true - used = quota_used - total = quota_limit - unit = quota_unit - trailProviderName = provider_name - }) - - return [used, total, unit, trailProviderName] - })() - const usedPercent = Math.round(used / total * 100) - const exhausted = isCloud && usedPercent === 100 if (!(isShow)) return null return ( -
+
- {isCloud && } + {isCloud && } {isCloud ? ( -
{t(`appOverview.apiKeyInfo.cloud.${exhausted ? 'exhausted' : 'trial'}.title`, { providerName: (ProviderConfig as any)[providerName as string]?.selector?.name[locale] || providerName })}
+
{t('appOverview.apiKeyInfo.cloud.trial.title', { providerName: 'OpenAI' })}
) : (
@@ -84,18 +41,7 @@ const APIKeyInfoPanel: FC = () => { )}
{isCloud && ( -
{t(`appOverview.apiKeyInfo.cloud.${exhausted ? 'exhausted' : 'trial'}.description`)}
- )} - {/* Call times info */} - {isCloud && ( -
-
-
{t(`appOverview.apiKeyInfo.${unit === 'times' ? 'callTimes' : 'usedToken'}`)}
-
·
-
{formatNumber(used)}/{formatNumber(total)}
-
- -
+
{t(`appOverview.apiKeyInfo.cloud.${'trial'}.description`)}
)}
{ !!configedProviders?.length && ( diff --git a/web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx b/web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx index 48cbfff6fab7eb..731f1bae4c99aa 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx @@ -18,6 +18,7 @@ import { saveCredentials, validateCredentials, } from '../utils' +import ProviderIcon from '../provider-icon' import { useValidate } from '../../key-validator/hooks' import { ValidatedStatus } from '../../key-validator/declarations' import Form from './Form' @@ -177,7 +178,7 @@ const ModelModal: FC = ({
{renderTitlePrefix()}
-
+
= ({ `} >
-
- +
+
= ({ + feature, +}) => { + if (feature === ModelFeatureEnum.agentThought) { + return ( + + + + ) + } + + if (feature === ModelFeatureEnum.toolCall) { + return ( + + + + ) + } + + if (feature === ModelFeatureEnum.multiToolCall) { + return ( + + + + ) + } + + if (feature === ModelFeatureEnum.vision) { + return ( + + + + ) + } + + return null +} + +export default FeatureIcon diff --git a/web/app/components/header/account-setting/model-provider-page/model-selector/index.tsx b/web/app/components/header/account-setting/model-provider-page/model-selector/index.tsx index ae4ab27e358652..6dd5160bcc3395 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-selector/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-selector/index.tsx @@ -1,13 +1,37 @@ +import type { FC } from 'react' import { useState } from 'react' +import type { + Model, + ModelItem, +} from '../declarations' +import ModelTrigger from './model-trigger' +import EmptyTrigger from './empty-trigger' +import Popup from './popup' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' -const ModelSelector = () => { +type ModelSelectorProps = { + defaultModel?: ModelItem + modelList: Model[] + popupClassName?: string + onSelect: (model: ModelItem) => void +} +const ModelSelector: FC = ({ + defaultModel, + modelList, + popupClassName, + onSelect, +}) => { const [open, setOpen] = useState(false) + const handleSelect = (model: ModelItem) => { + setOpen(false) + onSelect(model) + } + return ( {
setOpen(v => !v)} + className='block' > + { + defaultModel && ( + + ) + } + { + !defaultModel && ( + + ) + } - + +
diff --git a/web/app/components/header/account-setting/model-provider-page/model-selector/model-trigger.tsx b/web/app/components/header/account-setting/model-provider-page/model-selector/model-trigger.tsx index 12c363dba84679..b3d794337cdceb 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-selector/model-trigger.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-selector/model-trigger.tsx @@ -5,6 +5,7 @@ import { languageMaps, } from '../utils' import ModelBadge from '../model-badge' +import FeatureIcon from './feature-icon' import I18n from '@/context/i18n' // import { AlertTriangle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback' import { ChevronDown } from '@/app/components/base/icons/src/vender/line/arrows' @@ -23,25 +24,39 @@ const ModelTrigger: FC = ({ return (
{model.label[language]}
- { - model.model_properties.mode && ( - - {(model.model_properties.mode as string).toLocaleUpperCase()} - - ) - } +
void +} +const PopupItem: FC = ({ + defaultModel, + model, + onSelect, +}) => { + const { locale } = useContext(I18n) + const language = languageMaps[locale] + + const handleSelect = (modelItem: ModelItem) => { + if (modelItem.status !== ModelStatusEnum.active) + return + + onSelect(modelItem) + } + + return ( +
+
+ {model.label[language]} +
+ { + model.models.map(modelItem => ( +
handleSelect(modelItem)} + > +
+
+ {modelItem.label[language]} +
+ +
+ { + defaultModel?.model === modelItem.model && ( + + ) + } + { + modelItem.status === ModelStatusEnum.noConfigure && ( +
+ ADD +
+ ) + } +
+ )) + } +
+ ) +} + +export default PopupItem diff --git a/web/app/components/header/account-setting/model-provider-page/model-selector/popup.tsx b/web/app/components/header/account-setting/model-provider-page/model-selector/popup.tsx index 566a0056aea755..69031545c92435 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-selector/popup.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-selector/popup.tsx @@ -1,6 +1,81 @@ -const Popup = () => { +import type { FC } from 'react' +import { useContext } from 'use-context-selector' +import { useState } from 'react' +import type { + Model, + ModelItem, +} from '../declarations' +import { languageMaps } from '../utils' +import PopupItem from './popup-item' +import I18n from '@/context/i18n' +import { SearchLg } from '@/app/components/base/icons/src/vender/line/general' +import { XCircle } from '@/app/components/base/icons/src/vender/solid/general' + +type PopupProps = { + defaultModel?: ModelItem + modelList: Model[] + onSelect: (model: ModelItem) => void +} +const Popup: FC = ({ + defaultModel, + modelList, + onSelect, +}) => { + const { locale } = useContext(I18n) + const language = languageMaps[locale] + const [searchText, setSearchText] = useState('') + + const filteredModelList = modelList.filter(model => model.models.filter(modelItem => modelItem.label[language].includes(searchText)).length) + return ( -
+
+
+
+ + setSearchText(e.target.value)} + /> + { + searchText && ( + setSearchText('')} + /> + ) + } +
+
+
+ { + filteredModelList.map(model => ( + + )) + } + { + !filteredModelList.length && ( +
+ {`No model found for “${searchText}”`} +
+ ) + } +
+
) } diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/credential-panel.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/credential-panel.tsx index ac79cc5c03b9b4..6dfcc8e966a723 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/credential-panel.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/credential-panel.tsx @@ -2,7 +2,7 @@ import type { FC } from 'react' import { useTranslation } from 'react-i18next' import type { ModelProvider } from '../declarations' import { - CustomConfigurationEnum, + CustomConfigurationStatusEnum, PreferredProviderTypeEnum, } from '../declarations' import PrioritySelector from './priority-selector' @@ -30,7 +30,7 @@ const CredentialPanel: FC = ({
API-KEY - +
+ +
+
+ + + ) +} + +export default SystemModel diff --git a/web/context/provider-context.tsx b/web/context/provider-context.tsx index 56870c491bcc25..d8a0ff9c143d96 100644 --- a/web/context/provider-context.tsx +++ b/web/context/provider-context.tsx @@ -5,9 +5,14 @@ import useSWR from 'swr' import { useEffect, useState } from 'react' import { fetchDefaultModal, fetchModelList, fetchSupportRetrievalMethods } from '@/service/common' import { ModelType } from '@/app/components/header/account-setting/model-page/declarations' -import type { BackendModel } from '@/app/components/header/account-setting/model-page/declarations' -import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' -import type { Model } from '@/app/components/header/account-setting/model-provider-page/declarations' +import { + ModelStatusEnum, + ModelTypeEnum, +} from '@/app/components/header/account-setting/model-provider-page/declarations' +import type { + DefaultModel, + Model, +} from '@/app/components/header/account-setting/model-provider-page/declarations' import type { RETRIEVE_METHOD } from '@/types/app' import { Plan, type UsagePlanInfo } from '@/app/components/billing/type' import { fetchCurrentPlanInfo } from '@/service/billing' @@ -21,17 +26,18 @@ const ProviderContext = createContext<{ rerankModelList: Model[] agentThoughtModelList: Model[] updateModelList: (type: ModelType) => void - textGenerationDefaultModel?: BackendModel + textGenerationDefaultModel?: DefaultModel mutateTextGenerationDefaultModel: () => void - embeddingsDefaultModel?: BackendModel + embeddingsDefaultModel?: DefaultModel isEmbeddingsDefaultModelValid: boolean mutateEmbeddingsDefaultModel: () => void - speech2textDefaultModel?: BackendModel + speech2textDefaultModel?: DefaultModel mutateSpeech2textDefaultModel: () => void - rerankDefaultModel?: BackendModel + rerankDefaultModel?: DefaultModel isRerankDefaultModelVaild: boolean mutateRerankDefaultModel: () => void supportRetrievalMethods: RETRIEVE_METHOD[] + hasSettedApiKey: boolean plan: { type: Plan usage: UsagePlanInfo @@ -58,6 +64,7 @@ const ProviderContext = createContext<{ isRerankDefaultModelVaild: false, mutateRerankDefaultModel: () => { }, supportRetrievalMethods: [], + hasSettedApiKey: false, plan: { type: Plan.sandbox, usage: { @@ -150,22 +157,23 @@ export const ProviderContextProvider = ({ return ( model.status === ModelStatusEnum.active), supportRetrievalMethods: supportRetrievalMethods?.retrieval_method || [], plan, isFetchedPlan, diff --git a/web/service/common.ts b/web/service/common.ts index cb7209e1cfbaa9..654d7edf927136 100644 --- a/web/service/common.ts +++ b/web/service/common.ts @@ -25,8 +25,8 @@ import type { UpdateOpenAIKeyResponse, ValidateOpenAIKeyResponse, } from '@/models/app' -import type { BackendModel } from '@/app/components/header/account-setting/model-page/declarations' import type { + DefaultModel, Model, ModelItem, ModelProvider, @@ -195,8 +195,8 @@ export const getPayUrl: Fetcher<{ url: string }, string> = (url) => { return get<{ url: string }>(url) } -export const fetchDefaultModal: Fetcher = (url) => { - return get(url) +export const fetchDefaultModal: Fetcher<{ data: DefaultModel }, string> = (url) => { + return get<{ data: DefaultModel }>(url) } export const updateDefaultModel: Fetcher = ({ url, body }) => { From 1c154e0e4b75632ebb9451d1a7e0ff33f0cc7f1e Mon Sep 17 00:00:00 2001 From: StyleZhang Date: Fri, 22 Dec 2023 11:48:35 +0800 Subject: [PATCH 07/46] feat: model provider --- .../app/configuration/config-model/index.tsx | 72 +++++---- .../configuration/config-model/model-icon.tsx | 32 ---- web/app/components/app/log/list.tsx | 5 +- .../config-view/summary/index.tsx | 5 +- .../model-page/model-selector/index.tsx | 2 +- .../model-selector/portal-select.tsx | 5 +- .../model-provider-page/declarations.ts | 9 +- .../model-provider-page/hooks.ts | 26 +++- .../model-provider-page/model-icon/index.tsx | 142 ++++++++++++++++++ .../model-provider-page/model-modal/Form.tsx | 7 +- .../model-provider-page/model-modal/index.tsx | 7 +- .../model-provider-page/model-name/index.tsx | 75 +++++++++ .../model-selector/index.tsx | 18 ++- .../model-selector/model-trigger.tsx | 55 ++----- .../model-selector/popup-item.tsx | 64 +++----- .../model-selector/popup.tsx | 12 +- .../provider-added-card/index.tsx | 5 - .../provider-added-card/model-list.tsx | 51 ++----- .../provider-card/index.tsx | 7 +- .../provider-icon/index.tsx | 7 +- web/context/provider-context.tsx | 10 +- web/service/common.ts | 6 +- 22 files changed, 377 insertions(+), 245 deletions(-) delete mode 100644 web/app/components/app/configuration/config-model/model-icon.tsx create mode 100644 web/app/components/header/account-setting/model-provider-page/model-icon/index.tsx create mode 100644 web/app/components/header/account-setting/model-provider-page/model-name/index.tsx diff --git a/web/app/components/app/configuration/config-model/index.tsx b/web/app/components/app/configuration/config-model/index.tsx index 1e17edd1a6eced..6e0901fda6c455 100644 --- a/web/app/components/app/configuration/config-model/index.tsx +++ b/web/app/components/app/configuration/config-model/index.tsx @@ -7,9 +7,6 @@ import { useBoolean, useClickAway, useGetState } from 'ahooks' import { InformationCircleIcon } from '@heroicons/react/24/outline' import produce from 'immer' import ParamItem from './param-item' -import ModelIcon from './model-icon' -import ModelName from './model-name' -import ModelModeTypeLabel from './model-mode-type-label' import { SlidersH } from '@/app/components/base/icons/src/vender/line/mediaAndDevices' import Radio from '@/app/components/base/radio' import Panel from '@/app/components/base/panel' @@ -24,11 +21,14 @@ import { Target04 } from '@/app/components/base/icons/src/vender/solid/general' import { Sliders02 } from '@/app/components/base/icons/src/vender/solid/mediaAndDevices' import { fetchModelParams } from '@/service/debug' import Loading from '@/app/components/base/loading' -import ModelSelector from '@/app/components/header/account-setting/model-page/model-selector' -import { ModelType, ProviderEnum } from '@/app/components/header/account-setting/model-page/declarations' import { useProviderContext } from '@/context/provider-context' import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' import type { ModelModeType } from '@/types/app' +import ModelIcon from '@/app/components/header/account-setting/model-provider-page/model-icon' +import ModelName from '@/app/components/header/account-setting/model-provider-page/model-name' +import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' +import ModelSelector from '@/app/components/header/account-setting/model-provider-page/model-selector' + export type IConfigModelProps = { isAdvancedMode: boolean mode: string @@ -54,7 +54,8 @@ const ConfigModel: FC = ({ const [isShowConfig, { setFalse: hideConfig, toggle: toogleShowConfig }] = useBoolean(false) const [maxTokenSettingTipVisible, setMaxTokenSettingTipVisible] = useState(false) const configContentRef = React.useRef(null) - const currModel = textGenerationModelList.find(item => item.model_name === modelId) + const currentProvider = textGenerationModelList.find(model => model.provider === provider) + const currModel = currentProvider?.models.find(modelItem => modelItem.model === modelId) const media = useBreakpoints() const isMobile = media === MediaType.mobile @@ -81,7 +82,7 @@ const ConfigModel: FC = ({ setAllParams(newAllParams) } })() - }, [provider, modelId]) + }, [provider, modelId, allParams, setAllParams]) useClickAway(() => { hideConfig() @@ -269,26 +270,35 @@ const ConfigModel: FC = ({ const max = currParams.max_tokens.max const isSupportMaxToken = currParams.max_tokens.enabled - if (isSupportMaxToken && currModel?.model_provider.provider_name !== 'anthropic' && completionParams.max_tokens > max * 2 / 3) + if (isSupportMaxToken && currentProvider?.provider !== 'anthropic' && completionParams.max_tokens > max * 2 / 3) setMaxTokenSettingTipVisible(true) else setMaxTokenSettingTipVisible(false) - }, [currParams, completionParams.max_tokens, setMaxTokenSettingTipVisible]) + }, [currParams, completionParams.max_tokens, setMaxTokenSettingTipVisible, currentProvider]) return (
!disabled && toogleShowConfig()} > - -
- -
- {isAdvancedMode && } + { + currentProvider && ( + + ) + } + { + currModel && ( + + ) + } {disabled ? : }
{isShowConfig && ( @@ -312,23 +322,19 @@ const ConfigModel: FC = ({
{t('appDebug.modelConfig.model')}
{ + defaultModel={{ model: modelId, provider }} + modelList={textGenerationModelList} + onSelect={({ provider, model }) => { + const targetProvider = textGenerationModelList.find(modelItem => modelItem.provider === provider) + const targetModelItem = targetProvider?.models.find(modelItem => modelItem.model === model) handleSelectModel({ - id: model.model_name, - provider: model.model_provider.provider_name as ProviderEnum, - mode: model.model_mode, - features: model.features, + id: model, + provider, + mode: targetModelItem?.model_properties.mode as ModelModeType, + features: targetModelItem?.features || [], })() }} + popupClassName='z-[60]' />
{hasEnableParams && ( @@ -336,7 +342,7 @@ const ConfigModel: FC = ({ )} {/* Tone type */} - {[ProviderEnum.openai, ProviderEnum.azure_openai].includes(provider) && ( + {['openai', 'azure_openai'].includes(provider) && (
{t('appDebug.modelConfig.setTone')}
diff --git a/web/app/components/app/configuration/config-model/model-icon.tsx b/web/app/components/app/configuration/config-model/model-icon.tsx deleted file mode 100644 index 108ceca59d0860..00000000000000 --- a/web/app/components/app/configuration/config-model/model-icon.tsx +++ /dev/null @@ -1,32 +0,0 @@ -'use client' -import type { FC } from 'react' -import React from 'react' -import cn from 'classnames' -import { - OpenaiGreen, - OpenaiViolet, -} from '@/app/components/base/icons/src/public/llm' -import { ProviderEnum } from '@/app/components/header/account-setting/model-page/declarations' -import ProviderConfig from '@/app/components/header/account-setting/model-page/configs' - -export type IModelIconProps = { - modelId: string - providerName: ProviderEnum - className?: string -} - -const ModelIcon: FC = ({ modelId, providerName, className }) => { - let Icon = - if (providerName === ProviderEnum.openai) - Icon = modelId.includes('gpt-4') ? : - else - Icon = ProviderConfig[providerName]?.selector.icon - - return ( -
- {Icon} -
- ) -} - -export default React.memo(ModelIcon) diff --git a/web/app/components/app/log/list.tsx b/web/app/components/app/log/list.tsx index 1486aed7e47908..ab292ece6fb1d6 100644 --- a/web/app/components/app/log/list.tsx +++ b/web/app/components/app/log/list.tsx @@ -30,7 +30,8 @@ import Tooltip from '@/app/components/base/tooltip' import { ToastContext } from '@/app/components/base/toast' import { fetchChatConversationDetail, fetchChatMessages, fetchCompletionConversationDetail, updateLogMessageAnnotations, updateLogMessageFeedbacks } from '@/service/log' import { TONE_LIST } from '@/config' -import ModelIcon from '@/app/components/app/configuration/config-model/model-icon' +import ModelIcon from '@/app/components/header/account-setting/model-provider-page/model-icon' +import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' import ModelName from '@/app/components/app/configuration/config-model/model-name' import ModelModeTypeLabel from '@/app/components/app/configuration/config-model/model-mode-type-label' import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' @@ -226,7 +227,7 @@ function DetailPanel
diff --git a/web/app/components/explore/universal-chat/config-view/summary/index.tsx b/web/app/components/explore/universal-chat/config-view/summary/index.tsx index 124903c6184f2e..5a54cd3bae3def 100644 --- a/web/app/components/explore/universal-chat/config-view/summary/index.tsx +++ b/web/app/components/explore/universal-chat/config-view/summary/index.tsx @@ -4,7 +4,8 @@ import React from 'react' import cn from 'classnames' import { useBoolean, useClickAway } from 'ahooks' import s from './style.module.css' -import ModelIcon from '@/app/components/app/configuration/config-model/model-icon' +import ModelIcon from '@/app/components/header/account-setting/model-provider-page/model-icon' +import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' import { Google, WebReader, Wikipedia } from '@/app/components/base/icons/src/public/plugins' import ConfigDetail from '@/app/components/explore/universal-chat/config-view/detail' import type { ProviderEnum } from '@/app/components/header/account-setting/model-page/declarations' @@ -63,7 +64,7 @@ const Summary: FC = ({ return (
- +
{ pluginIds.length > 0 && ( diff --git a/web/app/components/header/account-setting/model-page/model-selector/index.tsx b/web/app/components/header/account-setting/model-page/model-selector/index.tsx index 69b46e0c7b1f3b..9fb570f80fef00 100644 --- a/web/app/components/header/account-setting/model-page/model-selector/index.tsx +++ b/web/app/components/header/account-setting/model-page/model-selector/index.tsx @@ -15,7 +15,7 @@ import { Check, LinkExternal01, SearchLg } from '@/app/components/base/icons/src import { XCircle } from '@/app/components/base/icons/src/vender/solid/general' import { AlertCircle } from '@/app/components/base/icons/src/vender/line/alertsAndFeedback' import Tooltip from '@/app/components/base/tooltip' -import ModelIcon from '@/app/components/app/configuration/config-model/model-icon' +import ModelIcon from '@/app/components/header/account-setting/model-provider-page/model-icon' import ModelName from '@/app/components/app/configuration/config-model/model-name' import ProviderName from '@/app/components/app/configuration/config-model/provider-name' import { useProviderContext } from '@/context/provider-context' diff --git a/web/app/components/header/account-setting/model-page/model-selector/portal-select.tsx b/web/app/components/header/account-setting/model-page/model-selector/portal-select.tsx index 19ad97c3996651..3e99c4928d4263 100644 --- a/web/app/components/header/account-setting/model-page/model-selector/portal-select.tsx +++ b/web/app/components/header/account-setting/model-page/model-selector/portal-select.tsx @@ -14,7 +14,8 @@ import { Check, LinkExternal01, SearchLg } from '@/app/components/base/icons/src import { XCircle } from '@/app/components/base/icons/src/vender/solid/general' import { AlertCircle } from '@/app/components/base/icons/src/vender/line/alertsAndFeedback' import Tooltip from '@/app/components/base/tooltip' -import ModelIcon from '@/app/components/app/configuration/config-model/model-icon' +import ModelIcon from '@/app/components/header/account-setting/model-provider-page/model-icon' +import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' import ModelName from '@/app/components/app/configuration/config-model/model-name' import ProviderName from '@/app/components/app/configuration/config-model/provider-name' import { useProviderContext } from '@/context/provider-context' @@ -196,8 +197,8 @@ const ModelSelector: FC = ({ <>
diff --git a/web/app/components/header/account-setting/model-provider-page/declarations.ts b/web/app/components/header/account-setting/model-provider-page/declarations.ts index 739d7dde6e4a8d..b6563a11b07432 100644 --- a/web/app/components/header/account-setting/model-provider-page/declarations.ts +++ b/web/app/components/header/account-setting/model-provider-page/declarations.ts @@ -149,10 +149,17 @@ export type Model = { status: ModelStatusEnum } -export type DefaultModel = { +export type DefaultModelResponse = { model: string model_type: ModelTypeEnum provider: { provider: string + icon_large: TypeWithI18N + icon_small: TypeWithI18N } } + +export type DefaultModel = { + provider: string + model: string +} diff --git a/web/app/components/header/account-setting/model-provider-page/hooks.ts b/web/app/components/header/account-setting/model-provider-page/hooks.ts index 96776aea952b23..a8739949466ba8 100644 --- a/web/app/components/header/account-setting/model-provider-page/hooks.ts +++ b/web/app/components/header/account-setting/model-provider-page/hooks.ts @@ -4,27 +4,35 @@ import { useMemo, useState, } from 'react' +import { useContext } from 'use-context-selector' import type { DefaultModel, + DefaultModelResponse, Model, - ModelItem, } from './declarations' +import { languageMaps } from './utils' +import I18n from '@/context/i18n' type UseDefaultModelAndModelList = ( - defaultModel: DefaultModel | undefined, + defaultModel: DefaultModelResponse | undefined, modelList: Model[], -) => [ModelItem | undefined, (model: ModelItem) => void] +) => [DefaultModel | undefined, (model: DefaultModel) => void] export const useDefaultModelAndModelList: UseDefaultModelAndModelList = ( defaultModel, modelList, ) => { const currentDefaultModel = useMemo(() => { - const currentDefaultModel: ModelItem | undefined = modelList.find(provider => provider.provider === defaultModel?.provider.provider)?.models.find(model => model.model === defaultModel?.model) + const currentProvider = modelList.find(provider => provider.provider === defaultModel?.provider.provider) + const currentModel = currentProvider?.models.find(model => model.model === defaultModel?.model) + const currentDefaultModel = currentProvider && currentModel && { + model: currentModel.model, + provider: currentProvider.provider, + } return currentDefaultModel }, [defaultModel, modelList]) - const [defaultModelState, setDefaultModelState] = useState(currentDefaultModel) - const handleDefaultModelChange = useCallback((model: ModelItem) => { + const [defaultModelState, setDefaultModelState] = useState(currentDefaultModel) + const handleDefaultModelChange = useCallback((model: DefaultModel) => { setDefaultModelState(model) }, []) useEffect(() => { @@ -33,3 +41,9 @@ export const useDefaultModelAndModelList: UseDefaultModelAndModelList = ( return [defaultModelState, handleDefaultModelChange] } + +export const useLanguage = () => { + const { locale } = useContext(I18n) + + return languageMaps[locale] +} diff --git a/web/app/components/header/account-setting/model-provider-page/model-icon/index.tsx b/web/app/components/header/account-setting/model-provider-page/model-icon/index.tsx new file mode 100644 index 00000000000000..30638b4217959f --- /dev/null +++ b/web/app/components/header/account-setting/model-provider-page/model-icon/index.tsx @@ -0,0 +1,142 @@ +import type { FC } from 'react' +import type { Model } from '../declarations' +import { ModelTypeEnum } from '../declarations' +import { useLanguage } from '../hooks' +import { useProviderContext } from '@/context/provider-context' + +type ModelIconBaseProps = { + provider?: Model + className?: string +} +const ModelIconBase: FC = ({ + provider, + className, +}) => { + const language = useLanguage() + + if (provider?.icon_small) { + return ( + model-icon + ) + } + + return ( +
+
+
+ ) +} + +type ModelIconSubProps = { + providerName: string + className?: string +} +export const ModelIconTextGeneration: FC = ({ + providerName, + className, +}) => { + const { textGenerationModelList } = useProviderContext() + const provider = textGenerationModelList.find(item => item.provider === providerName) + return ( + + ) +} + +export const ModelIconTextEmbedding: FC = ({ + providerName, + className, +}) => { + const { embeddingsModelList } = useProviderContext() + const provider = embeddingsModelList.find(item => item.provider === providerName) + return ( + + ) +} + +export const ModelIconRerank: FC = ({ + providerName, + className, +}) => { + const { rerankModelList } = useProviderContext() + const provider = rerankModelList.find(item => item.provider === providerName) + return ( + + ) +} + +export const ModelIconSpeechToText: FC = ({ + providerName, + className, +}) => { + const { speech2textModelList } = useProviderContext() + const provider = speech2textModelList.find(item => item.provider === providerName) + return ( + + ) +} + +type ModelIconProps = { + modelType: ModelTypeEnum +} & ModelIconSubProps +const ModelIcon: FC = ({ + modelType, + providerName, + className, +}) => { + if (modelType === ModelTypeEnum.textGeneration) { + return ( + + ) + } + if (modelType === ModelTypeEnum.textEmbedding) { + return ( + + ) + } + if (modelType === ModelTypeEnum.rerank) { + return ( + + ) + } + if (modelType === ModelTypeEnum.speech2text) { + return ( + + ) + } + + return ( + + ) +} + +export default ModelIcon diff --git a/web/app/components/header/account-setting/model-provider-page/model-modal/Form.tsx b/web/app/components/header/account-setting/model-provider-page/model-modal/Form.tsx index 6a3e8c21c0d9d1..f9084bebcb33c5 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-modal/Form.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-modal/Form.tsx @@ -1,6 +1,5 @@ import { useState } from 'react' import type { FC } from 'react' -import { useContext } from 'use-context-selector' import { ValidatingTip } from '../../key-validator/ValidateStatus' import type { CredentialFormSchema, @@ -11,9 +10,8 @@ import type { FormValue, } from '../declarations' import { FormTypeEnum } from '../declarations' -import { languageMaps } from '../utils' +import { useLanguage } from '../hooks' import Input from './Input' -import I18n from '@/context/i18n' import { SimpleSelect } from '@/app/components/base/select' type FormProps = { @@ -31,8 +29,7 @@ const Form: FC = ({ validating, validatedSuccess, }) => { - const { locale } = useContext(I18n) - const language = languageMaps[locale] + const language = useLanguage() const [changeKey, setChangeKey] = useState('') const handleFormChange = (key: string, val: string) => { diff --git a/web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx b/web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx index 731f1bae4c99aa..6b7acd4b31af45 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx @@ -2,7 +2,6 @@ import type { FC } from 'react' import { useEffect, useMemo, useState } from 'react' import useSWR from 'swr' import { useTranslation } from 'react-i18next' -import { useContext } from 'use-context-selector' import type { CredentialFormSchema, FormValue, @@ -13,16 +12,15 @@ import { FormTypeEnum, } from '../declarations' import { - languageMaps, removeCredentials, saveCredentials, validateCredentials, } from '../utils' +import { useLanguage } from '../hooks' import ProviderIcon from '../provider-icon' import { useValidate } from '../../key-validator/hooks' import { ValidatedStatus } from '../../key-validator/declarations' import Form from './Form' -import I18n from '@/context/i18n' import Button from '@/app/components/base/button' import { Lock01 } from '@/app/components/base/icons/src/vender/solid/security' import { LinkExternal02 } from '@/app/components/base/icons/src/vender/line/general' @@ -51,8 +49,7 @@ const ModelModal: FC = ({ }) => { const { t } = useTranslation() const { notify } = useToastContext() - const { locale } = useContext(I18n) - const language = languageMaps[locale] + const language = useLanguage() const [loading, setLoading] = useState(false) const [showConfirm, setShowConfirm] = useState(false) const { data: formSchemasValue, isLoading } = useSWR(`/workspaces/current/model-providers/${provider.provider}/credentials`, fetchModelProviderCredentials) diff --git a/web/app/components/header/account-setting/model-provider-page/model-name/index.tsx b/web/app/components/header/account-setting/model-provider-page/model-name/index.tsx new file mode 100644 index 00000000000000..e20449702ae8d7 --- /dev/null +++ b/web/app/components/header/account-setting/model-provider-page/model-name/index.tsx @@ -0,0 +1,75 @@ +import type { FC } from 'react' +import { + modelTypeFormat, + sizeFormat, +} from '../utils' +import { useLanguage } from '../hooks' +import type { ModelItem } from '../declarations' +import ModelBadge from '../model-badge' +import FeatureIcon from '../model-selector/feature-icon' + +type ModelNameProps = { + modelItem: ModelItem + className?: string + showModelType?: boolean + showMode?: boolean + showFeatures?: boolean + showContextSize?: boolean +} +const ModelName: FC = ({ + modelItem, + className, + showModelType, + showMode, + showFeatures, + showContextSize, +}) => { + const language = useLanguage() + + return ( +
+
+ {modelItem.label[language]} +
+ { + showModelType && ( + + {modelTypeFormat(modelItem.model_type)} + + ) + } + { + modelItem.model_properties.mode && showMode && ( + + {(modelItem.model_properties.mode as string).toLocaleUpperCase()} + + ) + } + { + showFeatures && modelItem.features?.map(feature => ( + + )) + } + { + showContextSize && modelItem.model_properties.context_size && ( + + {sizeFormat(modelItem.model_properties.context_size as number)} + + ) + } +
+ ) +} + +export default ModelName diff --git a/web/app/components/header/account-setting/model-provider-page/model-selector/index.tsx b/web/app/components/header/account-setting/model-provider-page/model-selector/index.tsx index 6dd5160bcc3395..272159ed4d1530 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-selector/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-selector/index.tsx @@ -1,6 +1,7 @@ import type { FC } from 'react' import { useState } from 'react' import type { + DefaultModel, Model, ModelItem, } from '../declarations' @@ -14,10 +15,10 @@ import { } from '@/app/components/base/portal-to-follow-elem' type ModelSelectorProps = { - defaultModel?: ModelItem + defaultModel?: DefaultModel modelList: Model[] popupClassName?: string - onSelect: (model: ModelItem) => void + onSelect: (model: DefaultModel) => void } const ModelSelector: FC = ({ defaultModel, @@ -26,10 +27,12 @@ const ModelSelector: FC = ({ onSelect, }) => { const [open, setOpen] = useState(false) + const currentProvider = modelList.find(model => model.provider === defaultModel?.provider) + const currentModel = currentProvider?.models.find(model => model.model === defaultModel?.model) - const handleSelect = (model: ModelItem) => { + const handleSelect = (provider: string, model: ModelItem) => { setOpen(false) - onSelect(model) + onSelect({ provider, model: model.model }) } return ( @@ -45,15 +48,16 @@ const ModelSelector: FC = ({ className='block' > { - defaultModel && ( + currentModel && currentProvider && ( ) } { - !defaultModel && ( + !currentModel && ( diff --git a/web/app/components/header/account-setting/model-provider-page/model-selector/model-trigger.tsx b/web/app/components/header/account-setting/model-provider-page/model-selector/model-trigger.tsx index b3d794337cdceb..d1ddcb8e1e06c2 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-selector/model-trigger.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-selector/model-trigger.tsx @@ -1,26 +1,20 @@ import type { FC } from 'react' -import { useContext } from 'use-context-selector' import type { ModelItem } from '../declarations' -import { - languageMaps, -} from '../utils' -import ModelBadge from '../model-badge' -import FeatureIcon from './feature-icon' -import I18n from '@/context/i18n' +import ModelIcon from '../model-icon' +import ModelName from '../model-name' // import { AlertTriangle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback' import { ChevronDown } from '@/app/components/base/icons/src/vender/line/arrows' type ModelTriggerProps = { open: boolean + provider: string model: ModelItem } const ModelTrigger: FC = ({ open, + provider, model, }) => { - const { locale } = useContext(I18n) - const language = languageMaps[locale] - return (
= ({ ${open && '!bg-gray-200'} `} > -
-
-
- {model.label[language]} -
- -
+ +
void + onSelect: (provider: string, model: ModelItem) => void } const PopupItem: FC = ({ defaultModel, model, onSelect, }) => { - const { locale } = useContext(I18n) - const language = languageMaps[locale] - - const handleSelect = (modelItem: ModelItem) => { + const language = useLanguage() + const handleSelect = (provider: string, modelItem: ModelItem) => { if (modelItem.status !== ModelStatusEnum.active) return - onSelect(modelItem) + onSelect(provider, modelItem) } return ( @@ -44,46 +41,25 @@ const PopupItem: FC = ({ group flex items-center px-3 py-1.5 h-8 rounded-lg ${modelItem.status === ModelStatusEnum.active ? 'cursor-pointer hover:bg-gray-50' : 'cursor-not-allowed hover:bg-gray-50/60'} `} - onClick={() => handleSelect(modelItem)} + onClick={() => handleSelect(model.provider, modelItem)} > -
-
- {modelItem.label[language]} -
-
+
-
+ modelItem={modelItem} + showMode + showFeatures + /> { defaultModel?.model === modelItem.model && ( diff --git a/web/app/components/header/account-setting/model-provider-page/model-selector/popup.tsx b/web/app/components/header/account-setting/model-provider-page/model-selector/popup.tsx index 69031545c92435..3cb420c47804dc 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-selector/popup.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-selector/popup.tsx @@ -1,28 +1,26 @@ import type { FC } from 'react' -import { useContext } from 'use-context-selector' import { useState } from 'react' import type { + DefaultModel, Model, ModelItem, } from '../declarations' -import { languageMaps } from '../utils' +import { useLanguage } from '../hooks' import PopupItem from './popup-item' -import I18n from '@/context/i18n' import { SearchLg } from '@/app/components/base/icons/src/vender/line/general' import { XCircle } from '@/app/components/base/icons/src/vender/solid/general' type PopupProps = { - defaultModel?: ModelItem + defaultModel?: DefaultModel modelList: Model[] - onSelect: (model: ModelItem) => void + onSelect: (provider: string, model: ModelItem) => void } const Popup: FC = ({ defaultModel, modelList, onSelect, }) => { - const { locale } = useContext(I18n) - const language = languageMaps[locale] + const language = useLanguage() const [searchText, setSearchText] = useState('') const filteredModelList = modelList.filter(model => model.models.filter(modelItem => modelItem.label[language].includes(searchText)).length) diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/index.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/index.tsx index 1e02663a3ccf98..91b85ea416c546 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/index.tsx @@ -1,6 +1,5 @@ import type { FC } from 'react' import { useState } from 'react' -import { useContext } from 'use-context-selector' import type { ModelItem, ModelProvider, @@ -8,7 +7,6 @@ import type { import { ConfigurateMethodEnum } from '../declarations' import { DEFAULT_BACKGROUND_COLOR, - languageMaps, modelTypeFormat, } from '../utils' import ProviderIcon from '../provider-icon' @@ -17,7 +15,6 @@ import CredentialPanel from './credential-panel' import QuotaPanel from './quota-panel' import ModelList from './model-list' import AddModelButton from './add-model-button' -import I18n from '@/context/i18n' import { ChevronDownDouble } from '@/app/components/base/icons/src/vender/line/arrows' import { Loading02 } from '@/app/components/base/icons/src/vender/line/general' import { fetchModelProviderModelList } from '@/service/common' @@ -30,8 +27,6 @@ const ProviderAddedCard: FC = ({ provider, onOpenModal, }) => { - const { locale } = useContext(I18n) - const language = languageMaps[locale] const [fetched, setFetched] = useState(false) const [loading, setLoading] = useState(false) const [collapsed, setCollapsed] = useState(true) diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-list.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-list.tsx index 73ac096f8a88cc..d5fa59c0179345 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-list.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-list.tsx @@ -1,23 +1,18 @@ import type { FC } from 'react' -import { useContext } from 'use-context-selector' import type { ModelItem, ModelProvider } from '../declarations' import { ConfigurateMethodEnum, ModelStatusEnum, } from '../declarations' -import { - languageMaps, - modelTypeFormat, - sizeFormat, -} from '../utils' -import ModelBadge from '../model-badge' +import { useLanguage } from '../hooks' +import ModelIcon from '../model-icon' +import ModelName from '../model-name' import Tab from './tab' import AddModelButton from './add-model-button' import Indicator from '@/app/components/header/indicator' import { Settings01 } from '@/app/components/base/icons/src/vender/line/general' import { ChevronDownDouble } from '@/app/components/base/icons/src/vender/line/arrows' import Button from '@/app/components/base/button' -import I18n from '@/context/i18n' type ModelListProps = { provider: ModelProvider @@ -29,8 +24,7 @@ const ModelList: FC = ({ models, onCollapse, }) => { - const { locale } = useContext(I18n) - const language = languageMaps[locale] + const language = useLanguage() const configurateMethods = provider.configurate_methods.filter(method => method !== ConfigurateMethodEnum.fetchFromRemote) const canCustomConfig = configurateMethods.includes(ConfigurateMethodEnum.customizableModel) const canSystemConfig = configurateMethods.includes(ConfigurateMethodEnum.predefinedModel) @@ -77,31 +71,18 @@ const ModelList: FC = ({ `} >
- - {model.label[language]} - -
- - {modelTypeFormat(model.model_type)} - - { - model.model_properties.mode && ( - - {(model.model_properties.mode as string).toLocaleUpperCase()} - - ) - } - { - model.model_properties.context_size && ( - - {sizeFormat(model.model_properties.context_size as number)} - - ) - } -
+ +
{ canCustomConfig && ( diff --git a/web/app/components/header/account-setting/model-provider-page/provider-card/index.tsx b/web/app/components/header/account-setting/model-provider-page/provider-card/index.tsx index 8fcff946a95268..56e77c876cd569 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-card/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-card/index.tsx @@ -1,16 +1,14 @@ import type { FC } from 'react' import { useTranslation } from 'react-i18next' -import { useContext } from 'use-context-selector' import type { ModelProvider } from '../declarations' import { ConfigurateMethodEnum } from '../declarations' import { DEFAULT_BACKGROUND_COLOR, - languageMaps, modelTypeFormat, } from '../utils' +import { useLanguage } from '../hooks' import ModelBadge from '../model-badge' import ProviderIcon from '../provider-icon' -import I18n from '@/context/i18n' import { Plus, Settings01 } from '@/app/components/base/icons/src/vender/line/general' // import { CoinsStacked01 } from '@/app/components/base/icons/src/vender/line/financeAndECommerce' import Button from '@/app/components/base/button' @@ -24,8 +22,7 @@ const ProviderCard: FC = ({ onOpenModal, }) => { const { t } = useTranslation() - const { locale } = useContext(I18n) - const language = languageMaps[locale] + const language = useLanguage() const configurateMethods = provider.configurate_methods.filter(method => method !== ConfigurateMethodEnum.fetchFromRemote) return ( diff --git a/web/app/components/header/account-setting/model-provider-page/provider-icon/index.tsx b/web/app/components/header/account-setting/model-provider-page/provider-icon/index.tsx index e022d1ac207d93..d6c7a960b72bae 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-icon/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-icon/index.tsx @@ -1,8 +1,6 @@ import type { FC } from 'react' -import { useContext } from 'use-context-selector' import type { ModelProvider } from '../declarations' -import { languageMaps } from '../utils' -import I18n from '@/context/i18n' +import { useLanguage } from '../hooks' type ProviderIconProps = { provider: ModelProvider @@ -12,8 +10,7 @@ const ProviderIcon: FC = ({ provider, className, }) => { - const { locale } = useContext(I18n) - const language = languageMaps[locale] + const language = useLanguage() if (provider.icon_large) { return ( diff --git a/web/context/provider-context.tsx b/web/context/provider-context.tsx index d8a0ff9c143d96..c78b6bfcadb303 100644 --- a/web/context/provider-context.tsx +++ b/web/context/provider-context.tsx @@ -10,7 +10,7 @@ import { ModelTypeEnum, } from '@/app/components/header/account-setting/model-provider-page/declarations' import type { - DefaultModel, + DefaultModelResponse, Model, } from '@/app/components/header/account-setting/model-provider-page/declarations' import type { RETRIEVE_METHOD } from '@/types/app' @@ -26,14 +26,14 @@ const ProviderContext = createContext<{ rerankModelList: Model[] agentThoughtModelList: Model[] updateModelList: (type: ModelType) => void - textGenerationDefaultModel?: DefaultModel + textGenerationDefaultModel?: DefaultModelResponse mutateTextGenerationDefaultModel: () => void - embeddingsDefaultModel?: DefaultModel + embeddingsDefaultModel?: DefaultModelResponse isEmbeddingsDefaultModelValid: boolean mutateEmbeddingsDefaultModel: () => void - speech2textDefaultModel?: DefaultModel + speech2textDefaultModel?: DefaultModelResponse mutateSpeech2textDefaultModel: () => void - rerankDefaultModel?: DefaultModel + rerankDefaultModel?: DefaultModelResponse isRerankDefaultModelVaild: boolean mutateRerankDefaultModel: () => void supportRetrievalMethods: RETRIEVE_METHOD[] diff --git a/web/service/common.ts b/web/service/common.ts index 654d7edf927136..431f99f461feb1 100644 --- a/web/service/common.ts +++ b/web/service/common.ts @@ -26,7 +26,7 @@ import type { ValidateOpenAIKeyResponse, } from '@/models/app' import type { - DefaultModel, + DefaultModelResponse, Model, ModelItem, ModelProvider, @@ -195,8 +195,8 @@ export const getPayUrl: Fetcher<{ url: string }, string> = (url) => { return get<{ url: string }>(url) } -export const fetchDefaultModal: Fetcher<{ data: DefaultModel }, string> = (url) => { - return get<{ data: DefaultModel }>(url) +export const fetchDefaultModal: Fetcher<{ data: DefaultModelResponse }, string> = (url) => { + return get<{ data: DefaultModelResponse }>(url) } export const updateDefaultModel: Fetcher = ({ url, body }) => { From 2299cfa46c83ecb435946c6df999342b49e81d24 Mon Sep 17 00:00:00 2001 From: StyleZhang Date: Fri, 22 Dec 2023 18:45:47 +0800 Subject: [PATCH 08/46] feat: model provider --- .../key-validator/declarations.ts | 2 +- .../model-provider-page/declarations.ts | 11 +- .../model-provider-page/index.tsx | 10 +- .../model-provider-page/model-icon/index.tsx | 22 ++ .../model-provider-page/model-modal/Form.tsx | 60 +++- .../model-provider-page/model-modal/index.tsx | 300 +++++++++++------- .../system-model-selector/index.tsx | 25 ++ .../model-provider-page/utils.ts | 87 +++-- web/context/provider-context.tsx | 26 +- web/i18n/lang/common.en.ts | 4 + web/i18n/lang/common.zh.ts | 4 + web/service/debug.ts | 2 +- 12 files changed, 389 insertions(+), 164 deletions(-) diff --git a/web/app/components/header/account-setting/key-validator/declarations.ts b/web/app/components/header/account-setting/key-validator/declarations.ts index 718bb9e2c2c67b..7e14fa1f5e4f7c 100644 --- a/web/app/components/header/account-setting/key-validator/declarations.ts +++ b/web/app/components/header/account-setting/key-validator/declarations.ts @@ -13,7 +13,7 @@ export type ValidatedStatusState = { export type Status = 'add' | 'fail' | 'success' -export type ValidateValue = Record +export type ValidateValue = Record export type ValidateCallback = { before: (v?: ValidateValue) => boolean | undefined diff --git a/web/app/components/header/account-setting/model-provider-page/declarations.ts b/web/app/components/header/account-setting/model-provider-page/declarations.ts index b6563a11b07432..888d6d4a2201e0 100644 --- a/web/app/components/header/account-setting/model-provider-page/declarations.ts +++ b/web/app/components/header/account-setting/model-provider-page/declarations.ts @@ -1,4 +1,4 @@ -export type FormValue = Record +export type FormValue = Record export type TypeWithI18N = { 'en_US': T @@ -15,6 +15,7 @@ export enum FormTypeEnum { export type FormOption = { label: TypeWithI18N value: string + show_on: FormShowOnObject[] } export enum ModelTypeEnum { @@ -60,14 +61,14 @@ export type CredentialFormSchemaBase = { label: TypeWithI18N type: FormTypeEnum required: boolean - default: string + default?: string show_on: FormShowOnObject[] } -export type CredentialFormSchemaTextInput = CredentialFormSchemaBase & { max_length: number; placeholder: TypeWithI18N } +export type CredentialFormSchemaTextInput = CredentialFormSchemaBase & { max_length?: number; placeholder?: TypeWithI18N } export type CredentialFormSchemaSelect = CredentialFormSchemaBase & { options: FormOption[] } export type CredentialFormSchemaRadio = CredentialFormSchemaBase & { options: FormOption[] } -export type CredentialFormSchemaSecretInput = CredentialFormSchemaBase & { placeholder: TypeWithI18N } +export type CredentialFormSchemaSecretInput = CredentialFormSchemaBase & { placeholder?: TypeWithI18N } export type CredentialFormSchema = CredentialFormSchemaTextInput | CredentialFormSchemaSelect | CredentialFormSchemaRadio | CredentialFormSchemaSecretInput export type ModelItem = { @@ -78,7 +79,7 @@ export type ModelItem = { fetch_from: ConfigurateMethodEnum status: ModelStatusEnum model_properties: Record - deprecated: boolean + deprecated?: boolean } export enum PreferredProviderTypeEnum { diff --git a/web/app/components/header/account-setting/model-provider-page/index.tsx b/web/app/components/header/account-setting/model-provider-page/index.tsx index b8d1cbb00042f6..dc4bb2e2fa8611 100644 --- a/web/app/components/header/account-setting/model-provider-page/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/index.tsx @@ -21,6 +21,7 @@ const ModelProviderPage = () => { embeddingsDefaultModel, speech2textDefaultModel, rerankDefaultModel, + updateModelList, } = useProviderContext() const [currentProvider, setCurrentProvider] = useState(null) const [currentConfigurateMethod, setCurrentConfigurateMethod] = useState(null) @@ -53,6 +54,13 @@ const ModelProviderPage = () => { setCurrentConfigurateMethod(null) } + const handleSaveCrendentials = () => { + mutateProviders() + currentProvider?.supported_model_types.forEach((modelType) => { + updateModelList(modelType) + }) + } + return (
@@ -110,7 +118,7 @@ const ModelProviderPage = () => { provider={currentProvider} configurateMethod={currentConfigurateMethod} onCancel={handleCancelModelModal} - onSave={() => mutateProviders()} + onSave={handleSaveCrendentials} /> ) } diff --git a/web/app/components/header/account-setting/model-provider-page/model-icon/index.tsx b/web/app/components/header/account-setting/model-provider-page/model-icon/index.tsx index 30638b4217959f..544444710f2781 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-icon/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-icon/index.tsx @@ -91,6 +91,20 @@ export const ModelIconSpeechToText: FC = ({ ) } +export const ModelIconModeration: FC = ({ + providerName, + className, +}) => { + const { moderationModelList } = useProviderContext() + const provider = moderationModelList.find(item => item.provider === providerName) + return ( + + ) +} + type ModelIconProps = { modelType: ModelTypeEnum } & ModelIconSubProps @@ -131,6 +145,14 @@ const ModelIcon: FC = ({ /> ) } + if (modelType === ModelTypeEnum.moderation) { + return ( + + ) + } return ( } const Form: FC = ({ @@ -28,13 +29,21 @@ const Form: FC = ({ formSchemas, validating, validatedSuccess, + showOnVariableMap, }) => { const language = useLanguage() const [changeKey, setChangeKey] = useState('') const handleFormChange = (key: string, val: string) => { setChangeKey(key) - onChange({ ...value, [key]: val }) + const shouldClearVariable: Record = {} + if (showOnVariableMap[key]?.length) { + showOnVariableMap[key].forEach((clearVariable) => { + shouldClearVariable[clearVariable] = undefined + }) + } + console.log(key, val, shouldClearVariable) + onChange({ ...value, [key]: val, ...shouldClearVariable }) } const renderField = (formSchema: CredentialFormSchema) => { @@ -44,7 +53,12 @@ const Form: FC = ({ label, placeholder, required, + show_on, } = formSchema as (CredentialFormSchemaTextInput | CredentialFormSchemaSecretInput) + + if (show_on.length && !show_on.every(showOnItem => value[showOnItem.variable] === showOnItem.value)) + return null + return (
@@ -59,7 +73,7 @@ const Form: FC = ({ value={value[variable] as string} onChange={val => handleFormChange(variable, val)} validated={validatedSuccess} - placeholder={placeholder[language]} + placeholder={placeholder?.[language]} /> {validating && changeKey === variable && }
@@ -71,14 +85,31 @@ const Form: FC = ({ options, variable, label, + show_on, + required, } = formSchema as CredentialFormSchemaRadio + if (show_on.length && !show_on.every(showOnItem => value[showOnItem.variable] === showOnItem.value)) + return null + return (
-
{label[language]}
+
+ {label[language]} + { + required && ( + * + ) + } +
{ - options?.map(option => ( + options.filter((option) => { + if (option.show_on.length) + return option.show_on.every(showOnItem => value[showOnItem.variable] === showOnItem.value) + + return true + }).map(option => (
= ({ options, variable, label, + show_on, + required, } = formSchema as CredentialFormSchemaSelect + if (show_on.length && !show_on.every(showOnItem => value[showOnItem.variable] === showOnItem.value)) + return null + return (
-
{label[language]}
+
+ {label[language]} + { + required && ( + * + ) + } +
({ value: option.value, name: option.label[language] }))} + items={options.filter((option) => { + if (option.show_on.length) + return option.show_on.every(showOnItem => value[showOnItem.variable] === showOnItem.value) + + return true + }).map(option => ({ value: option.value, name: option.label[language] }))} onSelect={item => handleFormChange(variable, item.value as string)} /> {validating && changeKey === variable && } diff --git a/web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx b/web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx index 6b7acd4b31af45..7044b5b15a7781 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx @@ -4,6 +4,8 @@ import useSWR from 'swr' import { useTranslation } from 'react-i18next' import type { CredentialFormSchema, + CredentialFormSchemaRadio, + CredentialFormSchemaSelect, FormValue, ModelProvider, } from '../declarations' @@ -12,6 +14,8 @@ import { FormTypeEnum, } from '../declarations' import { + genModelNameFormSchema, + genModelTypeFormSchema, removeCredentials, saveCredentials, validateCredentials, @@ -30,7 +34,6 @@ import { PortalToFollowElemContent, } from '@/app/components/base/portal-to-follow-elem' import { fetchModelProviderCredentials } from '@/service/common' -import Loading from '@/app/components/base/loading' import { useToastContext } from '@/app/components/base/toast' import ConfirmCommon from '@/app/components/base/confirm/common' @@ -47,26 +50,43 @@ const ModelModal: FC = ({ onCancel, onSave, }) => { + const { data: formSchemasValue } = useSWR( + `/workspaces/current/model-providers/${provider.provider}/credentials`, + fetchModelProviderCredentials, + { + keepPreviousData: false, + }, + ) + console.log(formSchemasValue) const { t } = useTranslation() const { notify } = useToastContext() const language = useLanguage() const [loading, setLoading] = useState(false) const [showConfirm, setShowConfirm] = useState(false) - const { data: formSchemasValue, isLoading } = useSWR(`/workspaces/current/model-providers/${provider.provider}/credentials`, fetchModelProviderCredentials) - const initialFormSchemasValue = useMemo(() => { - return formSchemasValue?.credentials || {} - }, [formSchemasValue]) - const [value, setValue] = useState(initialFormSchemasValue) - useEffect(() => { - setValue(initialFormSchemasValue) - }, [initialFormSchemasValue]) - const [validate, validating, validatedStatusState] = useValidate(value) - const isEditMode = !!formSchemasValue?.credentials - const formSchemas = provider.provider_credential_schema.credential_form_schemas const providerFormSchemaPredefined = configurateMethod === ConfigurateMethodEnum.predefinedModel - const [requiredFormSchemas, secretFormSchemas] = useMemo(() => { + const formSchemas = useMemo(() => { + return providerFormSchemaPredefined + ? provider.provider_credential_schema.credential_form_schemas + : [ + genModelTypeFormSchema(provider.supported_model_types), + genModelNameFormSchema(), + ...provider.provider_credential_schema.credential_form_schemas, + ] + }, [ + providerFormSchemaPredefined, + provider.provider_credential_schema.credential_form_schemas, + provider.supported_model_types, + ]) + const [ + requiredFormSchemas, + secretFormSchemas, + defaultFormSchemaValue, + showOnVariableMap, + ] = useMemo(() => { const requiredFormSchemas: CredentialFormSchema[] = [] const secretFormSchemas: CredentialFormSchema[] = [] + const defaultFormSchemaValue: Record = {} + const showOnVariableMap: Record = {} formSchemas.forEach((formSchema) => { if (formSchema.required) @@ -74,20 +94,73 @@ const ModelModal: FC = ({ if (formSchema.type === FormTypeEnum.secretInput) secretFormSchemas.push(formSchema) + + if (formSchema.default) + defaultFormSchemaValue[formSchema.variable] = formSchema.default + + if (formSchema.show_on.length) { + formSchema.show_on.forEach((showOnItem) => { + if (!showOnVariableMap[showOnItem.variable]) + showOnVariableMap[showOnItem.variable] = [] + + if (!showOnVariableMap[showOnItem.variable].includes(formSchema.variable)) + showOnVariableMap[showOnItem.variable].push(formSchema.variable) + }) + } + + if (formSchema.type === FormTypeEnum.select || formSchema.type === FormTypeEnum.radio) { + (formSchema as (CredentialFormSchemaRadio | CredentialFormSchemaSelect)).options.forEach((option) => { + if (option.show_on.length) { + option.show_on.forEach((showOnItem) => { + if (!showOnVariableMap[showOnItem.variable]) + showOnVariableMap[showOnItem.variable] = [] + + if (!showOnVariableMap[showOnItem.variable].includes(formSchema.variable)) + showOnVariableMap[showOnItem.variable].push(formSchema.variable) + }) + } + }) + } }) - return [requiredFormSchemas, secretFormSchemas] + return [ + requiredFormSchemas, + secretFormSchemas, + defaultFormSchemaValue, + showOnVariableMap, + ] }, [formSchemas]) + const initialFormSchemasValue = useMemo(() => { + return { + ...defaultFormSchemaValue, + ...formSchemasValue?.credentials, + } + }, [formSchemasValue, defaultFormSchemaValue]) + const [value, setValue] = useState(initialFormSchemasValue) + useEffect(() => { + setValue(initialFormSchemasValue) + }, [initialFormSchemasValue]) + const [validate, validating, validatedStatusState] = useValidate(value) + const isEditMode = !!formSchemasValue?.credentials const formRequiredValueAllCompleted = requiredFormSchemas.every(formSchema => value[formSchema.variable]) const handleValueChange = (v: FormValue) => { setValue(v) + const filteredRequiredFormSchemas = requiredFormSchemas.filter((requiredFormSchema) => { + if (requiredFormSchema.show_on.length && requiredFormSchema.show_on.every(showOnItem => v[showOnItem.variable] === showOnItem.value)) + return true + + if (!requiredFormSchema.show_on.length) + return true - if (requiredFormSchemas.length) { + return false + }) + + if (filteredRequiredFormSchemas.length) { validate({ before: () => { - for (let i = 0; i < requiredFormSchemas.length; i++) { - if (!v[requiredFormSchemas[i].variable]) + for (let i = 0; i < filteredRequiredFormSchemas.length; i++) { + if (!v[filteredRequiredFormSchemas[i].variable]) return false } return true @@ -96,9 +169,7 @@ const ModelModal: FC = ({ return validateCredentials( providerFormSchemaPredefined, provider.provider, - { - credentials: v, - }, + v, ) }, }) @@ -118,10 +189,8 @@ const ModelModal: FC = ({ providerFormSchemaPredefined, provider.provider, { - credentials: { - ...value, - ...secretValues, - }, + ...value, + ...secretValues, }, ) if (res.result === 'success') { @@ -142,6 +211,7 @@ const ModelModal: FC = ({ const res = await removeCredentials( providerFormSchemaPredefined, provider.provider, + value, ) if (res.result === 'success') { notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') }) @@ -163,111 +233,103 @@ const ModelModal: FC = ({ return ( - { - isLoading && ( - - ) - } - { - !isLoading && ( -
-
-
-
-
{renderTitlePrefix()}
- -
- -
- { - (provider.help && (provider.help.title || provider.help.url)) - ? ( - !provider.help.url && e.preventDefault()} - > - {provider.help.title?.[language] || provider.help.url[language]} - - - ) - :
- } -
- { - isEditMode && ( - - ) - } - + {provider.help.title?.[language] || provider.help.url[language]} + + + ) + :
+ } +
+ { + isEditMode && ( -
-
-
-
- { - (validatedStatusState.status === ValidatedStatus.Error && validatedStatusState.message) - ? ( -
- - {validatedStatusState.message} -
- ) - : ( -
- - {t('common.modelProvider.encrypted.front')} - - PKCS1_OAEP - - {t('common.modelProvider.encrypted.back')} -
- ) + ) } + +
+
+
{ - showConfirm && ( - setShowConfirm(false)} - onConfirm={handleRemove} - confirmWrapperClassName='z-[70]' - /> - ) + (validatedStatusState.status === ValidatedStatus.Error && validatedStatusState.message) + ? ( +
+ + {validatedStatusState.message} +
+ ) + : ( +
+ + {t('common.modelProvider.encrypted.front')} + + PKCS1_OAEP + + {t('common.modelProvider.encrypted.back')} +
+ ) }
- ) - } +
+ { + showConfirm && ( + setShowConfirm(false)} + onConfirm={handleRemove} + confirmWrapperClassName='z-[70]' + /> + ) + } +
) diff --git a/web/app/components/header/account-setting/model-provider-page/system-model-selector/index.tsx b/web/app/components/header/account-setting/model-provider-page/system-model-selector/index.tsx index 69ae0e7fbdc6db..0a36f9ad53b916 100644 --- a/web/app/components/header/account-setting/model-provider-page/system-model-selector/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/system-model-selector/index.tsx @@ -29,11 +29,15 @@ const SystemModel: FC = ({ rerankModelList, speech2textDefaultModel, speech2textModelList, + moderationModelList, + moderationDefaultModel, + updateModelList, } = useProviderContext() const [currentTextGenerationDefaultModel, changeCurrentTextGenerationDefaultModel] = useDefaultModelAndModelList(textGenerationDefaultModel, textGenerationModelList) const [currentEmbeddingsDefaultModel, changeCurrentEmbeddingsDefaultModel] = useDefaultModelAndModelList(embeddingsDefaultModel, embeddingsModelList) const [currentRerankDefaultModel, changeCurrentRerankDefaultModel] = useDefaultModelAndModelList(rerankDefaultModel, rerankModelList) const [currentSpeech2textDefaultModel, changeCurrentSpeech2textDefaultModel] = useDefaultModelAndModelList(speech2textDefaultModel, speech2textModelList) + const [currentModerationDefaultModel, changeCurrentModerationDefaultModel] = useDefaultModelAndModelList(moderationDefaultModel, moderationModelList) const [open, setOpen] = useState(false) const handleChangeDefaultModel = async () => { @@ -148,6 +152,27 @@ const SystemModel: FC = ({ />
+
+
+ {t('common.modelProvider.moderationModel.key')} + {t('common.modelProvider.moderationModel.tip')}
+ } + > + + +
+
+ +
+
diff --git a/web/app/components/header/account-setting/model-provider-page/utils.ts b/web/app/components/header/account-setting/model-provider-page/utils.ts index 749a03f485107e..c424b142008389 100644 --- a/web/app/components/header/account-setting/model-provider-page/utils.ts +++ b/web/app/components/header/account-setting/model-provider-page/utils.ts @@ -64,7 +64,7 @@ export const saveCredentials = async (predefined: boolean, provider: string, v: url = `/workspaces/current/model-providers/${provider}` } else { - const { __model_name, __model_type, credentials } = v + const { __model_name, __model_type, ...credentials } = v body = { model: __model_name, model_type: __model_type, @@ -134,17 +134,17 @@ export const genModelTypeFormSchema = (modelTypes: ModelTypeEnum[]) => { } as CredentialFormSchemaRadio } -export const genModelNameFormSchema = () => { +export const genModelNameFormSchema = (model: Pick) => { return { type: FormTypeEnum.textInput, - label: { + label: model.label || { zh_Hans: '模型名称', en_US: 'Model Name', }, variable: '__model_name', required: true, show_on: [], - placeholder: { + placeholder: model.placeholder || { zh_Hans: '请输入模型名称', en_US: 'Please enter model name', }, From 848075984d8631b8f31ba72cd82961f4108b67e5 Mon Sep 17 00:00:00 2001 From: StyleZhang Date: Sun, 24 Dec 2023 10:10:58 +0800 Subject: [PATCH 10/46] fix model-modal --- .../model-provider-page/declarations.ts | 5 +++ .../model-provider-page/hooks.ts | 33 +++++++++++++++++++ .../model-provider-page/index.tsx | 13 ++++++-- .../model-provider-page/model-modal/index.tsx | 27 +++++++++------ .../provider-added-card/index.tsx | 6 ++-- .../provider-added-card/model-list.tsx | 19 ++++++++--- .../model-provider-page/utils.ts | 6 ++-- 7 files changed, 88 insertions(+), 21 deletions(-) diff --git a/web/app/components/header/account-setting/model-provider-page/declarations.ts b/web/app/components/header/account-setting/model-provider-page/declarations.ts index 45b8c4fd9803bd..ac773765ae0770 100644 --- a/web/app/components/header/account-setting/model-provider-page/declarations.ts +++ b/web/app/components/header/account-setting/model-provider-page/declarations.ts @@ -164,3 +164,8 @@ export type DefaultModel = { provider: string model: string } + +export type CustomConfigrationModelFixedFields = { + __model_name: string + __model_type: ModelTypeEnum +} diff --git a/web/app/components/header/account-setting/model-provider-page/hooks.ts b/web/app/components/header/account-setting/model-provider-page/hooks.ts index a8739949466ba8..3ede94963bd462 100644 --- a/web/app/components/header/account-setting/model-provider-page/hooks.ts +++ b/web/app/components/header/account-setting/model-provider-page/hooks.ts @@ -4,14 +4,18 @@ import { useMemo, useState, } from 'react' +import useSWR from 'swr' import { useContext } from 'use-context-selector' import type { + CustomConfigrationModelFixedFields, DefaultModel, DefaultModelResponse, Model, } from './declarations' +import { ConfigurateMethodEnum } from './declarations' import { languageMaps } from './utils' import I18n from '@/context/i18n' +import { fetchModelProviderCredentials } from '@/service/common' type UseDefaultModelAndModelList = ( defaultModel: DefaultModelResponse | undefined, @@ -47,3 +51,32 @@ export const useLanguage = () => { return languageMaps[locale] } + +export const useProviderCrenditialsFormSchemasValue = ( + provider: string, + configurateMethod: ConfigurateMethodEnum, + configured?: boolean, + currentCustomConfigrationModelFixedFields?: CustomConfigrationModelFixedFields, +) => { + const { data: predefinedFormSchemasValue } = useSWR( + (configurateMethod === ConfigurateMethodEnum.predefinedModel && configured) + ? `/workspaces/current/model-providers/${provider}/credentials` + : null, + fetchModelProviderCredentials, + ) + const { data: customFormSchemasValue } = useSWR( + (configurateMethod === ConfigurateMethodEnum.customizableModel && currentCustomConfigrationModelFixedFields) + ? `/workspaces/current/model-providers/${provider}/models/credentials?model=${currentCustomConfigrationModelFixedFields?.__model_name}&model_type=${currentCustomConfigrationModelFixedFields?.__model_type}` + : null, + fetchModelProviderCredentials, + ) + + return configurateMethod === ConfigurateMethodEnum.predefinedModel + ? predefinedFormSchemasValue?.credentials + : customFormSchemasValue?.credentials + ? { + ...customFormSchemasValue?.credentials, + ...currentCustomConfigrationModelFixedFields, + } + : undefined +} diff --git a/web/app/components/header/account-setting/model-provider-page/index.tsx b/web/app/components/header/account-setting/model-provider-page/index.tsx index dc4bb2e2fa8611..8729e3cf879082 100644 --- a/web/app/components/header/account-setting/model-provider-page/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/index.tsx @@ -7,6 +7,7 @@ import ProviderCard from './provider-card' import ModelModal from './model-modal' import type { ConfigurateMethodEnum, + CustomConfigrationModelFixedFields, ModelProvider, } from './declarations' import { CustomConfigurationStatusEnum } from './declarations' @@ -25,6 +26,7 @@ const ModelProviderPage = () => { } = useProviderContext() const [currentProvider, setCurrentProvider] = useState(null) const [currentConfigurateMethod, setCurrentConfigurateMethod] = useState(null) + const [currentCustomConfigrationModelFixedFields, setCurrentCustomConfigrationModelFixedFields] = useState(undefined) const { data: providersData, mutate: mutateProviders } = useSWR('/workspaces/current/model-providers', fetchModelProviders) const defaultModelNotConfigured = !textGenerationDefaultModel && !embeddingsDefaultModel && !speech2textDefaultModel && !rerankDefaultModel const providers = useMemo(() => { @@ -44,14 +46,20 @@ const ModelProviderPage = () => { return [configedProviders, notConfigedProviders] }, [providers]) - const handleOpenModal = (provider: ModelProvider, configurateMethod: ConfigurateMethodEnum) => { + const handleOpenModal = ( + provider: ModelProvider, + configurateMethod: ConfigurateMethodEnum, + customConfigrationModelFixedFields?: CustomConfigrationModelFixedFields, + ) => { setCurrentProvider(provider) setCurrentConfigurateMethod(configurateMethod) + setCurrentCustomConfigrationModelFixedFields(customConfigrationModelFixedFields) } const handleCancelModelModal = () => { setCurrentProvider(null) setCurrentConfigurateMethod(null) + setCurrentCustomConfigrationModelFixedFields(undefined) } const handleSaveCrendentials = () => { @@ -84,7 +92,7 @@ const ModelProviderPage = () => { handleOpenModal(provider, configurateMethod)} + onOpenModal={(configurateMethod: ConfigurateMethodEnum, currentCustomConfigrationModelFixedFields?: CustomConfigrationModelFixedFields) => handleOpenModal(provider, configurateMethod, currentCustomConfigrationModelFixedFields)} /> )) } @@ -117,6 +125,7 @@ const ModelProviderPage = () => { diff --git a/web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx b/web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx index 5c557566a4aa59..10a3cb23cff254 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx @@ -1,16 +1,17 @@ import type { FC } from 'react' import { useCallback, useEffect, useMemo, useState } from 'react' -import useSWR from 'swr' import { useTranslation } from 'react-i18next' import type { CredentialFormSchema, CredentialFormSchemaRadio, CredentialFormSchemaSelect, + CustomConfigrationModelFixedFields, FormValue, ModelProvider, } from '../declarations' import { ConfigurateMethodEnum, + CustomConfigurationStatusEnum, FormTypeEnum, } from '../declarations' import { @@ -20,7 +21,10 @@ import { saveCredentials, validateCredentials, } from '../utils' -import { useLanguage } from '../hooks' +import { + useLanguage, + useProviderCrenditialsFormSchemasValue, +} from '../hooks' import ProviderIcon from '../provider-icon' import { useValidate } from '../../key-validator/hooks' import { ValidatedStatus } from '../../key-validator/declarations' @@ -33,13 +37,13 @@ import { PortalToFollowElem, PortalToFollowElemContent, } from '@/app/components/base/portal-to-follow-elem' -import { fetchModelProviderCredentials } from '@/service/common' import { useToastContext } from '@/app/components/base/toast' import ConfirmCommon from '@/app/components/base/confirm/common' type ModelModalProps = { provider: ModelProvider configurateMethod: ConfigurateMethodEnum + currentCustomConfigrationModelFixedFields?: CustomConfigrationModelFixedFields onCancel: () => void onSave: () => void } @@ -47,25 +51,29 @@ type ModelModalProps = { const ModelModal: FC = ({ provider, configurateMethod, + currentCustomConfigrationModelFixedFields, onCancel, onSave, }) => { - const { data: formSchemasValue } = useSWR( - `/workspaces/current/model-providers/${provider.provider}/credentials`, - fetchModelProviderCredentials, + const providerFormSchemaPredefined = configurateMethod === ConfigurateMethodEnum.predefinedModel + const formSchemasValue = useProviderCrenditialsFormSchemasValue( + provider.provider, + configurateMethod, + providerFormSchemaPredefined && provider.custom_configuration.status === CustomConfigurationStatusEnum.active, + currentCustomConfigrationModelFixedFields, ) + const isEditMode = !!formSchemasValue const { t } = useTranslation() const { notify } = useToastContext() const language = useLanguage() const [loading, setLoading] = useState(false) const [showConfirm, setShowConfirm] = useState(false) - const providerFormSchemaPredefined = configurateMethod === ConfigurateMethodEnum.predefinedModel const formSchemas = useMemo(() => { return providerFormSchemaPredefined ? provider.provider_credential_schema.credential_form_schemas : [ genModelTypeFormSchema(provider.supported_model_types), - genModelNameFormSchema(provider.model_credential_schema.model), + genModelNameFormSchema(provider.model_credential_schema?.model), ...provider.model_credential_schema.credential_form_schemas, ] }, [ @@ -131,7 +139,7 @@ const ModelModal: FC = ({ const initialFormSchemasValue = useMemo(() => { return { ...defaultFormSchemaValue, - ...formSchemasValue?.credentials, + ...formSchemasValue, } }, [formSchemasValue, defaultFormSchemaValue]) const [value, setValue] = useState(initialFormSchemasValue) @@ -139,7 +147,6 @@ const ModelModal: FC = ({ setValue(initialFormSchemasValue) }, [initialFormSchemasValue]) const [validate, validating, validatedStatusState] = useValidate(value) - const isEditMode = !!formSchemasValue?.credentials const filteredRequiredFormSchemas = requiredFormSchemas.filter((requiredFormSchema) => { if (requiredFormSchema.show_on.length && requiredFormSchema.show_on.every(showOnItem => value[showOnItem.variable] === showOnItem.value)) return true diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/index.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/index.tsx index 91b85ea416c546..3b278ab6ea378f 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/index.tsx @@ -1,6 +1,7 @@ import type { FC } from 'react' import { useState } from 'react' import type { + CustomConfigrationModelFixedFields, ModelItem, ModelProvider, } from '../declarations' @@ -21,7 +22,7 @@ import { fetchModelProviderModelList } from '@/service/common' type ProviderAddedCardProps = { provider: ModelProvider - onOpenModal: (configurateMethod: ConfigurateMethodEnum) => void + onOpenModal: (configurateMethod: ConfigurateMethodEnum, currentCustomConfigrationModelFixedFields?: CustomConfigrationModelFixedFields) => void } const ProviderAddedCard: FC = ({ provider, @@ -119,7 +120,7 @@ const ProviderAddedCard: FC = ({ { configurateMethods.includes(ConfigurateMethodEnum.customizableModel) && ( {}} + onClick={() => onOpenModal(ConfigurateMethodEnum.customizableModel)} className='hidden group-hover:flex group-hover:text-primary-600' /> ) @@ -133,6 +134,7 @@ const ProviderAddedCard: FC = ({ provider={provider} models={modelList} onCollapse={() => setCollapsed(true)} + onConfig={currentCustomConfigrationModelFixedFields => onOpenModal(ConfigurateMethodEnum.customizableModel, currentCustomConfigrationModelFixedFields)} /> ) } diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-list.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-list.tsx index d5fa59c0179345..d6405d83e818d9 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-list.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-list.tsx @@ -1,5 +1,9 @@ import type { FC } from 'react' -import type { ModelItem, ModelProvider } from '../declarations' +import type { + CustomConfigrationModelFixedFields, + ModelItem, + ModelProvider, +} from '../declarations' import { ConfigurateMethodEnum, ModelStatusEnum, @@ -18,11 +22,13 @@ type ModelListProps = { provider: ModelProvider models: ModelItem[] onCollapse: () => void + onConfig: (currentCustomConfigrationModelFixedFields?: CustomConfigrationModelFixedFields) => void } const ModelList: FC = ({ provider, models, onCollapse, + onConfig, }) => { const language = useLanguage() const configurateMethods = provider.configurate_methods.filter(method => method !== ConfigurateMethodEnum.fetchFromRemote) @@ -50,14 +56,16 @@ const ModelList: FC = ({ { canCustomConfig && canSystemConfig && ( - + {}} /> ) } { canCustomConfig && ( - {}} /> +
+ +
) }
@@ -86,7 +94,10 @@ const ModelList: FC = ({
{ canCustomConfig && ( - diff --git a/web/app/components/header/account-setting/model-provider-page/utils.ts b/web/app/components/header/account-setting/model-provider-page/utils.ts index c424b142008389..03ef1aef7fca7a 100644 --- a/web/app/components/header/account-setting/model-provider-page/utils.ts +++ b/web/app/components/header/account-setting/model-provider-page/utils.ts @@ -134,17 +134,17 @@ export const genModelTypeFormSchema = (modelTypes: ModelTypeEnum[]) => { } as CredentialFormSchemaRadio } -export const genModelNameFormSchema = (model: Pick) => { +export const genModelNameFormSchema = (model?: Pick) => { return { type: FormTypeEnum.textInput, - label: model.label || { + label: model?.label || { zh_Hans: '模型名称', en_US: 'Model Name', }, variable: '__model_name', required: true, show_on: [], - placeholder: model.placeholder || { + placeholder: model?.placeholder || { zh_Hans: '请输入模型名称', en_US: 'Please enter model name', }, From 0c2a6d48867aca7e5211a68ddb65a00d0aa797f8 Mon Sep 17 00:00:00 2001 From: StyleZhang Date: Mon, 25 Dec 2023 17:00:40 +0800 Subject: [PATCH 11/46] fix: i18n --- web/app/components/base/button/index.css | 31 +++++++++++++++++++ .../explore/create-app-modal/index.tsx | 1 - .../model-provider-page/declarations.ts | 2 +- .../model-provider-page/index.tsx | 2 +- .../model-provider-page/model-modal/Form.tsx | 2 ++ .../model-selector/popup-item.tsx | 4 ++- .../provider-added-card/add-model-button.tsx | 5 ++- .../provider-added-card/credential-panel.tsx | 5 +-- .../provider-added-card/index.tsx | 10 +++--- .../provider-added-card/model-list.tsx | 20 ++++++------ .../provider-added-card/quota-panel.tsx | 4 ++- .../provider-card/index.tsx | 2 +- web/app/styles/globals.css | 30 +----------------- web/i18n/lang/common.en.ts | 7 +++++ web/i18n/lang/common.zh.ts | 7 +++++ 15 files changed, 81 insertions(+), 51 deletions(-) create mode 100644 web/app/components/base/button/index.css diff --git a/web/app/components/base/button/index.css b/web/app/components/base/button/index.css new file mode 100644 index 00000000000000..bffb19be94dbd0 --- /dev/null +++ b/web/app/components/base/button/index.css @@ -0,0 +1,31 @@ +@tailwind components; + +@layer components { + .btn { + @apply inline-flex justify-center items-center content-center h-9 leading-5 rounded-lg px-4 py-2 text-base cursor-pointer; + } + + .btn-default { + @apply border-solid border border-gray-200 cursor-pointer text-gray-500 hover:bg-white hover:shadow-sm hover:border-gray-300; + } + + .btn-default-disabled { + @apply border-solid border border-gray-200 bg-gray-200 cursor-not-allowed text-gray-800; + } + + .btn-primary { + @apply bg-primary-600 hover:bg-primary-600/75 cursor-pointer text-white hover:shadow-sm; + } + + .btn-primary-disabled { + @apply bg-primary-200 cursor-not-allowed text-white; + } + + .btn-warning { + @apply bg-red-600 hover:bg-red-600/75 cursor-pointer text-white hover:shadow-sm; + } + + .btn-warning-disabled { + @apply bg-red-600/75 cursor-not-allowed text-white; + } +} \ No newline at end of file diff --git a/web/app/components/explore/create-app-modal/index.tsx b/web/app/components/explore/create-app-modal/index.tsx index 9a3ca3aae79ee4..4bf55e0aa3ce98 100644 --- a/web/app/components/explore/create-app-modal/index.tsx +++ b/web/app/components/explore/create-app-modal/index.tsx @@ -77,7 +77,6 @@ const CreateAppModal = ({ {showEmojiPicker && { - console.log(icon, icon_background) setEmoji({ icon, icon_background }) setShowEmojiPicker(false) }} diff --git a/web/app/components/header/account-setting/model-provider-page/declarations.ts b/web/app/components/header/account-setting/model-provider-page/declarations.ts index ac773765ae0770..dd334572956010 100644 --- a/web/app/components/header/account-setting/model-provider-page/declarations.ts +++ b/web/app/components/header/account-setting/model-provider-page/declarations.ts @@ -66,7 +66,7 @@ export type CredentialFormSchemaBase = { } export type CredentialFormSchemaTextInput = CredentialFormSchemaBase & { max_length?: number; placeholder?: TypeWithI18N } -export type CredentialFormSchemaSelect = CredentialFormSchemaBase & { options: FormOption[] } +export type CredentialFormSchemaSelect = CredentialFormSchemaBase & { options: FormOption[]; placeholder?: TypeWithI18N } export type CredentialFormSchemaRadio = CredentialFormSchemaBase & { options: FormOption[] } export type CredentialFormSchemaSecretInput = CredentialFormSchemaBase & { placeholder?: TypeWithI18N } export type CredentialFormSchema = CredentialFormSchemaTextInput | CredentialFormSchemaSelect | CredentialFormSchemaRadio | CredentialFormSchemaSecretInput diff --git a/web/app/components/header/account-setting/model-provider-page/index.tsx b/web/app/components/header/account-setting/model-provider-page/index.tsx index 8729e3cf879082..4e5951ded2f163 100644 --- a/web/app/components/header/account-setting/model-provider-page/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/index.tsx @@ -103,7 +103,7 @@ const ModelProviderPage = () => { !!notConfigedProviders?.length && ( <>
- + ADD MORE MODEL PROVIDER + + {t('common.modelProvider.addMoreModelProvider')}
diff --git a/web/app/components/header/account-setting/model-provider-page/model-modal/Form.tsx b/web/app/components/header/account-setting/model-provider-page/model-modal/Form.tsx index d6388f76f1f6c4..cf936b9a7841e5 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-modal/Form.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-modal/Form.tsx @@ -139,6 +139,7 @@ const Form: FC = ({ label, show_on, required, + placeholder, } = formSchema as CredentialFormSchemaSelect if (show_on.length && !show_on.every(showOnItem => value[showOnItem.variable] === showOnItem.value)) @@ -163,6 +164,7 @@ const Form: FC = ({ return true }).map(option => ({ value: option.value, name: option.label[language] }))} onSelect={item => handleFormChange(variable, item.value as string)} + placeholder={placeholder?.[language]} /> {validating && changeKey === variable && }
diff --git a/web/app/components/header/account-setting/model-provider-page/model-selector/popup-item.tsx b/web/app/components/header/account-setting/model-provider-page/model-selector/popup-item.tsx index 8a4234fada635b..693473e74aea69 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-selector/popup-item.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-selector/popup-item.tsx @@ -1,4 +1,5 @@ import type { FC } from 'react' +import { useTranslation } from 'react-i18next' import type { DefaultModel, Model, @@ -20,6 +21,7 @@ const PopupItem: FC = ({ model, onSelect, }) => { + const { t } = useTranslation() const language = useLanguage() const handleSelect = (provider: string, modelItem: ModelItem) => { if (modelItem.status !== ModelStatusEnum.active) @@ -68,7 +70,7 @@ const PopupItem: FC = ({ { modelItem.status === ModelStatusEnum.noConfigure && (
- ADD + {t('common.operation.add').toLocaleUpperCase()}
) } diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/add-model-button.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/add-model-button.tsx index 3395b1849581f3..cc8fa67efcc564 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/add-model-button.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/add-model-button.tsx @@ -1,4 +1,5 @@ import type { FC } from 'react' +import { useTranslation } from 'react-i18next' import { PlusCircle } from '@/app/components/base/icons/src/vender/solid/general' type AddModelButtonProps = { @@ -9,6 +10,8 @@ const AddModelButton: FC = ({ className, onClick, }) => { + const { t } = useTranslation() + return ( = ({ onClick={onClick} > - Add Model + {t('common.modelProvider.addModel')} ) } diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/credential-panel.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/credential-panel.tsx index 6dfcc8e966a723..0683e896791899 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/credential-panel.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/credential-panel.tsx @@ -23,6 +23,7 @@ const CredentialPanel: FC = ({ const customConfig = provider.custom_configuration const systemConfig = provider.system_configuration const priorityUseType = provider.preferred_provider_type + const customConfiged = customConfig.status === CustomConfigurationStatusEnum.active const handleChangePriority = () => {} @@ -30,7 +31,7 @@ const CredentialPanel: FC = ({
API-KEY - +
{ - systemConfig.enabled && ( + systemConfig.enabled && customConfiged && ( = ({ provider, onOpenModal, }) => { + const { t } = useTranslation() const [fetched, setFetched] = useState(false) const [loading, setLoading] = useState(false) const [collapsed, setCollapsed] = useState(true) @@ -97,8 +99,8 @@ const ProviderAddedCard: FC = ({
{ hasModelList - ? `${modelList.length} Models` - : 'Show Models' + ? t('common.modelProvider.modelsNum', { num: modelList.length }) + : t('common.modelProvider.showModels') }
= ({ { hasModelList - ? `Show ${modelList.length} Models` - : 'Show Models' + ? t('common.modelProvider.showModelsNum', { num: modelList.length }) + : t('common.modelProvider.showModels') } { loading && ( diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-list.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-list.tsx index d6405d83e818d9..12a44a4244c625 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-list.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-list.tsx @@ -1,4 +1,5 @@ import type { FC } from 'react' +import { useTranslation } from 'react-i18next' import type { CustomConfigrationModelFixedFields, ModelItem, @@ -11,7 +12,7 @@ import { import { useLanguage } from '../hooks' import ModelIcon from '../model-icon' import ModelName from '../model-name' -import Tab from './tab' +// import Tab from './tab' import AddModelButton from './add-model-button' import Indicator from '@/app/components/header/indicator' import { Settings01 } from '@/app/components/base/icons/src/vender/line/general' @@ -30,10 +31,11 @@ const ModelList: FC = ({ onCollapse, onConfig, }) => { + const { t } = useTranslation() const language = useLanguage() const configurateMethods = provider.configurate_methods.filter(method => method !== ConfigurateMethodEnum.fetchFromRemote) const canCustomConfig = configurateMethods.includes(ConfigurateMethodEnum.customizableModel) - const canSystemConfig = configurateMethods.includes(ConfigurateMethodEnum.predefinedModel) + // const canSystemConfig = configurateMethods.includes(ConfigurateMethodEnum.predefinedModel) return (
@@ -41,7 +43,7 @@ const ModelList: FC = ({
- {models.length} Models + {t('common.modelProvider.modelsNum', { num: models.length })} = ({ onClick={() => onCollapse()} > - Collapse + {t('common.modelProvider.collapse')} - { + {/* { canCustomConfig && canSystemConfig && ( {}} /> ) - } + } */} { canCustomConfig && (
- + onConfig()} />
) } @@ -93,13 +95,13 @@ const ModelList: FC = ({ />
{ - canCustomConfig && ( + model.fetch_from === ConfigurateMethodEnum.customizableModel && ( ) } diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/quota-panel.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/quota-panel.tsx index 9f1c371de783b2..b9709027df1980 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/quota-panel.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/quota-panel.tsx @@ -1,4 +1,5 @@ import type { FC } from 'react' +import { useTranslation } from 'react-i18next' import type { ModelProvider } from '../declarations' import { CustomConfigurationStatusEnum, @@ -13,13 +14,14 @@ type QuotaPanelProps = { const QuotaPanel: FC = ({ provider, }) => { + const { t } = useTranslation() const customConfig = provider.custom_configuration const priorityUseType = provider.preferred_provider_type return (
- QUOTA + {t('common.modelProvider.quota')}
diff --git a/web/app/components/header/account-setting/model-provider-page/provider-card/index.tsx b/web/app/components/header/account-setting/model-provider-page/provider-card/index.tsx index 56e77c876cd569..befa16dd7ee2bc 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-card/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-card/index.tsx @@ -72,7 +72,7 @@ const ProviderCard: FC = ({ onClick={() => onOpenModal(method)} > - Add Model + {t('common.modelProvider.addModel')} ) }) diff --git a/web/app/styles/globals.css b/web/app/styles/globals.css index 772d8945501ce1..cc9b1ea83d2b26 100644 --- a/web/app/styles/globals.css +++ b/web/app/styles/globals.css @@ -147,32 +147,4 @@ button:focus-within { bottom: 0; } -@layer components { - .btn { - @apply inline-flex justify-center items-center content-center h-9 leading-5 rounded-lg px-4 py-2 text-base; - } - - .btn-default { - @apply border-solid border border-gray-200 cursor-pointer text-gray-500 hover:bg-white hover:shadow-sm hover:border-gray-300; - } - - .btn-default-disabled { - @apply border-solid border border-gray-200 bg-gray-200 cursor-not-allowed text-gray-800; - } - - .btn-primary { - @apply bg-primary-600 hover:bg-primary-600/75 cursor-pointer text-white hover:shadow-sm; - } - - .btn-primary-disabled { - @apply bg-primary-200 cursor-not-allowed text-white; - } - - .btn-warning { - @apply bg-red-600 hover:bg-red-600/75 cursor-pointer text-white hover:shadow-sm; - } - - .btn-warning-disabled { - @apply bg-red-600/75 cursor-not-allowed text-white; - } -} \ No newline at end of file +@import '../components/base/button/index.css'; \ No newline at end of file diff --git a/web/i18n/lang/common.en.ts b/web/i18n/lang/common.en.ts index b7596fd239aae8..deb2980f61b208 100644 --- a/web/i18n/lang/common.en.ts +++ b/web/i18n/lang/common.en.ts @@ -289,6 +289,13 @@ const translation = { freeQuota: { howToEarn: 'How to earn', }, + addMoreModelProvider: 'ADD MORE MODEL PROVIDER', + addModel: 'Add Model', + modelsNum: '{{num}} Models', + showModels: 'Show Models', + showModelsNum: 'Show {{num}} Models', + collapse: 'Collapse', + config: 'Config', }, dataSource: { add: 'Add a data source', diff --git a/web/i18n/lang/common.zh.ts b/web/i18n/lang/common.zh.ts index 9f01add4aa24cf..36d3e5de673b5e 100644 --- a/web/i18n/lang/common.zh.ts +++ b/web/i18n/lang/common.zh.ts @@ -289,6 +289,13 @@ const translation = { freeQuota: { howToEarn: '如何获取', }, + addMoreModelProvider: '添加更多模型提供商', + addModel: '添加模型', + modelsNum: '{{num}} 个模型', + showModels: '显示模型', + showModelsNum: '显示 {{num}} 个模型', + collapse: '收起', + config: '配置', }, dataSource: { add: '添加数据源', From 5f7dfc14c9b6334e22e3da95bbf806b6e5dc0cd0 Mon Sep 17 00:00:00 2001 From: StyleZhang Date: Mon, 25 Dec 2023 20:30:24 +0800 Subject: [PATCH 12/46] feat: add model-parameter-rule --- .../dataset-config/params-config/index.tsx | 16 +- .../dataset-config/settings-modal/index.tsx | 10 +- .../hooks/use-advanced-prompt-config.ts | 7 +- .../components/app/configuration/index.tsx | 19 +- .../toolbox/annotation/config-param-modal.tsx | 7 +- .../line/alertsAndFeedback/alert-triangle.svg | 5 + .../line/alertsAndFeedback/AlertTriangle.json | 39 +++++ .../line/alertsAndFeedback/AlertTriangle.tsx | 16 ++ .../vender/line/alertsAndFeedback/index.ts | 1 + .../model-provider-page/declarations.ts | 13 ++ .../model-parameter-modal/index.tsx | 165 ++++++++++++++++++ .../model-parameter-modal/parameter-item.tsx | 89 ++++++++++ .../model-parameter-modal/stop-sequence.tsx | 0 web/context/debug-configuration.ts | 6 +- web/context/provider-context.tsx | 2 +- web/i18n/lang/common.en.ts | 2 + web/i18n/lang/common.zh.ts | 2 + web/service/common.ts | 5 + web/service/debug.ts | 3 +- 19 files changed, 373 insertions(+), 34 deletions(-) create mode 100644 web/app/components/base/icons/assets/vender/line/alertsAndFeedback/alert-triangle.svg create mode 100644 web/app/components/base/icons/src/vender/line/alertsAndFeedback/AlertTriangle.json create mode 100644 web/app/components/base/icons/src/vender/line/alertsAndFeedback/AlertTriangle.tsx create mode 100644 web/app/components/header/account-setting/model-provider-page/model-parameter-modal/index.tsx create mode 100644 web/app/components/header/account-setting/model-provider-page/model-parameter-modal/parameter-item.tsx create mode 100644 web/app/components/header/account-setting/model-provider-page/model-parameter-modal/stop-sequence.tsx diff --git a/web/app/components/app/configuration/dataset-config/params-config/index.tsx b/web/app/components/app/configuration/dataset-config/params-config/index.tsx index f9954c2e6715d1..0e5eafaafe46bc 100644 --- a/web/app/components/app/configuration/dataset-config/params-config/index.tsx +++ b/web/app/components/app/configuration/dataset-config/params-config/index.tsx @@ -12,9 +12,9 @@ import Modal from '@/app/components/base/modal' import Button from '@/app/components/base/button' import RadioCard from '@/app/components/base/radio-card/simple' import { RETRIEVE_TYPE } from '@/types/app' -import ModelSelector from '@/app/components/header/account-setting/model-page/model-selector' +// import ModelSelector from '@/app/components/header/account-setting/model-page/model-selector' import { useProviderContext } from '@/context/provider-context' -import { ModelType } from '@/app/components/header/account-setting/model-page/declarations' +// import { ModelType } from '@/app/components/header/account-setting/model-page/declarations' import Toast from '@/app/components/base/toast' import { DATASET_DEFAULT } from '@/config' import { @@ -53,8 +53,8 @@ const ParamsConfig: FC = () => { } else if (rerankDefaultModel) { return { - provider_name: rerankDefaultModel.model_provider.provider_name, - model_name: rerankDefaultModel.model_name, + provider_name: rerankDefaultModel.provider, + model_name: rerankDefaultModel.model, } } })() @@ -104,8 +104,8 @@ const ParamsConfig: FC = () => { const config = { ...tempDataSetConfigs } if (config.retrieval_model === RETRIEVE_TYPE.multiWay && !config.reranking_model) { config.reranking_model = { - reranking_provider_name: rerankDefaultModel?.model_provider.provider_name, - reranking_model_name: rerankDefaultModel?.model_name, + reranking_provider_name: rerankDefaultModel?.provider, + reranking_model_name: rerankDefaultModel?.model, } as any } setDatasetConfigs(config) @@ -162,7 +162,7 @@ const ParamsConfig: FC = () => {
{t('common.modelProvider.rerankModel.key')}
- { }, }) }} - /> + /> */}
diff --git a/web/app/components/app/configuration/dataset-config/settings-modal/index.tsx b/web/app/components/app/configuration/dataset-config/settings-modal/index.tsx index 1c6d25c4a73344..2c29e8af5b882a 100644 --- a/web/app/components/app/configuration/dataset-config/settings-modal/index.tsx +++ b/web/app/components/app/configuration/dataset-config/settings-modal/index.tsx @@ -6,9 +6,9 @@ import cn from 'classnames' import { BookOpenIcon } from '@heroicons/react/24/outline' import IndexMethodRadio from '@/app/components/datasets/settings/index-method-radio' import Button from '@/app/components/base/button' -import ModelSelector from '@/app/components/header/account-setting/model-page/model-selector' -import type { ProviderEnum } from '@/app/components/header/account-setting/model-page/declarations' -import { ModelType } from '@/app/components/header/account-setting/model-page/declarations' +// import ModelSelector from '@/app/components/header/account-setting/model-page/model-selector' +// import type { ProviderEnum } from '@/app/components/header/account-setting/model-page/declarations' +// import { ModelType } from '@/app/components/header/account-setting/model-page/declarations' import type { DataSet } from '@/models/datasets' import { useToastContext } from '@/app/components/base/toast' import { updateDatasetSetting } from '@/service/datasets' @@ -202,7 +202,7 @@ const SettingsModal: FC = ({
- = ({ }} modelType={ModelType.embeddings} onChange={() => {}} - /> + /> */}
{t('datasetSettings.form.embeddingModelTip')} diff --git a/web/app/components/app/configuration/hooks/use-advanced-prompt-config.ts b/web/app/components/app/configuration/hooks/use-advanced-prompt-config.ts index faf929697ec285..c9106c77766a5d 100644 --- a/web/app/components/app/configuration/hooks/use-advanced-prompt-config.ts +++ b/web/app/components/app/configuration/hooks/use-advanced-prompt-config.ts @@ -1,12 +1,13 @@ import { useState } from 'react' import { clone } from 'lodash-es' import produce from 'immer' -import type { ChatPromptConfig, CompletionParams, CompletionPromptConfig, ConversationHistoriesRole, PromptItem } from '@/models/debug' +import type { ChatPromptConfig, CompletionPromptConfig, ConversationHistoriesRole, PromptItem } from '@/models/debug' import { PromptMode } from '@/models/debug' import { AppType, ModelModeType } from '@/types/app' import { DEFAULT_CHAT_PROMPT_CONFIG, DEFAULT_COMPLETION_PROMPT_CONFIG } from '@/config' import { PRE_PROMPT_PLACEHOLDER_TEXT, checkHasContextBlock, checkHasHistoryBlock, checkHasQueryBlock } from '@/app/components/base/prompt-editor/constants' import { fetchPromptTemplate } from '@/service/debug' +import type { FormValue } from '@/app/components/header/account-setting/model-provider-page/declarations' type Param = { appMode: string @@ -16,8 +17,8 @@ type Param = { prePrompt: string onUserChangedPrompt: () => void hasSetDataSet: boolean - completionParams: CompletionParams - setCompletionParams: (params: CompletionParams) => void + completionParams: FormValue + setCompletionParams: (params: FormValue) => void setStop: (stop: string[]) => void } diff --git a/web/app/components/app/configuration/index.tsx b/web/app/components/app/configuration/index.tsx index dc480fbe9743ed..b4f206d769ba98 100644 --- a/web/app/components/app/configuration/index.tsx +++ b/web/app/components/app/configuration/index.tsx @@ -16,7 +16,6 @@ import useAdvancedPromptConfig from './hooks/use-advanced-prompt-config' import EditHistoryModal from './config-prompt/conversation-histroy/edit-modal' import type { AnnotationReplyConfig, - CompletionParams, DatasetConfigs, Inputs, ModelConfig, @@ -29,7 +28,7 @@ import type { ExternalDataTool } from '@/models/common' import type { DataSet } from '@/models/datasets' import type { ModelConfig as BackendModelConfig, VisionSettings } from '@/types/app' import ConfigContext from '@/context/debug-configuration' -import ConfigModel from '@/app/components/app/configuration/config-model' +// import ConfigModel from '@/app/components/app/configuration/config-model' import Config from '@/app/components/app/configuration/config' import Debug from '@/app/components/app/configuration/debug' import Confirm from '@/app/components/base/confirm' @@ -48,10 +47,12 @@ import I18n from '@/context/i18n' import { useModalContext } from '@/context/modal-context' import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' import Drawer from '@/app/components/base/drawer' +import ModelParameterModal from '@/app/components/header/account-setting/model-provider-page/model-parameter-modal' +import type { FormValue } from '@/app/components/header/account-setting/model-provider-page/declarations' type PublichConfig = { modelConfig: ModelConfig - completionParams: CompletionParams + completionParams: FormValue } const Configuration: FC = () => { @@ -112,7 +113,7 @@ const Configuration: FC = () => { const [externalDataToolsConfig, setExternalDataToolsConfig] = useState([]) const [inputs, setInputs] = useState({}) const [query, setQuery] = useState('') - const [completionParams, doSetCompletionParams] = useState({ + const [completionParams, doSetCompletionParams] = useState({ max_tokens: 16, temperature: 1, // 0-2 top_p: 1, @@ -121,7 +122,7 @@ const Configuration: FC = () => { stop: [], }) const [tempStop, setTempStop, getTempStop] = useGetState([]) - const setCompletionParams = (value: CompletionParams) => { + const setCompletionParams = (value: FormValue) => { const params = { ...value } // eslint-disable-next-line @typescript-eslint/no-use-before-define @@ -281,11 +282,11 @@ const Configuration: FC = () => { }) const setModel = async ({ - id: modelId, + model: modelId, provider, mode: modeMode, features, - }: { id: string; provider: string; mode: ModelModeType; features: string[] }) => { + }: { model: string; provider: string; mode: string; features: string[] }) => { if (isAdvancedMode) { const appMode = mode @@ -646,14 +647,14 @@ const Configuration: FC = () => {
{/* Model and Parameters */} - { + onCompletionParamsChange={(newParams: FormValue) => { setCompletionParams(newParams) }} disabled={!hasSettedApiKey} diff --git a/web/app/components/app/configuration/toolbox/annotation/config-param-modal.tsx b/web/app/components/app/configuration/toolbox/annotation/config-param-modal.tsx index 95234e52a1e861..40b3ef588c9414 100644 --- a/web/app/components/app/configuration/toolbox/annotation/config-param-modal.tsx +++ b/web/app/components/app/configuration/toolbox/annotation/config-param-modal.tsx @@ -6,8 +6,7 @@ import ScoreSlider from '../score-slider' import { Item } from './config-param' import Modal from '@/app/components/base/modal' import Button from '@/app/components/base/button' -import { ModelType } from '@/app/components/header/account-setting/model-page/declarations' -import ModelSelector from '@/app/components/header/account-setting/model-page/model-selector/portal-select' +// import ModelSelector from '@/app/components/header/account-setting/model-page/model-selector/portal-select' import { useProviderContext } from '@/context/provider-context' import Toast from '@/app/components/base/toast' import type { AnnotationReplyConfig } from '@/models/debug' @@ -105,7 +104,7 @@ const ConfigParamModal: FC = ({ tooltip={t('appAnnotation.embeddingModelSwitchTip')} >
- = ({ modelName: val.model_name, }) }} - /> + /> */}
diff --git a/web/app/components/base/icons/assets/vender/line/alertsAndFeedback/alert-triangle.svg b/web/app/components/base/icons/assets/vender/line/alertsAndFeedback/alert-triangle.svg new file mode 100644 index 00000000000000..05f15c960af809 --- /dev/null +++ b/web/app/components/base/icons/assets/vender/line/alertsAndFeedback/alert-triangle.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/web/app/components/base/icons/src/vender/line/alertsAndFeedback/AlertTriangle.json b/web/app/components/base/icons/src/vender/line/alertsAndFeedback/AlertTriangle.json new file mode 100644 index 00000000000000..a200e6035e82cb --- /dev/null +++ b/web/app/components/base/icons/src/vender/line/alertsAndFeedback/AlertTriangle.json @@ -0,0 +1,39 @@ +{ + "icon": { + "type": "element", + "isRootNode": true, + "name": "svg", + "attributes": { + "width": "16", + "height": "16", + "viewBox": "0 0 16 16", + "fill": "none", + "xmlns": "http://www.w3.org/2000/svg" + }, + "children": [ + { + "type": "element", + "name": "g", + "attributes": { + "id": "alert-triangle" + }, + "children": [ + { + "type": "element", + "name": "path", + "attributes": { + "id": "Icon", + "d": "M7.99977 5.33314V7.99981M7.99977 10.6665H8.00644M6.85977 1.90648L1.2131 11.3331C1.09668 11.5348 1.03508 11.7633 1.03443 11.9962C1.03378 12.229 1.0941 12.4579 1.20939 12.6602C1.32468 12.8624 1.49092 13.031 1.69157 13.149C1.89223 13.2671 2.1203 13.3306 2.3531 13.3331H13.6464C13.8792 13.3306 14.1073 13.2671 14.308 13.149C14.5086 13.031 14.6749 12.8624 14.7902 12.6602C14.9054 12.4579 14.9658 12.229 14.9651 11.9962C14.9645 11.7633 14.9029 11.5348 14.7864 11.3331L9.13977 1.90648C9.02092 1.71055 8.85358 1.54856 8.6539 1.43613C8.45422 1.32371 8.22893 1.26465 7.99977 1.26465C7.77061 1.26465 7.54532 1.32371 7.34564 1.43613C7.14596 1.54856 6.97862 1.71055 6.85977 1.90648Z", + "stroke": "currentColor", + "stroke-width": "1.25", + "stroke-linecap": "round", + "stroke-linejoin": "round" + }, + "children": [] + } + ] + } + ] + }, + "name": "AlertTriangle" +} \ No newline at end of file diff --git a/web/app/components/base/icons/src/vender/line/alertsAndFeedback/AlertTriangle.tsx b/web/app/components/base/icons/src/vender/line/alertsAndFeedback/AlertTriangle.tsx new file mode 100644 index 00000000000000..780f859bd8b772 --- /dev/null +++ b/web/app/components/base/icons/src/vender/line/alertsAndFeedback/AlertTriangle.tsx @@ -0,0 +1,16 @@ +// GENERATE BY script +// DON NOT EDIT IT MANUALLY + +import * as React from 'react' +import data from './AlertTriangle.json' +import IconBase from '@/app/components/base/icons/IconBase' +import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase' + +const Icon = React.forwardRef, Omit>(( + props, + ref, +) => ) + +Icon.displayName = 'AlertTriangle' + +export default Icon diff --git a/web/app/components/base/icons/src/vender/line/alertsAndFeedback/index.ts b/web/app/components/base/icons/src/vender/line/alertsAndFeedback/index.ts index 1037c0958aacaa..3af8739829872b 100644 --- a/web/app/components/base/icons/src/vender/line/alertsAndFeedback/index.ts +++ b/web/app/components/base/icons/src/vender/line/alertsAndFeedback/index.ts @@ -1 +1,2 @@ export { default as AlertCircle } from './AlertCircle' +export { default as AlertTriangle } from './AlertTriangle' diff --git a/web/app/components/header/account-setting/model-provider-page/declarations.ts b/web/app/components/header/account-setting/model-provider-page/declarations.ts index dd334572956010..9e1edb9c9cab36 100644 --- a/web/app/components/header/account-setting/model-provider-page/declarations.ts +++ b/web/app/components/header/account-setting/model-provider-page/declarations.ts @@ -169,3 +169,16 @@ export type CustomConfigrationModelFixedFields = { __model_name: string __model_type: ModelTypeEnum } + +export type ModelParameterRule = { + default: number + help: TypeWithI18N + label: TypeWithI18N + min: number + max: number + name: string + precision: number + required: false + type: string + use_template: string +} diff --git a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/index.tsx b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/index.tsx new file mode 100644 index 00000000000000..196d5a1f1cd775 --- /dev/null +++ b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/index.tsx @@ -0,0 +1,165 @@ +import type { FC } from 'react' +import { useState } from 'react' +import useSWR from 'swr' +import { useTranslation } from 'react-i18next' +import type { + FormValue, +} from '../declarations' +import { ModelTypeEnum } from '../declarations' +import ModelIcon from '../model-icon' +import ModelName from '../model-name' +import ModelSelector from '../model-selector' +import ParameterItem from './parameter-item' +import { + PortalToFollowElem, + PortalToFollowElemContent, + PortalToFollowElemTrigger, +} from '@/app/components/base/portal-to-follow-elem' +import { SlidersH } from '@/app/components/base/icons/src/vender/line/mediaAndDevices' +import { AlertTriangle } from '@/app/components/base/icons/src/vender/line/alertsAndFeedback' +import { useProviderContext } from '@/context/provider-context' +import { CubeOutline } from '@/app/components/base/icons/src/vender/line/shapes' +import { fetchModelParameterRules } from '@/service/common' + +type ModelParameterModalProps = { + isAdvancedMode: boolean + mode: string + modelId: string + provider: string + setModel: (model: { model: string; provider: string; mode?: string; features: string[] }) => void + completionParams: FormValue + onCompletionParamsChange: (newParams: FormValue) => void + disabled: boolean +} +const ModelParameterModal: FC = ({ + isAdvancedMode, + mode, + modelId, + provider, + setModel, + completionParams, + onCompletionParamsChange, + disabled, +}) => { + const { t } = useTranslation() + const [open, setOpen] = useState(false) + const { data: parameterRulesData } = useSWR(`/workspaces/current/model-providers/${provider}/models/parameter-rules?model=${modelId}`, fetchModelParameterRules) + const { textGenerationModelList } = useProviderContext() + const currentProvider = textGenerationModelList.find(model => model.provider === provider) + const currentModel = currentProvider?.models.find(modelItem => modelItem.model === modelId) + + const handleParamChange = (key: string, value: number | string[]) => { + if (value === undefined) + return + + if (key === 'stop') { + onCompletionParamsChange({ + ...completionParams, + [key]: value as string[], + }) + } + else { + onCompletionParamsChange({ + ...completionParams, + [key]: value, + }) + } + } + + return ( + +
+ setOpen(v => !v)} + className='block' + > +
+ { + currentProvider && ( + + ) + } + { + currentModel && ( + + ) + } + { + disabled + ? ( + + ) + : ( + + ) + } +
+
+ +
+
+ + {t('common.modelProvider.modelAndParameters')} +
+
+
+
+ {t('common.modelProvider.model')} +
+ { + const targetProvider = textGenerationModelList.find(modelItem => modelItem.provider === provider) + const targetModelItem = targetProvider?.models.find(modelItem => modelItem.model === model) + setModel({ + model, + provider, + mode: targetModelItem?.model_properties.mode as string, + features: targetModelItem?.features || [], + }) + }} + /> +
+
+ { + parameterRulesData?.data && ( + parameterRulesData.data.map(parameter => ( + handleParamChange(parameter.name, v)} + /> + )) + ) + } +
+
+ +
+ + ) +} + +export default ModelParameterModal diff --git a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/parameter-item.tsx b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/parameter-item.tsx new file mode 100644 index 00000000000000..185f76f36695d0 --- /dev/null +++ b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/parameter-item.tsx @@ -0,0 +1,89 @@ +import type { FC } from 'react' +import type { ModelParameterRule } from '../declarations' +import { useLanguage } from '../hooks' +import { HelpCircle } from '@/app/components/base/icons/src/vender/line/general' +import Switch from '@/app/components/base/switch' +import Tooltip from '@/app/components/base/tooltip' +import Slider from '@/app/components/base/slider' + +type ParameterItemProps = { + parameterRule: ModelParameterRule + value: number + onChange: (value: number) => void + className?: string +} +const ParameterItem: FC = ({ + parameterRule, + value, + onChange, + className, +}) => { + const language = useLanguage() + + const handleChange = (e: React.ChangeEvent) => { + let num = +e.target.value + + if (num > parameterRule.max) + num = parameterRule.max + + if (num < parameterRule.min) + num = parameterRule.min + + onChange(num) + } + + return ( +
+
+
+ {parameterRule.label[language]} +
+ {parameterRule.help[language]}
+ )} + > + + + { + !parameterRule.required && ( + {}} + size='md' + /> + ) + } +
+ { + (parameterRule.type === 'int' || parameterRule.type === 'float') && ( +
+ + +
+ ) + } +
+ ) +} + +export default ParameterItem diff --git a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/stop-sequence.tsx b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/stop-sequence.tsx new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/web/context/debug-configuration.ts b/web/context/debug-configuration.ts index 17d61de166dd33..f666491286278c 100644 --- a/web/context/debug-configuration.ts +++ b/web/context/debug-configuration.ts @@ -5,7 +5,6 @@ import type { BlockStatus, ChatPromptConfig, CitationConfig, - CompletionParams, CompletionPromptConfig, ConversationHistoriesRole, DatasetConfigs, @@ -23,6 +22,7 @@ import type { DataSet } from '@/models/datasets' import type { VisionSettings } from '@/types/app' import { ModelModeType, RETRIEVE_TYPE, Resolution, TransferMethod } from '@/types/app' import { ANNOTATION_DEFAULT, DEFAULT_CHAT_PROMPT_CONFIG, DEFAULT_COMPLETION_PROMPT_CONFIG } from '@/config' +import type { FormValue } from '@/app/components/header/account-setting/model-provider-page/declarations' type IDebugConfiguration = { appId: string @@ -72,8 +72,8 @@ type IDebugConfiguration = { query: string // user question setQuery: (query: string) => void // Belows are draft infos - completionParams: CompletionParams - setCompletionParams: (completionParams: CompletionParams) => void + completionParams: FormValue + setCompletionParams: (completionParams: FormValue) => void // model_config modelConfig: ModelConfig setModelConfig: (modelConfig: ModelConfig) => void diff --git a/web/context/provider-context.tsx b/web/context/provider-context.tsx index 67489bee0d8987..3f8173d821643e 100644 --- a/web/context/provider-context.tsx +++ b/web/context/provider-context.tsx @@ -69,7 +69,7 @@ const ProviderContext = createContext<{ moderationDefaultModel: undefined, mutateModerationDefaultModel: () => {}, supportRetrievalMethods: [], - hasSettedApiKey: false, + hasSettedApiKey: true, plan: { type: Plan.sandbox, usage: { diff --git a/web/i18n/lang/common.en.ts b/web/i18n/lang/common.en.ts index deb2980f61b208..3f50ff3f31968b 100644 --- a/web/i18n/lang/common.en.ts +++ b/web/i18n/lang/common.en.ts @@ -296,6 +296,8 @@ const translation = { showModelsNum: 'Show {{num}} Models', collapse: 'Collapse', config: 'Config', + modelAndParameters: 'Model and Parameters', + model: 'Model', }, dataSource: { add: 'Add a data source', diff --git a/web/i18n/lang/common.zh.ts b/web/i18n/lang/common.zh.ts index 36d3e5de673b5e..ca78b55e54a8d8 100644 --- a/web/i18n/lang/common.zh.ts +++ b/web/i18n/lang/common.zh.ts @@ -296,6 +296,8 @@ const translation = { showModelsNum: '显示 {{num}} 个模型', collapse: '收起', config: '配置', + modelAndParameters: '模型及参数', + model: '模型', }, dataSource: { add: '添加数据源', diff --git a/web/service/common.ts b/web/service/common.ts index 431f99f461feb1..a3a1c7c8118f34 100644 --- a/web/service/common.ts +++ b/web/service/common.ts @@ -29,6 +29,7 @@ import type { DefaultModelResponse, Model, ModelItem, + ModelParameterRule, ModelProvider, } from '@/app/components/header/account-setting/model-provider-page/declarations' import type { RETRIEVE_METHOD } from '@/types/app' @@ -203,6 +204,10 @@ export const updateDefaultModel: Fetcher(url, { body }) } +export const fetchModelParameterRules: Fetcher<{ data: ModelParameterRule[] }, string> = (url) => { + return get<{ data: ModelParameterRule[] }>(url) +} + export const submitFreeQuota: Fetcher<{ type: string; redirect_url?: string; result?: string }, string> = (url) => { return post<{ type: string; redirect_url?: string; result?: string }>(url) } diff --git a/web/service/debug.ts b/web/service/debug.ts index 0c627fe9c72996..c555ffe0de6b05 100644 --- a/web/service/debug.ts +++ b/web/service/debug.ts @@ -2,6 +2,7 @@ import type { IOnAnnotationReply, IOnCompleted, IOnData, IOnError, IOnMessageEnd import { get, post, ssePost } from './base' import type { ChatPromptConfig, CompletionPromptConfig } from '@/models/debug' import type { ModelModeType } from '@/types/app' +import type { ModelParameterRule } from '@/app/components/header/account-setting/model-provider-page/declarations' export type AutomaticRes = { prompt: string @@ -67,7 +68,7 @@ export const fetchModelParams = (providerName: string, modelId: string) => { params: { model: modelId, }, - }) + }) as Promise<{ data: ModelParameterRule[] }> } export const fetchPromptTemplate = ({ From 8abd39b5bd86b7335ba71c5a9a474b0f1fe24d3b Mon Sep 17 00:00:00 2001 From: StyleZhang Date: Mon, 25 Dec 2023 20:40:54 +0800 Subject: [PATCH 13/46] fix: slider style --- web/app/components/base/slider/style.css | 7 ++++--- .../model-provider-page/model-parameter-modal/index.tsx | 8 +++++++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/web/app/components/base/slider/style.css b/web/app/components/base/slider/style.css index 4ac9a963b498b4..212d4d1dccc30b 100644 --- a/web/app/components/base/slider/style.css +++ b/web/app/components/base/slider/style.css @@ -6,12 +6,13 @@ } .slider-thumb { - width: 18px; - height: 18px; + width: 16px; + height: 16px; background-color: white; border-radius: 50%; + border: 1px solid rgba(0, 0, 0, 0.08); position: absolute; - top: -9px; + top: -8px; box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.08); cursor: pointer; diff --git a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/index.tsx b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/index.tsx index 196d5a1f1cd775..932b4b0a2c140a 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/index.tsx @@ -20,6 +20,7 @@ import { AlertTriangle } from '@/app/components/base/icons/src/vender/line/alert import { useProviderContext } from '@/context/provider-context' import { CubeOutline } from '@/app/components/base/icons/src/vender/line/shapes' import { fetchModelParameterRules } from '@/service/common' +import Loading from '@/app/components/base/loading' type ModelParameterModalProps = { isAdvancedMode: boolean @@ -43,7 +44,7 @@ const ModelParameterModal: FC = ({ }) => { const { t } = useTranslation() const [open, setOpen] = useState(false) - const { data: parameterRulesData } = 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 { textGenerationModelList } = useProviderContext() const currentProvider = textGenerationModelList.find(model => model.provider === provider) const currentModel = currentProvider?.models.find(modelItem => modelItem.model === modelId) @@ -141,6 +142,11 @@ const ModelParameterModal: FC = ({ />
+ { + isLoading && ( + + ) + } { parameterRulesData?.data && ( parameterRulesData.data.map(parameter => ( From 4f0821f3a46ae7ebfce058050f952b6d1ec7e429 Mon Sep 17 00:00:00 2001 From: StyleZhang Date: Tue, 26 Dec 2023 11:42:26 +0800 Subject: [PATCH 14/46] remove moderation --- .../model-provider-page/model-modal/index.tsx | 5 +---- .../system-model-selector/index.tsx | 16 +--------------- web/context/provider-context.tsx | 13 ------------- web/i18n/lang/common.en.ts | 4 ---- web/i18n/lang/common.zh.ts | 4 ---- 5 files changed, 2 insertions(+), 40 deletions(-) diff --git a/web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx b/web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx index 10a3cb23cff254..66f8dc0aabd56b 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx @@ -181,10 +181,7 @@ const ModelModal: FC = ({ return validateCredentials( providerFormSchemaPredefined, provider.provider, - { - ...v, - ...getSecretValues(v), - }, + v, ) }, }) diff --git a/web/app/components/header/account-setting/model-provider-page/system-model-selector/index.tsx b/web/app/components/header/account-setting/model-provider-page/system-model-selector/index.tsx index 0a36f9ad53b916..0123913bb7152d 100644 --- a/web/app/components/header/account-setting/model-provider-page/system-model-selector/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/system-model-selector/index.tsx @@ -29,20 +29,14 @@ const SystemModel: FC = ({ rerankModelList, speech2textDefaultModel, speech2textModelList, - moderationModelList, - moderationDefaultModel, - updateModelList, } = useProviderContext() const [currentTextGenerationDefaultModel, changeCurrentTextGenerationDefaultModel] = useDefaultModelAndModelList(textGenerationDefaultModel, textGenerationModelList) const [currentEmbeddingsDefaultModel, changeCurrentEmbeddingsDefaultModel] = useDefaultModelAndModelList(embeddingsDefaultModel, embeddingsModelList) const [currentRerankDefaultModel, changeCurrentRerankDefaultModel] = useDefaultModelAndModelList(rerankDefaultModel, rerankModelList) const [currentSpeech2textDefaultModel, changeCurrentSpeech2textDefaultModel] = useDefaultModelAndModelList(speech2textDefaultModel, speech2textModelList) - const [currentModerationDefaultModel, changeCurrentModerationDefaultModel] = useDefaultModelAndModelList(moderationDefaultModel, moderationModelList) const [open, setOpen] = useState(false) - const handleChangeDefaultModel = async () => { - } - const handleSave = async () => { + const handleSave = () => { } @@ -164,14 +158,6 @@ const SystemModel: FC = ({
-
- -
) } From 926f31b5e802aff6fa437e2928e5293fe5c71331 Mon Sep 17 00:00:00 2001 From: StyleZhang Date: Tue, 26 Dec 2023 15:55:31 +0800 Subject: [PATCH 16/46] fix: content moderation --- .../moderation/moderation-setting-modal.tsx | 10 ++- .../model-provider-page/index.tsx | 10 ++- .../model-provider-page/model-icon/index.tsx | 22 ----- .../system-model-selector/index.tsx | 82 +++++++++++++++---- 4 files changed, 79 insertions(+), 45 deletions(-) diff --git a/web/app/components/app/configuration/toolbox/moderation/moderation-setting-modal.tsx b/web/app/components/app/configuration/toolbox/moderation/moderation-setting-modal.tsx index 072339751f3a71..2522bd492f20a2 100644 --- a/web/app/components/app/configuration/toolbox/moderation/moderation-setting-modal.tsx +++ b/web/app/components/app/configuration/toolbox/moderation/moderation-setting-modal.tsx @@ -19,6 +19,7 @@ import type { CodeBasedExtensionItem } from '@/models/common' import I18n from '@/context/i18n' import { InfoCircle } from '@/app/components/base/icons/src/vender/line/general' import { useModalContext } from '@/context/modal-context' +import { CustomConfigurationStatusEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' const systemTypes = ['openai_moderation', 'keywords', 'api'] @@ -57,10 +58,11 @@ const ModerationSettingModal: FC = ({ '/code-based-extension?module=moderation', fetchCodeBasedExtensionList, ) - const systemOpenaiProvider = modelProviders?.openai.providers.find(item => item.provider_type === 'system') - const systemOpenaiProviderCanUse = systemOpenaiProvider && (((systemOpenaiProvider as any).quota_limit - (systemOpenaiProvider as any).quota_used) > 0) - const customOpenaiProviders = modelProviders?.openai.providers.filter(item => item.provider_type !== 'system') - const customOpenaiProvidersCanUse = customOpenaiProviders?.some(item => item.is_valid) + const openaiProvider = modelProviders?.data.find(item => item.provider === 'openai') + const systemOpenaiProviderEnabled = openaiProvider?.system_configuration.enabled + const systemOpenaiProviderQuota = systemOpenaiProviderEnabled ? openaiProvider?.system_configuration.quota_configurations.find(item => item.quota_type === openaiProvider.system_configuration.current_quota_type) : undefined + const systemOpenaiProviderCanUse = systemOpenaiProviderQuota?.is_valid + const customOpenaiProvidersCanUse = openaiProvider?.custom_configuration.status === CustomConfigurationStatusEnum.active const openaiProviderConfiged = customOpenaiProvidersCanUse || systemOpenaiProviderCanUse const providers: Provider[] = [ { diff --git a/web/app/components/header/account-setting/model-provider-page/index.tsx b/web/app/components/header/account-setting/model-provider-page/index.tsx index 4e5951ded2f163..2018e471a11bea 100644 --- a/web/app/components/header/account-setting/model-provider-page/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/index.tsx @@ -14,6 +14,7 @@ import { CustomConfigurationStatusEnum } from './declarations' import { fetchModelProviders } from '@/service/common' import { useProviderContext } from '@/context/provider-context' import { AlertTriangle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback' +import Loading from '@/app/components/base/loading' const ModelProviderPage = () => { const { t } = useTranslation() @@ -27,7 +28,7 @@ const ModelProviderPage = () => { const [currentProvider, setCurrentProvider] = useState(null) const [currentConfigurateMethod, setCurrentConfigurateMethod] = useState(null) const [currentCustomConfigrationModelFixedFields, setCurrentCustomConfigrationModelFixedFields] = useState(undefined) - const { data: providersData, mutate: mutateProviders } = useSWR('/workspaces/current/model-providers', fetchModelProviders) + const { data: providersData, mutate: mutateProviders, isLoading } = useSWR('/workspaces/current/model-providers', fetchModelProviders) const defaultModelNotConfigured = !textGenerationDefaultModel && !embeddingsDefaultModel && !speech2textDefaultModel && !rerankDefaultModel const providers = useMemo(() => { return providersData ? providersData.data : [] @@ -84,6 +85,13 @@ const ModelProviderPage = () => { } mutateProviders()} />
+ { + isLoading && ( +
+ +
+ ) + } { !!configedProviders?.length && (
diff --git a/web/app/components/header/account-setting/model-provider-page/model-icon/index.tsx b/web/app/components/header/account-setting/model-provider-page/model-icon/index.tsx index 544444710f2781..30638b4217959f 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-icon/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-icon/index.tsx @@ -91,20 +91,6 @@ export const ModelIconSpeechToText: FC = ({ ) } -export const ModelIconModeration: FC = ({ - providerName, - className, -}) => { - const { moderationModelList } = useProviderContext() - const provider = moderationModelList.find(item => item.provider === providerName) - return ( - - ) -} - type ModelIconProps = { modelType: ModelTypeEnum } & ModelIconSubProps @@ -145,14 +131,6 @@ const ModelIcon: FC = ({ /> ) } - if (modelType === ModelTypeEnum.moderation) { - return ( - - ) - } return ( void @@ -20,6 +24,7 @@ const SystemModel: FC = ({ onUpdate, }) => { const { t } = useTranslation() + const { notify } = useToastContext() const { textGenerationDefaultModel, textGenerationModelList, @@ -29,15 +34,69 @@ const SystemModel: FC = ({ rerankModelList, speech2textDefaultModel, speech2textModelList, + updateModelList, } = useProviderContext() + const [changedModelTypes, setChangedModelTypes] = useState([]) const [currentTextGenerationDefaultModel, changeCurrentTextGenerationDefaultModel] = useDefaultModelAndModelList(textGenerationDefaultModel, textGenerationModelList) const [currentEmbeddingsDefaultModel, changeCurrentEmbeddingsDefaultModel] = useDefaultModelAndModelList(embeddingsDefaultModel, embeddingsModelList) const [currentRerankDefaultModel, changeCurrentRerankDefaultModel] = useDefaultModelAndModelList(rerankDefaultModel, rerankModelList) const [currentSpeech2textDefaultModel, changeCurrentSpeech2textDefaultModel] = useDefaultModelAndModelList(speech2textDefaultModel, speech2textModelList) const [open, setOpen] = useState(false) - const handleSave = () => { + const getCurrentDefaultModelByModelType = (modelType: ModelTypeEnum) => { + if (modelType === ModelTypeEnum.textGeneration) + return currentTextGenerationDefaultModel + else if (modelType === ModelTypeEnum.textEmbedding) + return currentEmbeddingsDefaultModel + else if (modelType === ModelTypeEnum.rerank) + return currentRerankDefaultModel + else if (modelType === ModelTypeEnum.speech2text) + return currentSpeech2textDefaultModel + return undefined + } + const handleChangeDefaultModel = (modelType: ModelTypeEnum, model: DefaultModel) => { + if (modelType === ModelTypeEnum.textGeneration) + changeCurrentTextGenerationDefaultModel(model) + else if (modelType === ModelTypeEnum.textEmbedding) + changeCurrentEmbeddingsDefaultModel(model) + else if (modelType === ModelTypeEnum.rerank) + changeCurrentRerankDefaultModel(model) + else if (modelType === ModelTypeEnum.speech2text) + changeCurrentSpeech2textDefaultModel(model) + + if (!changedModelTypes.includes(modelType)) + setChangedModelTypes([...changedModelTypes, modelType]) + } + const handleSave = async () => { + const res = await updateDefaultModel({ + url: '/workspaces/current/default-model', + body: { + model_settings: [ModelTypeEnum.textGeneration, ModelTypeEnum.textEmbedding, ModelTypeEnum.rerank, ModelTypeEnum.speech2text].map((modelType) => { + return { + model_type: modelType, + provider_name: getCurrentDefaultModelByModelType(modelType)?.provider, + model_name: getCurrentDefaultModelByModelType(modelType)?.model, + } + }), + }, + }) + if (res.result === 'success') { + notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') }) + setOpen(false) + onUpdate() + + changedModelTypes.forEach((modelType) => { + if (modelType === ModelTypeEnum.textGeneration) + updateModelList(modelType) + else if (modelType === ModelTypeEnum.textEmbedding) + updateModelList(modelType) + else if (modelType === ModelTypeEnum.rerank) + updateModelList(modelType) + else if (modelType === ModelTypeEnum.speech2text) + updateModelList(modelType) + }) + } } return ( @@ -78,7 +137,7 @@ const SystemModel: FC = ({ handleChangeDefaultModel(ModelTypeEnum.textGeneration, model)} popupClassName='z-[60]' />
@@ -99,7 +158,7 @@ const SystemModel: FC = ({ handleChangeDefaultModel(ModelTypeEnum.textEmbedding, model)} popupClassName='z-[60]' />
@@ -120,7 +179,7 @@ const SystemModel: FC = ({ handleChangeDefaultModel(ModelTypeEnum.rerank, model)} popupClassName='z-[60]' />
@@ -141,24 +200,11 @@ const SystemModel: FC = ({ handleChangeDefaultModel(ModelTypeEnum.speech2text, model)} popupClassName='z-[60]' />
-
-
- {t('common.modelProvider.moderationModel.key')} - {t('common.modelProvider.moderationModel.tip')}
- } - > - - -
-
{hasEnableParams && ( diff --git a/web/app/components/app/configuration/config/index.tsx b/web/app/components/app/configuration/config/index.tsx index 0e75d7b11ac1d1..b0d1e236a47ab9 100644 --- a/web/app/components/app/configuration/config/index.tsx +++ b/web/app/components/app/configuration/config/index.tsx @@ -21,10 +21,10 @@ import ConfigPrompt from '@/app/components/app/configuration/config-prompt' import ConfigVar from '@/app/components/app/configuration/config-var' import type { CitationConfig, ModelConfig, ModerationConfig, MoreLikeThisConfig, PromptVariable, SpeechToTextConfig, SuggestedQuestionsAfterAnswerConfig } from '@/models/debug' import { AppType, ModelModeType } from '@/types/app' -import { useProviderContext } from '@/context/provider-context' import { useModalContext } from '@/context/modal-context' import ConfigParamModal from '@/app/components/app/configuration/toolbox/annotation/config-param-modal' import AnnotationFullModal from '@/app/components/billing/annotation-full/modal' +import { useDefaultModel } from '@/app/components/header/account-setting/model-provider-page/hooks' const Config: FC = () => { const { @@ -55,7 +55,7 @@ const Config: FC = () => { setModerationConfig, } = useContext(ConfigContext) const isChatApp = mode === AppType.chat - const { speech2textDefaultModel } = useProviderContext() + const { data: speech2textDefaultModel } = useDefaultModel(4) const { setShowModerationSettingModal } = useModalContext() const promptTemplate = modelConfig.configs.prompt_template diff --git a/web/app/components/app/configuration/dataset-config/params-config/index.tsx b/web/app/components/app/configuration/dataset-config/params-config/index.tsx index 0e5eafaafe46bc..dcf22134ca3f60 100644 --- a/web/app/components/app/configuration/dataset-config/params-config/index.tsx +++ b/web/app/components/app/configuration/dataset-config/params-config/index.tsx @@ -12,15 +12,14 @@ import Modal from '@/app/components/base/modal' import Button from '@/app/components/base/button' import RadioCard from '@/app/components/base/radio-card/simple' import { RETRIEVE_TYPE } from '@/types/app' -// import ModelSelector from '@/app/components/header/account-setting/model-page/model-selector' -import { useProviderContext } from '@/context/provider-context' -// import { ModelType } from '@/app/components/header/account-setting/model-page/declarations' import Toast from '@/app/components/base/toast' import { DATASET_DEFAULT } from '@/config' import { MultiPathRetrieval, NTo1Retrieval, } from '@/app/components/base/icons/src/public/common' +import ModelSelector from '@/app/components/header/account-setting/model-provider-page/model-selector' +import { useModelListAndDefaultModelAndCurrentProviderAndModel } from '@/app/components/header/account-setting/model-provider-page/hooks' const ParamsConfig: FC = () => { const { t } = useTranslation() @@ -38,11 +37,11 @@ const ParamsConfig: FC = () => { retrieval_model: value, }) } - const { - rerankDefaultModel, - isRerankDefaultModelVaild, - } = useProviderContext() + modelList: rerankModelList, + defaultModel: rerankDefaultModel, + currentModel: isRerankDefaultModelVaild, + } = useModelListAndDefaultModelAndCurrentProviderAndModel(3) const rerankModel = (() => { if (tempDataSetConfigs.reranking_model) { @@ -53,7 +52,7 @@ const ParamsConfig: FC = () => { } else if (rerankDefaultModel) { return { - provider_name: rerankDefaultModel.provider, + provider_name: rerankDefaultModel.provider.provider, model_name: rerankDefaultModel.model, } } @@ -162,20 +161,19 @@ const ParamsConfig: FC = () => {
{t('common.modelProvider.rerankModel.key')}
- {/* { + { setTempDataSetConfigs({ ...tempDataSetConfigs, reranking_model: { - reranking_provider_name: v.model_provider.provider_name, - reranking_model_name: v.model_name, + reranking_provider_name: v.provider, + reranking_model_name: v.model, }, }) }} - /> */} + modelList={rerankModelList} + />
diff --git a/web/app/components/app/configuration/dataset-config/settings-modal/index.tsx b/web/app/components/app/configuration/dataset-config/settings-modal/index.tsx index 2c29e8af5b882a..d94cc632c3bd2f 100644 --- a/web/app/components/app/configuration/dataset-config/settings-modal/index.tsx +++ b/web/app/components/app/configuration/dataset-config/settings-modal/index.tsx @@ -6,9 +6,6 @@ import cn from 'classnames' import { BookOpenIcon } from '@heroicons/react/24/outline' import IndexMethodRadio from '@/app/components/datasets/settings/index-method-radio' import Button from '@/app/components/base/button' -// import ModelSelector from '@/app/components/header/account-setting/model-page/model-selector' -// import type { ProviderEnum } from '@/app/components/header/account-setting/model-page/declarations' -// import { ModelType } from '@/app/components/header/account-setting/model-page/declarations' import type { DataSet } from '@/models/datasets' import { useToastContext } from '@/app/components/base/toast' import { updateDatasetSetting } from '@/service/datasets' @@ -17,10 +14,14 @@ import { XClose } from '@/app/components/base/icons/src/vender/line/general' import type { RetrievalConfig } from '@/types/app' import RetrievalMethodConfig from '@/app/components/datasets/common/retrieval-method-config' import EconomicalRetrievalMethodConfig from '@/app/components/datasets/common/economical-retrieval-method-config' -import { useProviderContext } from '@/context/provider-context' import { ensureRerankModelSelected, isReRankModelSelected } from '@/app/components/datasets/common/check-rerank-model' import { AlertTriangle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback' import PermissionsRadio from '@/app/components/datasets/settings/permissions-radio' +import ModelSelector from '@/app/components/header/account-setting/model-provider-page/model-selector' +import { + useModelList, + useModelListAndDefaultModelAndCurrentProviderAndModel, +} from '@/app/components/header/account-setting/model-provider-page/hooks' type SettingsModalProps = { currentDataset: DataSet @@ -41,6 +42,12 @@ const SettingsModal: FC = ({ onCancel, onSave, }) => { + const { data: embeddingsModelList } = useModelList(2) + const { + modelList: rerankModelList, + defaultModel: rerankDefaultModel, + currentModel: isRerankDefaultModelVaild, + } = useModelListAndDefaultModelAndCurrentProviderAndModel(3) const { t } = useTranslation() const { notify } = useToastContext() const ref = useRef(null) @@ -51,12 +58,6 @@ const SettingsModal: FC = ({ const [indexMethod, setIndexMethod] = useState(currentDataset.indexing_technique) const [retrievalConfig, setRetrievalConfig] = useState(localeCurrentDataset?.retrieval_model_dict as RetrievalConfig) - const { - rerankDefaultModel, - isRerankDefaultModelVaild, - rerankModelList, - } = useProviderContext() - const handleValueChange = (type: string, value: string) => { setLocaleCurrentDataset({ ...localeCurrentDataset, [type]: value }) } @@ -73,7 +74,7 @@ const SettingsModal: FC = ({ if ( !isReRankModelSelected({ rerankDefaultModel, - isRerankDefaultModelVaild, + isRerankDefaultModelVaild: !!isRerankDefaultModelVaild, rerankModelList, retrievalConfig, indexMethod, @@ -202,15 +203,14 @@ const SettingsModal: FC = ({
- {/* {}} - /> */} + modelList={embeddingsModelList} + />
{t('datasetSettings.form.embeddingModelTip')} diff --git a/web/app/components/app/configuration/debug/index.tsx b/web/app/components/app/configuration/debug/index.tsx index aa6224982eeb19..14b20de64171d1 100644 --- a/web/app/components/app/configuration/debug/index.tsx +++ b/web/app/components/app/configuration/debug/index.tsx @@ -24,10 +24,11 @@ import type { ModelConfig as BackendModelConfig, VisionFile } from '@/types/app' import { promptVariablesToUserInputsForm } from '@/utils/model-config' import TextGeneration from '@/app/components/app/text-generate/item' import { IS_CE_EDITION } from '@/config' -import { useProviderContext } from '@/context/provider-context' import type { Inputs } from '@/models/debug' import { fetchFileUploadConfig } from '@/service/common' import type { Annotation as AnnotationType } from '@/models/log' +import { useDefaultModel } from '@/app/components/header/account-setting/model-provider-page/hooks' + type IDebug = { hasSetAPIKEY: boolean onSetting: () => void @@ -69,7 +70,7 @@ const Debug: FC = ({ visionConfig, annotationConfig, } = useContext(ConfigContext) - const { speech2textDefaultModel } = useProviderContext() + const { data: speech2textDefaultModel } = useDefaultModel(4) const [chatList, setChatList, getChatList] = useGetState([]) const chatListDomRef = useRef(null) const { data: fileUploadConfigResponse } = useSWR({ url: '/files/upload' }, fetchFileUploadConfig) diff --git a/web/app/components/app/configuration/index.tsx b/web/app/components/app/configuration/index.tsx index aeb52a009edef5..1eaef6f6b92b62 100644 --- a/web/app/components/app/configuration/index.tsx +++ b/web/app/components/app/configuration/index.tsx @@ -49,6 +49,7 @@ import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' import Drawer from '@/app/components/base/drawer' import ModelParameterModal from '@/app/components/header/account-setting/model-provider-page/model-parameter-modal' import type { FormValue } from '@/app/components/header/account-setting/model-provider-page/declarations' +import { useCurrentProviderAndModel } from '@/app/components/header/account-setting/model-provider-page/hooks' type PublichConfig = { modelConfig: ModelConfig @@ -221,7 +222,13 @@ const Configuration: FC = () => { hasSettedApiKey, textGenerationModelList, } = useProviderContext() - const currModel = textGenerationModelList.find(item => item.provider === modelConfig.provider)?.models.find(model => model.model === modelConfig.model_id) + const { currentModel: currModel } = useCurrentProviderAndModel( + { + provider: modelConfig.provider, + model: modelConfig.model_id, + }, + textGenerationModelList, + ) // Fill old app data missing model mode. useEffect(() => { diff --git a/web/app/components/app/configuration/toolbox/annotation/config-param-modal.tsx b/web/app/components/app/configuration/toolbox/annotation/config-param-modal.tsx index 40b3ef588c9414..c4f449628edc0f 100644 --- a/web/app/components/app/configuration/toolbox/annotation/config-param-modal.tsx +++ b/web/app/components/app/configuration/toolbox/annotation/config-param-modal.tsx @@ -6,11 +6,11 @@ import ScoreSlider from '../score-slider' import { Item } from './config-param' import Modal from '@/app/components/base/modal' import Button from '@/app/components/base/button' -// import ModelSelector from '@/app/components/header/account-setting/model-page/model-selector/portal-select' -import { useProviderContext } from '@/context/provider-context' import Toast from '@/app/components/base/toast' import type { AnnotationReplyConfig } from '@/models/debug' import { ANNOTATION_DEFAULT } from '@/config' +import ModelSelector from '@/app/components/header/account-setting/model-provider-page/model-selector' +import { useModelListAndDefaultModelAndCurrentProviderAndModel } from '@/app/components/header/account-setting/model-provider-page/hooks' type Props = { appId: string @@ -33,9 +33,10 @@ const ConfigParamModal: FC = ({ }) => { const { t } = useTranslation() const { - embeddingsDefaultModel, - isEmbeddingsDefaultModelValid, - } = useProviderContext() + modelList: embeddingsModelList, + defaultModel: embeddingsDefaultModel, + currentModel: isEmbeddingsDefaultModelValid, + } = useModelListAndDefaultModelAndCurrentProviderAndModel(2) const [annotationConfig, setAnnotationConfig] = useState(oldAnnotationConfig) const [isLoading, setLoading] = useState(false) @@ -104,17 +105,19 @@ const ConfigParamModal: FC = ({ tooltip={t('appAnnotation.embeddingModelSwitchTip')} >
- {/* { + { setEmbeddingModel({ - providerName: val.model_provider.provider_name, - modelName: val.model_name, + providerName: val.provider, + modelName: val.model, }) }} - /> */} + />
diff --git a/web/app/components/datasets/common/check-rerank-model.ts b/web/app/components/datasets/common/check-rerank-model.ts index 615bbdc3f3e298..f4baaac5c88093 100644 --- a/web/app/components/datasets/common/check-rerank-model.ts +++ b/web/app/components/datasets/common/check-rerank-model.ts @@ -1,5 +1,8 @@ -import type { BackendModel } from '../../header/account-setting/model-page/declarations' import { RETRIEVE_METHOD, type RetrievalConfig } from '@/types/app' +import type { + DefaultModelResponse, + Model, +} from '@/app/components/header/account-setting/model-provider-page/declarations' export const isReRankModelSelected = ({ rerankDefaultModel, @@ -8,15 +11,18 @@ export const isReRankModelSelected = ({ rerankModelList, indexMethod, }: { - rerankDefaultModel?: BackendModel + rerankDefaultModel?: DefaultModelResponse isRerankDefaultModelVaild: boolean retrievalConfig: RetrievalConfig - rerankModelList: BackendModel[] + rerankModelList: Model[] indexMethod?: string }) => { const rerankModelSelected = (() => { - if (retrievalConfig.reranking_model?.reranking_model_name) - return !!rerankModelList.find(({ model_name }) => model_name === retrievalConfig.reranking_model?.reranking_model_name) + if (retrievalConfig.reranking_model?.reranking_model_name) { + const provider = rerankModelList.find(({ provider }) => provider === retrievalConfig.reranking_model?.reranking_provider_name) + + return provider?.models.find(({ model }) => model === retrievalConfig.reranking_model?.reranking_model_name) + } if (isRerankDefaultModelVaild) return !!rerankDefaultModel @@ -39,7 +45,7 @@ export const ensureRerankModelSelected = ({ indexMethod, retrievalConfig, }: { - rerankDefaultModel: BackendModel + rerankDefaultModel: DefaultModelResponse retrievalConfig: RetrievalConfig indexMethod?: string }) => { @@ -52,8 +58,8 @@ export const ensureRerankModelSelected = ({ return { ...retrievalConfig, reranking_model: { - reranking_provider_name: rerankDefaultModel.model_provider.provider_name, - reranking_model_name: rerankDefaultModel.model_name, + reranking_provider_name: rerankDefaultModel.provider.provider, + reranking_model_name: rerankDefaultModel.model, }, } } diff --git a/web/app/components/datasets/common/retrieval-param-config/index.tsx b/web/app/components/datasets/common/retrieval-param-config/index.tsx index 7a4feac8ba8aa1..be4b53b462697a 100644 --- a/web/app/components/datasets/common/retrieval-param-config/index.tsx +++ b/web/app/components/datasets/common/retrieval-param-config/index.tsx @@ -9,10 +9,9 @@ import { RETRIEVE_METHOD } from '@/types/app' import Switch from '@/app/components/base/switch' import Tooltip from '@/app/components/base/tooltip-plus' import { HelpCircle } from '@/app/components/base/icons/src/vender/line/general' -import ModelSelector from '@/app/components/header/account-setting/model-page/model-selector' -import { ModelType } from '@/app/components/header/account-setting/model-page/declarations' import type { RetrievalConfig } from '@/types/app' -import { useProviderContext } from '@/context/provider-context' +import ModelSelector from '@/app/components/header/account-setting/model-provider-page/model-selector' +import { useModelListAndDefaultModel } from '@/app/components/header/account-setting/model-provider-page/hooks' type Props = { type: RETRIEVE_METHOD @@ -29,8 +28,9 @@ const RetrievalParamConfig: FC = ({ const canToggleRerankModalEnable = type !== RETRIEVE_METHOD.hybrid const isEconomical = type === RETRIEVE_METHOD.invertedIndex const { - rerankDefaultModel, - } = useProviderContext() + defaultModel: rerankDefaultModel, + modelList: rerankModelList, + } = useModelListAndDefaultModel(3) const rerankModel = (() => { if (value.reranking_model) { @@ -41,8 +41,8 @@ const RetrievalParamConfig: FC = ({ } else if (rerankDefaultModel) { return { - provider_name: rerankDefaultModel.model_provider.provider_name, - model_name: rerankDefaultModel.model_name, + provider_name: rerankDefaultModel.provider.provider, + model_name: rerankDefaultModel.model, } } })() @@ -73,17 +73,15 @@ const RetrievalParamConfig: FC = ({
{ + onSelect={(v) => { onChange({ ...value, reranking_model: { - reranking_provider_name: v.model_provider.provider_name, - reranking_model_name: v.model_name, + reranking_provider_name: v.provider, + reranking_model_name: v.model, }, }) }} diff --git a/web/app/components/datasets/settings/form/index.tsx b/web/app/components/datasets/settings/form/index.tsx index b2904e432a3b72..b6dc4a5eafb07b 100644 --- a/web/app/components/datasets/settings/form/index.tsx +++ b/web/app/components/datasets/settings/form/index.tsx @@ -15,14 +15,16 @@ import { ToastContext } from '@/app/components/base/toast' import Button from '@/app/components/base/button' import { updateDatasetSetting } from '@/service/datasets' import type { DataSet, DataSetListResponse } from '@/models/datasets' -import ModelSelector from '@/app/components/header/account-setting/model-page/model-selector' -import type { ProviderEnum } from '@/app/components/header/account-setting/model-page/declarations' -import { ModelType } from '@/app/components/header/account-setting/model-page/declarations' import DatasetDetailContext from '@/context/dataset-detail' import { type RetrievalConfig } from '@/types/app' import { useModalContext } from '@/context/modal-context' -import { useProviderContext } from '@/context/provider-context' import { ensureRerankModelSelected, isReRankModelSelected } from '@/app/components/datasets/common/check-rerank-model' +import ModelSelector from '@/app/components/header/account-setting/model-provider-page/model-selector' +import { + useModelList, + useModelListAndDefaultModelAndCurrentProviderAndModel, +} from '@/app/components/header/account-setting/model-provider-page/hooks' + const rowClass = ` flex justify-between py-4 flex-wrap gap-y-2 ` @@ -56,11 +58,13 @@ const Form = () => { const [permission, setPermission] = useState(currentDataset?.permission) const [indexMethod, setIndexMethod] = useState(currentDataset?.indexing_technique) const [retrievalConfig, setRetrievalConfig] = useState(currentDataset?.retrieval_model_dict as RetrievalConfig) + const { - rerankDefaultModel, - isRerankDefaultModelVaild, - rerankModelList, - } = useProviderContext() + modelList: rerankModelList, + defaultModel: rerankDefaultModel, + currentModel: isRerankDefaultModelVaild, + } = useModelListAndDefaultModelAndCurrentProviderAndModel(3) + const { data: embeddingModelList } = useModelList(2) const handleSave = async () => { if (loading) @@ -72,7 +76,7 @@ const Form = () => { if ( !isReRankModelSelected({ rerankDefaultModel, - isRerankDefaultModelVaild, + isRerankDefaultModelVaild: !!isRerankDefaultModelVaild, rerankModelList, retrievalConfig, indexMethod, @@ -186,12 +190,11 @@ const Form = () => {
{}} + modelList={embeddingModelList} />
diff --git a/web/app/components/header/account-setting/model-provider-page/hooks.ts b/web/app/components/header/account-setting/model-provider-page/hooks.ts index 3ede94963bd462..55de1fbf30d3a9 100644 --- a/web/app/components/header/account-setting/model-provider-page/hooks.ts +++ b/web/app/components/header/account-setting/model-provider-page/hooks.ts @@ -12,10 +12,17 @@ import type { DefaultModelResponse, Model, } from './declarations' -import { ConfigurateMethodEnum } from './declarations' +import { + ConfigurateMethodEnum, + ModelTypeEnum, +} from './declarations' import { languageMaps } from './utils' import I18n from '@/context/i18n' -import { fetchModelProviderCredentials } from '@/service/common' +import { + fetchDefaultModal, + fetchModelList, + fetchModelProviderCredentials, +} from '@/service/common' type UseDefaultModelAndModelList = ( defaultModel: DefaultModelResponse | undefined, @@ -80,3 +87,66 @@ export const useProviderCrenditialsFormSchemasValue = ( } : undefined } + +export type ModelTypeIndex = 1 | 2 | 3 | 4 +export const MODEL_TYPE_MAPS = { + 1: ModelTypeEnum.textGeneration, + 2: ModelTypeEnum.textEmbedding, + 3: ModelTypeEnum.rerank, + 4: ModelTypeEnum.speech2text, +} + +export const useModelList = (type: ModelTypeIndex) => { + const { data, mutate, isLoading } = useSWR(`/workspaces/current/models/model-types/${MODEL_TYPE_MAPS[type]}`, fetchModelList) + + return { + data: data?.data || [], + mutate, + isLoading, + } +} + +export const useDefaultModel = (type: ModelTypeIndex) => { + const { data, mutate, isLoading } = useSWR(`/workspaces/current/default-model?model_type=${MODEL_TYPE_MAPS[type]}`, fetchDefaultModal) + + return { + data: data?.data, + mutate, + isLoading, + } +} + +export const useCurrentProviderAndModel = (defaultModel: DefaultModel, modelList: Model[]) => { + const currentProvider = modelList.find(provider => provider.provider === defaultModel?.provider) + const currentModel = currentProvider?.models.find(model => model.model === defaultModel?.model) + + return { + currentProvider, + currentModel, + } +} + +export const useModelListAndDefaultModel = (type: ModelTypeIndex) => { + const { data: modelList } = useModelList(type) + const { data: defaultModel } = useDefaultModel(type) + + return { + modelList, + defaultModel, + } +} + +export const useModelListAndDefaultModelAndCurrentProviderAndModel = (type: ModelTypeIndex) => { + const { modelList, defaultModel } = useModelListAndDefaultModel(type) + const { currentProvider, currentModel } = useCurrentProviderAndModel( + { provider: defaultModel?.provider.provider || '', model: defaultModel?.model || '' }, + modelList, + ) + + return { + modelList, + defaultModel, + currentProvider, + currentModel, + } +} diff --git a/web/app/components/header/account-setting/model-provider-page/model-selector/index.tsx b/web/app/components/header/account-setting/model-provider-page/model-selector/index.tsx index 272159ed4d1530..55a69e76b31f37 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-selector/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-selector/index.tsx @@ -18,7 +18,8 @@ type ModelSelectorProps = { defaultModel?: DefaultModel modelList: Model[] popupClassName?: string - onSelect: (model: DefaultModel) => void + onSelect?: (model: DefaultModel) => void + readonly?: boolean } const ModelSelector: FC = ({ defaultModel, @@ -32,7 +33,9 @@ const ModelSelector: FC = ({ const handleSelect = (provider: string, model: ModelItem) => { setOpen(false) - onSelect({ provider, model: model.model }) + + if (onSelect) + onSelect({ provider, model: model.model }) } return ( @@ -64,7 +67,7 @@ const ModelSelector: FC = ({ ) } - + = ({ defaultModel={currentTextGenerationDefaultModel} modelList={textGenerationModelList} onSelect={model => handleChangeDefaultModel(ModelTypeEnum.textGeneration, model)} - popupClassName='z-[60]' />
@@ -159,7 +158,6 @@ const SystemModel: FC = ({ defaultModel={currentEmbeddingsDefaultModel} modelList={embeddingsModelList} onSelect={model => handleChangeDefaultModel(ModelTypeEnum.textEmbedding, model)} - popupClassName='z-[60]' />
@@ -180,7 +178,6 @@ const SystemModel: FC = ({ defaultModel={currentRerankDefaultModel} modelList={rerankModelList} onSelect={model => handleChangeDefaultModel(ModelTypeEnum.rerank, model)} - popupClassName='z-[60]' />
@@ -201,7 +198,6 @@ const SystemModel: FC = ({ defaultModel={currentSpeech2textDefaultModel} modelList={speech2textModelList} onSelect={model => handleChangeDefaultModel(ModelTypeEnum.speech2text, model)} - popupClassName='z-[60]' />
From b3331891ded1b1d6653b7a4820177ce49bf39ce5 Mon Sep 17 00:00:00 2001 From: StyleZhang Date: Tue, 26 Dec 2023 19:49:26 +0800 Subject: [PATCH 18/46] hooks --- .../app/configuration/config-model/index.tsx | 14 +- .../components/app/configuration/index.tsx | 2 +- .../common/retrieval-method-config/index.tsx | 8 +- web/app/components/datasets/create/index.tsx | 4 +- .../datasets/create/step-two/index.tsx | 16 +-- .../documents/detail/settings/index.tsx | 4 +- .../hit-testing/modify-retrieval-modal.tsx | 12 +- .../model-provider-page/hooks.ts | 19 ++- .../model-provider-page/index.tsx | 25 ++-- .../model-provider-page/model-icon/index.tsx | 121 +---------------- .../model-parameter-modal/index.tsx | 14 +- .../model-selector/index.tsx | 12 +- .../model-selector/model-trigger.tsx | 10 +- .../model-selector/popup-item.tsx | 3 +- .../provider-added-card/model-list.tsx | 3 +- .../system-model-selector/index.tsx | 45 ++++--- web/context/provider-context.tsx | 127 ++++-------------- 17 files changed, 150 insertions(+), 289 deletions(-) diff --git a/web/app/components/app/configuration/config-model/index.tsx b/web/app/components/app/configuration/config-model/index.tsx index 752da1cc3d6137..a64a33191839d4 100644 --- a/web/app/components/app/configuration/config-model/index.tsx +++ b/web/app/components/app/configuration/config-model/index.tsx @@ -26,8 +26,8 @@ import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' import type { ModelModeType } from '@/types/app' import ModelIcon from '@/app/components/header/account-setting/model-provider-page/model-icon' import ModelName from '@/app/components/header/account-setting/model-provider-page/model-name' -import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' import ModelSelector from '@/app/components/header/account-setting/model-provider-page/model-selector' +import { useCurrentProviderAndModel } from '@/app/components/header/account-setting/model-provider-page/hooks' export type IConfigModelProps = { isAdvancedMode: boolean @@ -54,8 +54,13 @@ const ConfigModel: FC = ({ const [isShowConfig, { setFalse: hideConfig, toggle: toogleShowConfig }] = useBoolean(false) const [maxTokenSettingTipVisible, setMaxTokenSettingTipVisible] = useState(false) const configContentRef = React.useRef(null) - const currentProvider = textGenerationModelList.find(model => model.provider === provider) - const currModel = currentProvider?.models.find(modelItem => modelItem.model === modelId) + const { + currentProvider, + currentModel: currModel, + } = useCurrentProviderAndModel( + textGenerationModelList, + { provider, model: modelId }, + ) const media = useBreakpoints() const isMobile = media === MediaType.mobile @@ -285,8 +290,7 @@ const ConfigModel: FC = ({ currentProvider && ( ) } diff --git a/web/app/components/app/configuration/index.tsx b/web/app/components/app/configuration/index.tsx index 1eaef6f6b92b62..272852a4bca35f 100644 --- a/web/app/components/app/configuration/index.tsx +++ b/web/app/components/app/configuration/index.tsx @@ -223,11 +223,11 @@ const Configuration: FC = () => { textGenerationModelList, } = useProviderContext() const { currentModel: currModel } = useCurrentProviderAndModel( + textGenerationModelList, { provider: modelConfig.provider, model: modelConfig.model_id, }, - textGenerationModelList, ) // Fill old app data missing model mode. diff --git a/web/app/components/datasets/common/retrieval-method-config/index.tsx b/web/app/components/datasets/common/retrieval-method-config/index.tsx index b49cea2b349eef..b9af89ea1fbcc0 100644 --- a/web/app/components/datasets/common/retrieval-method-config/index.tsx +++ b/web/app/components/datasets/common/retrieval-method-config/index.tsx @@ -9,6 +9,7 @@ import RadioCard from '@/app/components/base/radio-card' import { PatternRecognition, Semantic } from '@/app/components/base/icons/src/vender/solid/development' import { FileSearch02 } from '@/app/components/base/icons/src/vender/solid/files' import { useProviderContext } from '@/context/provider-context' +import { useDefaultModel } from '@/app/components/header/account-setting/model-provider-page/hooks' type Props = { value: RetrievalConfig @@ -20,14 +21,15 @@ const RetrievalMethodConfig: FC = ({ onChange, }) => { const { t } = useTranslation() - const { supportRetrievalMethods, rerankDefaultModel } = useProviderContext() + const { supportRetrievalMethods } = useProviderContext() + const { data: rerankDefaultModel } = useDefaultModel(3) const value = (() => { if (!passValue.reranking_model.reranking_model_name) { return { ...passValue, reranking_model: { - reranking_provider_name: rerankDefaultModel?.model_provider.provider_name || '', - reranking_model_name: rerankDefaultModel?.model_name || '', + reranking_provider_name: rerankDefaultModel?.provider.provider || '', + reranking_model_name: rerankDefaultModel?.model || '', }, } } diff --git a/web/app/components/datasets/create/index.tsx b/web/app/components/datasets/create/index.tsx index f96dac6762fb96..535a9a4ad21dfc 100644 --- a/web/app/components/datasets/create/index.tsx +++ b/web/app/components/datasets/create/index.tsx @@ -11,8 +11,8 @@ import type { DataSet, FileItem, createDocumentResponse } from '@/models/dataset import { fetchDataSource } from '@/service/common' import { fetchDatasetDetail } from '@/service/datasets' import type { NotionPage } from '@/models/common' -import { useProviderContext } from '@/context/provider-context' import { useModalContext } from '@/context/modal-context' +import { useDefaultModel } from '@/app/components/header/account-setting/model-provider-page/hooks' type DatasetUpdateFormProps = { datasetId?: string @@ -28,7 +28,7 @@ const DatasetUpdateForm = ({ datasetId }: DatasetUpdateFormProps) => { const [fileList, setFiles] = useState([]) const [result, setResult] = useState() const [hasError, setHasError] = useState(false) - const { embeddingsDefaultModel } = useProviderContext() + const { data: embeddingsDefaultModel } = useDefaultModel(2) const [notionPages, setNotionPages] = useState([]) const updateNotionPages = (value: NotionPage[]) => { diff --git a/web/app/components/datasets/create/step-two/index.tsx b/web/app/components/datasets/create/step-two/index.tsx index f9692bc8fae898..9f6d8348f33ef8 100644 --- a/web/app/components/datasets/create/step-two/index.tsx +++ b/web/app/components/datasets/create/step-two/index.tsx @@ -38,9 +38,9 @@ import { useDatasetDetailContext } from '@/context/dataset-detail' import I18n from '@/context/i18n' import { IS_CE_EDITION } from '@/config' import { RETRIEVE_METHOD } from '@/types/app' -import { useProviderContext } from '@/context/provider-context' import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' import Tooltip from '@/app/components/base/tooltip' +import { useModelListAndDefaultModelAndCurrentProviderAndModel } from '@/app/components/header/account-setting/model-provider-page/hooks' type ValueOf = T[keyof T] type StepTwoProps = { @@ -268,10 +268,10 @@ const StepTwo = ({ } } const { - rerankDefaultModel, - isRerankDefaultModelVaild, - rerankModelList, - } = useProviderContext() + modelList: rerankModelList, + defaultModel: rerankDefaultModel, + currentModel: isRerankDefaultModelVaild, + } = useModelListAndDefaultModelAndCurrentProviderAndModel(3) const getCreationParams = () => { let params if (isSetting) { @@ -289,7 +289,7 @@ const StepTwo = ({ if ( !isReRankModelSelected({ rerankDefaultModel, - isRerankDefaultModelVaild, + isRerankDefaultModelVaild: !!isRerankDefaultModelVaild, rerankModelList, // eslint-disable-next-line @typescript-eslint/no-use-before-define retrievalConfig, @@ -489,8 +489,8 @@ const StepTwo = ({ search_method: RETRIEVE_METHOD.semantic, reranking_enable: false, reranking_model: { - reranking_provider_name: rerankDefaultModel?.model_provider.provider_name, - reranking_model_name: rerankDefaultModel?.model_name, + reranking_provider_name: rerankDefaultModel?.provider.provider, + reranking_model_name: rerankDefaultModel?.model, }, top_k: 3, score_threshold_enabled: false, diff --git a/web/app/components/datasets/documents/detail/settings/index.tsx b/web/app/components/datasets/documents/detail/settings/index.tsx index a1b0eaf3a11044..149b5e55469a72 100644 --- a/web/app/components/datasets/documents/detail/settings/index.tsx +++ b/web/app/components/datasets/documents/detail/settings/index.tsx @@ -13,7 +13,7 @@ import Loading from '@/app/components/base/loading' import StepTwo from '@/app/components/datasets/create/step-two' import AccountSetting from '@/app/components/header/account-setting' import AppUnavailable from '@/app/components/base/app-unavailable' -import { useProviderContext } from '@/context/provider-context' +import { useDefaultModel } from '@/app/components/header/account-setting/model-provider-page/hooks' type DocumentSettingsProps = { datasetId: string @@ -26,7 +26,7 @@ const DocumentSettings = ({ datasetId, documentId }: DocumentSettingsProps) => { const [isShowSetAPIKey, { setTrue: showSetAPIKey, setFalse: hideSetAPIkey }] = useBoolean() const [hasError, setHasError] = useState(false) const { indexingTechnique, dataset } = useContext(DatasetDetailContext) - const { embeddingsDefaultModel } = useProviderContext() + const { data: embeddingsDefaultModel } = useDefaultModel(2) const saveHandler = () => router.push(`/datasets/${datasetId}/documents/${documentId}`) diff --git a/web/app/components/datasets/hit-testing/modify-retrieval-modal.tsx b/web/app/components/datasets/hit-testing/modify-retrieval-modal.tsx index e6a503ebf4aee7..0aed0a02fa275f 100644 --- a/web/app/components/datasets/hit-testing/modify-retrieval-modal.tsx +++ b/web/app/components/datasets/hit-testing/modify-retrieval-modal.tsx @@ -8,8 +8,8 @@ import type { RetrievalConfig } from '@/types/app' import RetrievalMethodConfig from '@/app/components/datasets/common/retrieval-method-config' import EconomicalRetrievalMethodConfig from '@/app/components/datasets/common/economical-retrieval-method-config' import Button from '@/app/components/base/button' -import { useProviderContext } from '@/context/provider-context' import { ensureRerankModelSelected, isReRankModelSelected } from '@/app/components/datasets/common/check-rerank-model' +import { useModelListAndDefaultModelAndCurrentProviderAndModel } from '@/app/components/header/account-setting/model-provider-page/hooks' type Props = { indexMethod: string @@ -36,16 +36,16 @@ const ModifyRetrievalModal: FC = ({ // }, ref) const { - rerankDefaultModel, - isRerankDefaultModelVaild, - rerankModelList, - } = useProviderContext() + modelList: rerankModelList, + defaultModel: rerankDefaultModel, + currentModel: isRerankDefaultModelVaild, + } = useModelListAndDefaultModelAndCurrentProviderAndModel(3) const handleSave = () => { if ( !isReRankModelSelected({ rerankDefaultModel, - isRerankDefaultModelVaild, + isRerankDefaultModelVaild: !!isRerankDefaultModelVaild, rerankModelList, retrievalConfig, indexMethod, diff --git a/web/app/components/header/account-setting/model-provider-page/hooks.ts b/web/app/components/header/account-setting/model-provider-page/hooks.ts index 55de1fbf30d3a9..ad1c8cb761a759 100644 --- a/web/app/components/header/account-setting/model-provider-page/hooks.ts +++ b/web/app/components/header/account-setting/model-provider-page/hooks.ts @@ -4,7 +4,7 @@ import { useMemo, useState, } from 'react' -import useSWR from 'swr' +import useSWR, { useSWRConfig } from 'swr' import { useContext } from 'use-context-selector' import type { CustomConfigrationModelFixedFields, @@ -28,7 +28,7 @@ type UseDefaultModelAndModelList = ( defaultModel: DefaultModelResponse | undefined, modelList: Model[], ) => [DefaultModel | undefined, (model: DefaultModel) => void] -export const useDefaultModelAndModelList: UseDefaultModelAndModelList = ( +export const useSystemDefaultModelAndModelList: UseDefaultModelAndModelList = ( defaultModel, modelList, ) => { @@ -116,7 +116,7 @@ export const useDefaultModel = (type: ModelTypeIndex) => { } } -export const useCurrentProviderAndModel = (defaultModel: DefaultModel, modelList: Model[]) => { +export const useCurrentProviderAndModel = (modelList: Model[], defaultModel?: DefaultModel) => { const currentProvider = modelList.find(provider => provider.provider === defaultModel?.provider) const currentModel = currentProvider?.models.find(model => model.model === defaultModel?.model) @@ -139,8 +139,8 @@ export const useModelListAndDefaultModel = (type: ModelTypeIndex) => { export const useModelListAndDefaultModelAndCurrentProviderAndModel = (type: ModelTypeIndex) => { const { modelList, defaultModel } = useModelListAndDefaultModel(type) const { currentProvider, currentModel } = useCurrentProviderAndModel( - { provider: defaultModel?.provider.provider || '', model: defaultModel?.model || '' }, modelList, + { provider: defaultModel?.provider.provider || '', model: defaultModel?.model || '' }, ) return { @@ -150,3 +150,14 @@ export const useModelListAndDefaultModelAndCurrentProviderAndModel = (type: Mode currentModel, } } + +export const useUpdateModelList = () => { + const { mutate } = useSWRConfig() + + const updateModelList = useCallback((type: ModelTypeIndex | ModelTypeEnum) => { + const modelType = typeof type === 'number' ? MODEL_TYPE_MAPS[type] : type + mutate(`/workspaces/current/models/model-types/${modelType}`) + }, [mutate]) + + return updateModelList +} diff --git a/web/app/components/header/account-setting/model-provider-page/index.tsx b/web/app/components/header/account-setting/model-provider-page/index.tsx index 2018e471a11bea..8862c9bd9e6cf1 100644 --- a/web/app/components/header/account-setting/model-provider-page/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/index.tsx @@ -11,20 +11,21 @@ import type { ModelProvider, } from './declarations' import { CustomConfigurationStatusEnum } from './declarations' +import { + useDefaultModel, + useUpdateModelList, +} from './hooks' import { fetchModelProviders } from '@/service/common' -import { useProviderContext } from '@/context/provider-context' import { AlertTriangle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback' import Loading from '@/app/components/base/loading' const ModelProviderPage = () => { const { t } = useTranslation() - const { - textGenerationDefaultModel, - embeddingsDefaultModel, - speech2textDefaultModel, - rerankDefaultModel, - updateModelList, - } = useProviderContext() + const updateModelList = useUpdateModelList() + const { data: textGenerationDefaultModel } = useDefaultModel(1) + const { data: embeddingsDefaultModel } = useDefaultModel(2) + const { data: rerankDefaultModel } = useDefaultModel(3) + const { data: speech2textDefaultModel } = useDefaultModel(4) const [currentProvider, setCurrentProvider] = useState(null) const [currentConfigurateMethod, setCurrentConfigurateMethod] = useState(null) const [currentCustomConfigrationModelFixedFields, setCurrentCustomConfigrationModelFixedFields] = useState(undefined) @@ -83,7 +84,13 @@ const ModelProviderPage = () => { ) :
{t('common.modelProvider.models')}
} - mutateProviders()} /> + mutateProviders()} + />
{ isLoading && ( diff --git a/web/app/components/header/account-setting/model-provider-page/model-icon/index.tsx b/web/app/components/header/account-setting/model-provider-page/model-icon/index.tsx index 30638b4217959f..a23a858333cb01 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-icon/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-icon/index.tsx @@ -1,14 +1,15 @@ import type { FC } from 'react' -import type { Model } from '../declarations' -import { ModelTypeEnum } from '../declarations' +import type { + Model, + ModelProvider, +} from '../declarations' import { useLanguage } from '../hooks' -import { useProviderContext } from '@/context/provider-context' -type ModelIconBaseProps = { - provider?: Model +type ModelIconProps = { + provider?: Model | ModelProvider className?: string } -const ModelIconBase: FC = ({ +const ModelIcon: FC = ({ provider, className, }) => { @@ -31,112 +32,4 @@ const ModelIconBase: FC = ({ ) } -type ModelIconSubProps = { - providerName: string - className?: string -} -export const ModelIconTextGeneration: FC = ({ - providerName, - className, -}) => { - const { textGenerationModelList } = useProviderContext() - const provider = textGenerationModelList.find(item => item.provider === providerName) - return ( - - ) -} - -export const ModelIconTextEmbedding: FC = ({ - providerName, - className, -}) => { - const { embeddingsModelList } = useProviderContext() - const provider = embeddingsModelList.find(item => item.provider === providerName) - return ( - - ) -} - -export const ModelIconRerank: FC = ({ - providerName, - className, -}) => { - const { rerankModelList } = useProviderContext() - const provider = rerankModelList.find(item => item.provider === providerName) - return ( - - ) -} - -export const ModelIconSpeechToText: FC = ({ - providerName, - className, -}) => { - const { speech2textModelList } = useProviderContext() - const provider = speech2textModelList.find(item => item.provider === providerName) - return ( - - ) -} - -type ModelIconProps = { - modelType: ModelTypeEnum -} & ModelIconSubProps -const ModelIcon: FC = ({ - modelType, - providerName, - className, -}) => { - if (modelType === ModelTypeEnum.textGeneration) { - return ( - - ) - } - if (modelType === ModelTypeEnum.textEmbedding) { - return ( - - ) - } - if (modelType === ModelTypeEnum.rerank) { - return ( - - ) - } - if (modelType === ModelTypeEnum.speech2text) { - return ( - - ) - } - - return ( - - ) -} - export default ModelIcon diff --git a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/index.tsx b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/index.tsx index 6a602a81f547f4..b03597c0782509 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/index.tsx @@ -5,10 +5,10 @@ import { useTranslation } from 'react-i18next' import type { FormValue, } from '../declarations' -import { ModelTypeEnum } from '../declarations' import ModelIcon from '../model-icon' import ModelName from '../model-name' import ModelSelector from '../model-selector' +import { useCurrentProviderAndModel } from '../hooks' import ParameterItem from './parameter-item' import type { ParameterValue } from './parameter-item' import { @@ -47,8 +47,13 @@ const ModelParameterModal: FC = ({ const [open, setOpen] = useState(false) const { data: parameterRulesData, isLoading } = useSWR(`/workspaces/current/model-providers/${provider}/models/parameter-rules?model=${modelId}`, fetchModelParameterRules) const { textGenerationModelList } = useProviderContext() - const currentProvider = textGenerationModelList.find(model => model.provider === provider) - const currentModel = currentProvider?.models.find(modelItem => modelItem.model === modelId) + const { + currentProvider, + currentModel, + } = useCurrentProviderAndModel( + textGenerationModelList, + { provider, model: modelId }, + ) const handleParamChange = (key: string, value: ParameterValue) => { onCompletionParamsChange({ @@ -94,8 +99,7 @@ const ModelParameterModal: FC = ({ currentProvider && ( ) } diff --git a/web/app/components/header/account-setting/model-provider-page/model-selector/index.tsx b/web/app/components/header/account-setting/model-provider-page/model-selector/index.tsx index 55a69e76b31f37..5a3b69e6794dfb 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-selector/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-selector/index.tsx @@ -5,6 +5,7 @@ import type { Model, ModelItem, } from '../declarations' +import { useCurrentProviderAndModel } from '../hooks' import ModelTrigger from './model-trigger' import EmptyTrigger from './empty-trigger' import Popup from './popup' @@ -28,8 +29,13 @@ const ModelSelector: FC = ({ onSelect, }) => { const [open, setOpen] = useState(false) - const currentProvider = modelList.find(model => model.provider === defaultModel?.provider) - const currentModel = currentProvider?.models.find(model => model.model === defaultModel?.model) + const { + currentProvider, + currentModel, + } = useCurrentProviderAndModel( + modelList, + defaultModel, + ) const handleSelect = (provider: string, model: ModelItem) => { setOpen(false) @@ -54,7 +60,7 @@ const ModelSelector: FC = ({ currentModel && currentProvider && ( ) diff --git a/web/app/components/header/account-setting/model-provider-page/model-selector/model-trigger.tsx b/web/app/components/header/account-setting/model-provider-page/model-selector/model-trigger.tsx index d1ddcb8e1e06c2..8937e12e90817e 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-selector/model-trigger.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-selector/model-trigger.tsx @@ -1,5 +1,8 @@ import type { FC } from 'react' -import type { ModelItem } from '../declarations' +import type { + Model, + ModelItem, +} from '../declarations' import ModelIcon from '../model-icon' import ModelName from '../model-name' // import { AlertTriangle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback' @@ -7,7 +10,7 @@ import { ChevronDown } from '@/app/components/base/icons/src/vender/line/arrows' type ModelTriggerProps = { open: boolean - provider: string + provider: Model model: ModelItem } const ModelTrigger: FC = ({ @@ -24,8 +27,7 @@ const ModelTrigger: FC = ({ > = ({ shrink-0 mr-2 w-4 h-4 ${modelItem.status !== ModelStatusEnum.active && 'opacity-60'} `} - providerName={model.provider} - modelType={modelItem.model_type} + provider={model} /> = ({
void } const SystemModel: FC = ({ + textGenerationDefaultModel, + embeddingsDefaultModel, + rerankDefaultModel, + speech2textDefaultModel, onUpdate, }) => { const { t } = useTranslation() const { notify } = useToastContext() - const { - textGenerationDefaultModel, - textGenerationModelList, - embeddingsDefaultModel, - embeddingsModelList, - rerankDefaultModel, - rerankModelList, - speech2textDefaultModel, - speech2textModelList, - updateModelList, - } = useProviderContext() + const { textGenerationModelList } = useProviderContext() + const updateModelList = useUpdateModelList() + const { data: embeddingModelList } = useModelList(2) + const { data: rerankModelList } = useModelList(3) + const { data: speech2textModelList } = useModelList(4) const [changedModelTypes, setChangedModelTypes] = useState([]) - const [currentTextGenerationDefaultModel, changeCurrentTextGenerationDefaultModel] = useDefaultModelAndModelList(textGenerationDefaultModel, textGenerationModelList) - const [currentEmbeddingsDefaultModel, changeCurrentEmbeddingsDefaultModel] = useDefaultModelAndModelList(embeddingsDefaultModel, embeddingsModelList) - const [currentRerankDefaultModel, changeCurrentRerankDefaultModel] = useDefaultModelAndModelList(rerankDefaultModel, rerankModelList) - const [currentSpeech2textDefaultModel, changeCurrentSpeech2textDefaultModel] = useDefaultModelAndModelList(speech2textDefaultModel, speech2textModelList) + const [currentTextGenerationDefaultModel, changeCurrentTextGenerationDefaultModel] = useSystemDefaultModelAndModelList(textGenerationDefaultModel, textGenerationModelList) + const [currentEmbeddingsDefaultModel, changeCurrentEmbeddingsDefaultModel] = useSystemDefaultModelAndModelList(embeddingsDefaultModel, embeddingModelList) + const [currentRerankDefaultModel, changeCurrentRerankDefaultModel] = useSystemDefaultModelAndModelList(rerankDefaultModel, rerankModelList) + const [currentSpeech2textDefaultModel, changeCurrentSpeech2textDefaultModel] = useSystemDefaultModelAndModelList(speech2textDefaultModel, speech2textModelList) const [open, setOpen] = useState(false) const getCurrentDefaultModelByModelType = (modelType: ModelTypeEnum) => { @@ -156,7 +165,7 @@ const SystemModel: FC = ({
handleChangeDefaultModel(ModelTypeEnum.textEmbedding, model)} />
diff --git a/web/context/provider-context.tsx b/web/context/provider-context.tsx index f054248c7699f6..3ad92bfd77329e 100644 --- a/web/context/provider-context.tsx +++ b/web/context/provider-context.tsx @@ -3,15 +3,12 @@ import { createContext, useContext } from 'use-context-selector' import useSWR from 'swr' import { useEffect, useState } from 'react' -import { fetchDefaultModal, fetchModelList, fetchSupportRetrievalMethods } from '@/service/common' +import { fetchModelList, fetchSupportRetrievalMethods } from '@/service/common' import { ModelStatusEnum, ModelTypeEnum, } from '@/app/components/header/account-setting/model-provider-page/declarations' -import type { - DefaultModelResponse, - Model, -} from '@/app/components/header/account-setting/model-provider-page/declarations' +import type { Model } from '@/app/components/header/account-setting/model-provider-page/declarations' import type { RETRIEVE_METHOD } from '@/types/app' import { Plan, type UsagePlanInfo } from '@/app/components/billing/type' import { fetchCurrentPlanInfo } from '@/service/billing' @@ -20,21 +17,7 @@ import { defaultPlan } from '@/app/components/billing/config' const ProviderContext = createContext<{ textGenerationModelList: Model[] - embeddingsModelList: Model[] - speech2textModelList: Model[] - rerankModelList: Model[] agentThoughtModelList: Model[] - updateModelList: (type: ModelTypeEnum) => void - textGenerationDefaultModel?: DefaultModelResponse - mutateTextGenerationDefaultModel: () => void - embeddingsDefaultModel?: DefaultModelResponse - isEmbeddingsDefaultModelValid: boolean - mutateEmbeddingsDefaultModel: () => void - speech2textDefaultModel?: DefaultModelResponse - mutateSpeech2textDefaultModel: () => void - rerankDefaultModel?: DefaultModelResponse - isRerankDefaultModelVaild: boolean - mutateRerankDefaultModel: () => void supportRetrievalMethods: RETRIEVE_METHOD[] hasSettedApiKey: boolean plan: { @@ -46,43 +29,29 @@ const ProviderContext = createContext<{ enableBilling: boolean enableReplaceWebAppLogo: boolean }>({ - textGenerationModelList: [], - embeddingsModelList: [], - speech2textModelList: [], - rerankModelList: [], - agentThoughtModelList: [], - updateModelList: () => { }, - textGenerationDefaultModel: undefined, - mutateTextGenerationDefaultModel: () => { }, - speech2textDefaultModel: undefined, - mutateSpeech2textDefaultModel: () => { }, - embeddingsDefaultModel: undefined, - isEmbeddingsDefaultModelValid: false, - mutateEmbeddingsDefaultModel: () => { }, - rerankDefaultModel: undefined, - isRerankDefaultModelVaild: false, - mutateRerankDefaultModel: () => { }, - supportRetrievalMethods: [], - hasSettedApiKey: true, - plan: { - type: Plan.sandbox, - usage: { - vectorSpace: 32, - buildApps: 12, - teamMembers: 1, - annotatedResponse: 1, - }, - total: { - vectorSpace: 200, - buildApps: 50, - teamMembers: 1, - annotatedResponse: 10, - }, - }, - isFetchedPlan: false, - enableBilling: false, - enableReplaceWebAppLogo: false, - }) + textGenerationModelList: [], + agentThoughtModelList: [], + supportRetrievalMethods: [], + hasSettedApiKey: true, + plan: { + type: Plan.sandbox, + usage: { + vectorSpace: 32, + buildApps: 12, + teamMembers: 1, + annotatedResponse: 1, + }, + total: { + vectorSpace: 200, + buildApps: 50, + teamMembers: 1, + annotatedResponse: 10, + }, + }, + isFetchedPlan: false, + enableBilling: false, + enableReplaceWebAppLogo: false, +}) export const useProviderContext = () => useContext(ProviderContext) @@ -92,44 +61,14 @@ type ProviderContextProviderProps = { export const ProviderContextProvider = ({ children, }: ProviderContextProviderProps) => { - const { data: textGenerationDefaultModel, mutate: mutateTextGenerationDefaultModel } = useSWR(`/workspaces/current/default-model?model_type=${ModelTypeEnum.textGeneration}`, fetchDefaultModal) - const { data: embeddingsDefaultModel, mutate: mutateEmbeddingsDefaultModel } = useSWR(`/workspaces/current/default-model?model_type=${ModelTypeEnum.textEmbedding}`, fetchDefaultModal) - const { data: speech2textDefaultModel, mutate: mutateSpeech2textDefaultModel } = useSWR(`/workspaces/current/default-model?model_type=${ModelTypeEnum.speech2text}`, fetchDefaultModal) - const { data: rerankDefaultModel, mutate: mutateRerankDefaultModel } = useSWR(`/workspaces/current/default-model?model_type=${ModelTypeEnum.rerank}`, fetchDefaultModal) const fetchModelListUrlPrefix = '/workspaces/current/models/model-types/' - const { data: textGenerationModelList, mutate: mutateTextGenerationModelList } = useSWR(`${fetchModelListUrlPrefix}${ModelTypeEnum.textGeneration}`, fetchModelList) - const { data: embeddingsModelList, mutate: mutateEmbeddingsModelList } = useSWR(`${fetchModelListUrlPrefix}${ModelTypeEnum.textEmbedding}`, fetchModelList) - const { data: speech2textModelList, mutate: mutateSpeech2textModelList } = useSWR(`${fetchModelListUrlPrefix}${ModelTypeEnum.speech2text}`, fetchModelList) - const { data: rerankModelList, mutate: mutateRerankModelList } = useSWR(`${fetchModelListUrlPrefix}${ModelTypeEnum.rerank}`, fetchModelList) + const { data: textGenerationModelList } = useSWR(`${fetchModelListUrlPrefix}${ModelTypeEnum.textGeneration}`, fetchModelList) const { data: supportRetrievalMethods } = useSWR('/datasets/retrieval-setting', fetchSupportRetrievalMethods) // const agentThoughtModelList = textGenerationModelList?.data?.filter((item) => { // return item.features?.includes(ModelFeature.agentThought) // }) - // const isRerankDefaultModelVaild = !!rerankModelList?.find( - // item => item.model_name === rerankDefaultModel?.model_name && item.model_provider.provider_name === rerankDefaultModel?.model_provider.provider_name, - // ) - - const isRerankDefaultModelVaild = false - - // const isEmbeddingsDefaultModelValid = !!embeddingsModelList?.find( - // item => item.model_name === embeddingsDefaultModel?.model_name && item.model_provider.provider_name === embeddingsDefaultModel?.model_provider.provider_name, - // ) - - const isEmbeddingsDefaultModelValid = false - - const updateModelList = (type: ModelTypeEnum) => { - if (type === ModelTypeEnum.textGeneration) - mutateTextGenerationModelList() - if (type === ModelTypeEnum.textEmbedding) - mutateEmbeddingsModelList() - if (type === ModelTypeEnum.speech2text) - mutateSpeech2textModelList() - if (type === ModelTypeEnum.rerank) - mutateRerankModelList() - } - const [plan, setPlan] = useState(defaultPlan) const [isFetchedPlan, setIsFetchedPlan] = useState(false) const [enableBilling, setEnableBilling] = useState(true) @@ -157,21 +96,7 @@ export const ProviderContextProvider = ({ return ( model.status === ModelStatusEnum.active), supportRetrievalMethods: supportRetrievalMethods?.retrieval_method || [], plan, From 5f7a55d93fe7c18c87e7674ad7cc16ffb675bcf7 Mon Sep 17 00:00:00 2001 From: StyleZhang Date: Wed, 27 Dec 2023 11:18:40 +0800 Subject: [PATCH 19/46] fix: default provider model icon --- .../model-provider-page/model-icon/index.tsx | 8 ++++++-- .../model-provider-page/provider-icon/index.tsx | 11 +++++++---- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/web/app/components/header/account-setting/model-provider-page/model-icon/index.tsx b/web/app/components/header/account-setting/model-provider-page/model-icon/index.tsx index a23a858333cb01..87ec7d2b5ae42d 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-icon/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-icon/index.tsx @@ -4,6 +4,7 @@ import type { ModelProvider, } from '../declarations' import { useLanguage } from '../hooks' +import { CubeOutline } from '@/app/components/base/icons/src/vender/line/shapes' type ModelIconProps = { provider?: Model | ModelProvider @@ -26,8 +27,11 @@ const ModelIcon: FC = ({ } return ( -
-
+
+
) } diff --git a/web/app/components/header/account-setting/model-provider-page/provider-icon/index.tsx b/web/app/components/header/account-setting/model-provider-page/provider-icon/index.tsx index d6c7a960b72bae..4d4dac97551f47 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-icon/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-icon/index.tsx @@ -1,6 +1,7 @@ import type { FC } from 'react' import type { ModelProvider } from '../declarations' import { useLanguage } from '../hooks' +import { CubeOutline } from '@/app/components/base/icons/src/vender/line/shapes' type ProviderIconProps = { provider: ModelProvider @@ -23,10 +24,12 @@ const ProviderIcon: FC = ({ } return ( -
-
-
- Model Provider Image +
+
+ +
+
+ {provider.label[language]}
) From db96302e3fbe8165de1e4f992d87e0586b10b007 Mon Sep 17 00:00:00 2001 From: StyleZhang Date: Wed, 27 Dec 2023 15:32:27 +0800 Subject: [PATCH 20/46] fix: model icon name --- .../app/configuration/config-model/index.tsx | 6 +- .../components/app/configuration/index.tsx | 9 +- web/app/components/app/log/list.tsx | 22 +- .../config-view/detail/index.tsx | 3 +- .../config-view/summary/index.tsx | 28 +- .../explore/universal-chat/config/index.tsx | 5 +- .../config/model-config/index.tsx | 24 +- .../explore/universal-chat/index.tsx | 24 +- .../model-page/configs/anthropic.tsx | 80 ---- .../model-page/configs/azure_openai.tsx | 189 --------- .../model-page/configs/baichuan.tsx | 70 ---- .../model-page/configs/chatglm.tsx | 69 ---- .../model-page/configs/cohere.tsx | 57 --- .../model-page/configs/huggingface_hub.tsx | 278 -------------- .../model-page/configs/index.ts | 37 -- .../model-page/configs/jina.tsx | 57 --- .../model-page/configs/localai.tsx | 176 --------- .../model-page/configs/minimax.tsx | 69 ---- .../model-page/configs/openai.tsx | 93 ----- .../model-page/configs/openllm.tsx | 114 ------ .../model-page/configs/replicate.tsx | 122 ------ .../model-page/configs/spark.tsx | 83 ---- .../model-page/configs/tongyi.tsx | 53 --- .../model-page/configs/wenxin.tsx | 66 ---- .../model-page/configs/xinference.tsx | 135 ------- .../model-page/configs/zhipuai.tsx | 55 --- .../model-page/declarations.ts | 163 -------- .../account-setting/model-page/index.tsx | 267 ------------- .../model-page/model-card/Quota.tsx | 117 ------ .../model-page/model-card/index.tsx | 81 ---- .../model-page/model-item/Card.tsx | 69 ---- .../model-page/model-item/FreeQuota.tsx | 79 ---- .../model-page/model-item/QuotaCard.tsx | 29 -- .../model-page/model-item/Setting.tsx | 104 ----- .../model-page/model-item/index.module.css | 4 - .../model-page/model-item/index.tsx | 73 ---- .../model-page/model-modal/Form.tsx | 208 ---------- .../model-page/model-modal/Input.tsx | 58 --- .../model-page/model-modal/index.tsx | 170 --------- .../model-page/model-selector/index.tsx | 347 ----------------- .../model-selector/portal-select.tsx | 359 ------------------ .../model-selector/style.module.css | 7 - .../model-page/selector/index.tsx | 97 ----- .../model-page/system-model/index.tsx | 213 ----------- .../account-setting/model-page/utils.ts | 35 -- .../model-provider-page/declarations.ts | 24 +- .../model-provider-page/hooks.ts | 29 ++ .../model-provider-page/model-name/index.tsx | 13 +- .../model-parameter-modal/index.tsx | 37 +- .../model-parameter-modal/parameter-item.tsx | 103 +++-- .../model-selector/feature-icon.tsx | 51 ++- .../system-model-selector/index.tsx | 4 +- web/context/provider-context.tsx | 25 +- web/hooks/use-pay.tsx | 17 +- web/i18n/lang/common.en.ts | 1 + web/i18n/lang/common.zh.ts | 1 + 56 files changed, 288 insertions(+), 4421 deletions(-) delete mode 100644 web/app/components/header/account-setting/model-page/configs/anthropic.tsx delete mode 100644 web/app/components/header/account-setting/model-page/configs/azure_openai.tsx delete mode 100644 web/app/components/header/account-setting/model-page/configs/baichuan.tsx delete mode 100644 web/app/components/header/account-setting/model-page/configs/chatglm.tsx delete mode 100644 web/app/components/header/account-setting/model-page/configs/cohere.tsx delete mode 100644 web/app/components/header/account-setting/model-page/configs/huggingface_hub.tsx delete mode 100644 web/app/components/header/account-setting/model-page/configs/index.ts delete mode 100644 web/app/components/header/account-setting/model-page/configs/jina.tsx delete mode 100644 web/app/components/header/account-setting/model-page/configs/localai.tsx delete mode 100644 web/app/components/header/account-setting/model-page/configs/minimax.tsx delete mode 100644 web/app/components/header/account-setting/model-page/configs/openai.tsx delete mode 100644 web/app/components/header/account-setting/model-page/configs/openllm.tsx delete mode 100644 web/app/components/header/account-setting/model-page/configs/replicate.tsx delete mode 100644 web/app/components/header/account-setting/model-page/configs/spark.tsx delete mode 100644 web/app/components/header/account-setting/model-page/configs/tongyi.tsx delete mode 100644 web/app/components/header/account-setting/model-page/configs/wenxin.tsx delete mode 100644 web/app/components/header/account-setting/model-page/configs/xinference.tsx delete mode 100644 web/app/components/header/account-setting/model-page/configs/zhipuai.tsx delete mode 100644 web/app/components/header/account-setting/model-page/declarations.ts delete mode 100644 web/app/components/header/account-setting/model-page/index.tsx delete mode 100644 web/app/components/header/account-setting/model-page/model-card/Quota.tsx delete mode 100644 web/app/components/header/account-setting/model-page/model-card/index.tsx delete mode 100644 web/app/components/header/account-setting/model-page/model-item/Card.tsx delete mode 100644 web/app/components/header/account-setting/model-page/model-item/FreeQuota.tsx delete mode 100644 web/app/components/header/account-setting/model-page/model-item/QuotaCard.tsx delete mode 100644 web/app/components/header/account-setting/model-page/model-item/Setting.tsx delete mode 100644 web/app/components/header/account-setting/model-page/model-item/index.module.css delete mode 100644 web/app/components/header/account-setting/model-page/model-item/index.tsx delete mode 100644 web/app/components/header/account-setting/model-page/model-modal/Form.tsx delete mode 100644 web/app/components/header/account-setting/model-page/model-modal/Input.tsx delete mode 100644 web/app/components/header/account-setting/model-page/model-modal/index.tsx delete mode 100644 web/app/components/header/account-setting/model-page/model-selector/index.tsx delete mode 100644 web/app/components/header/account-setting/model-page/model-selector/portal-select.tsx delete mode 100644 web/app/components/header/account-setting/model-page/model-selector/style.module.css delete mode 100644 web/app/components/header/account-setting/model-page/selector/index.tsx delete mode 100644 web/app/components/header/account-setting/model-page/system-model/index.tsx delete mode 100644 web/app/components/header/account-setting/model-page/utils.ts diff --git a/web/app/components/app/configuration/config-model/index.tsx b/web/app/components/app/configuration/config-model/index.tsx index a64a33191839d4..cf3e8aa858e761 100644 --- a/web/app/components/app/configuration/config-model/index.tsx +++ b/web/app/components/app/configuration/config-model/index.tsx @@ -21,13 +21,12 @@ import { Target04 } from '@/app/components/base/icons/src/vender/solid/general' import { Sliders02 } from '@/app/components/base/icons/src/vender/solid/mediaAndDevices' import { fetchModelParams } from '@/service/debug' import Loading from '@/app/components/base/loading' -import { useProviderContext } from '@/context/provider-context' import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' import type { ModelModeType } from '@/types/app' import ModelIcon from '@/app/components/header/account-setting/model-provider-page/model-icon' import ModelName from '@/app/components/header/account-setting/model-provider-page/model-name' import ModelSelector from '@/app/components/header/account-setting/model-provider-page/model-selector' -import { useCurrentProviderAndModel } from '@/app/components/header/account-setting/model-provider-page/hooks' +import { useTextGenerationCurrentProviderAndModelAndModelList } from '@/app/components/header/account-setting/model-provider-page/hooks' export type IConfigModelProps = { isAdvancedMode: boolean @@ -50,15 +49,14 @@ const ConfigModel: FC = ({ disabled, }) => { const { t } = useTranslation() - const { textGenerationModelList } = useProviderContext() const [isShowConfig, { setFalse: hideConfig, toggle: toogleShowConfig }] = useBoolean(false) const [maxTokenSettingTipVisible, setMaxTokenSettingTipVisible] = useState(false) const configContentRef = React.useRef(null) const { currentProvider, currentModel: currModel, - } = useCurrentProviderAndModel( textGenerationModelList, + } = useTextGenerationCurrentProviderAndModelAndModelList( { provider, model: modelId }, ) diff --git a/web/app/components/app/configuration/index.tsx b/web/app/components/app/configuration/index.tsx index 272852a4bca35f..7b219f992222de 100644 --- a/web/app/components/app/configuration/index.tsx +++ b/web/app/components/app/configuration/index.tsx @@ -49,7 +49,7 @@ import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' import Drawer from '@/app/components/base/drawer' import ModelParameterModal from '@/app/components/header/account-setting/model-provider-page/model-parameter-modal' import type { FormValue } from '@/app/components/header/account-setting/model-provider-page/declarations' -import { useCurrentProviderAndModel } from '@/app/components/header/account-setting/model-provider-page/hooks' +import { useTextGenerationCurrentProviderAndModelAndModelList } from '@/app/components/header/account-setting/model-provider-page/hooks' type PublichConfig = { modelConfig: ModelConfig @@ -218,12 +218,11 @@ const Configuration: FC = () => { }) } + const { hasSettedApiKey } = useProviderContext() const { - hasSettedApiKey, - textGenerationModelList, - } = useProviderContext() - const { currentModel: currModel } = useCurrentProviderAndModel( + currentModel: currModel, textGenerationModelList, + } = useTextGenerationCurrentProviderAndModelAndModelList( { provider: modelConfig.provider, model: modelConfig.model_id, diff --git a/web/app/components/app/log/list.tsx b/web/app/components/app/log/list.tsx index 5cd5dbfcda19f2..b692e944bf81ad 100644 --- a/web/app/components/app/log/list.tsx +++ b/web/app/components/app/log/list.tsx @@ -30,9 +30,8 @@ import { ToastContext } from '@/app/components/base/toast' import { fetchChatConversationDetail, fetchChatMessages, fetchCompletionConversationDetail, updateLogMessageAnnotations, updateLogMessageFeedbacks } from '@/service/log' import { TONE_LIST } from '@/config' import ModelIcon from '@/app/components/header/account-setting/model-provider-page/model-icon' -import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' -import ModelName from '@/app/components/app/configuration/config-model/model-name' -import ModelModeTypeLabel from '@/app/components/app/configuration/config-model/model-mode-type-label' +import { useTextGenerationCurrentProviderAndModelAndModelList } from '@/app/components/header/account-setting/model-provider-page/hooks' +import ModelName from '@/app/components/header/account-setting/model-provider-page/model-name' import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' import TextGeneration from '@/app/components/app/text-generate/item' @@ -189,6 +188,12 @@ function DetailPanel { const itemContent = item[Object.keys(item)[0]] return { @@ -225,13 +230,12 @@ function DetailPanel + -
- -
-
dataSets: DataSet[] } diff --git a/web/app/components/explore/universal-chat/config-view/summary/index.tsx b/web/app/components/explore/universal-chat/config-view/summary/index.tsx index 5a54cd3bae3def..b9e30e4cf1f7a9 100644 --- a/web/app/components/explore/universal-chat/config-view/summary/index.tsx +++ b/web/app/components/explore/universal-chat/config-view/summary/index.tsx @@ -5,17 +5,15 @@ import cn from 'classnames' import { useBoolean, useClickAway } from 'ahooks' import s from './style.module.css' import ModelIcon from '@/app/components/header/account-setting/model-provider-page/model-icon' -import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' +import ModelName from '@/app/components/header/account-setting/model-provider-page/model-name' import { Google, WebReader, Wikipedia } from '@/app/components/base/icons/src/public/plugins' import ConfigDetail from '@/app/components/explore/universal-chat/config-view/detail' -import type { ProviderEnum } from '@/app/components/header/account-setting/model-page/declarations' -import ModelName from '@/app/components/app/configuration/config-model/model-name' -import { useProviderContext } from '@/context/provider-context' import type { DataSet } from '@/models/datasets' +import { useAgentThoughtCurrentProviderAndModelAndModelList } from '@/app/components/header/account-setting/model-provider-page/hooks' export type ISummaryProps = { modelId: string - providerName: ProviderEnum + providerName: string plugins: Record dataSets: DataSet[] } @@ -50,9 +48,12 @@ const Summary: FC = ({ plugins, dataSets, }) => { - const { agentThoughtModelList } = useProviderContext() - const currModel = agentThoughtModelList.find(item => item.model_name === modelId && item.model_provider.provider_name === providerName) - + const { + currentModel: currModel, + currentProvider, + } = useAgentThoughtCurrentProviderAndModelAndModelList( + { provider: providerName, model: modelId }, + ) // current_datetime is not configable and do not have icon const pluginIds = Object.keys(plugins).filter(key => plugins[key] && key !== 'current_datetime') const [isShowConfig, { setFalse: hideConfig, toggle: toggleShowConfig }] = useBoolean(false) @@ -64,8 +65,15 @@ const Summary: FC = ({ return (
- -
+ +
+ +
{ pluginIds.length > 0 && (
diff --git a/web/app/components/explore/universal-chat/config/index.tsx b/web/app/components/explore/universal-chat/config/index.tsx index a6d3528ba2fa61..825677e069db74 100644 --- a/web/app/components/explore/universal-chat/config/index.tsx +++ b/web/app/components/explore/universal-chat/config/index.tsx @@ -4,15 +4,14 @@ import React from 'react' import ModelConfig from './model-config' import DataConfig from './data-config' import PluginConfig from './plugins-config' -import type { ProviderEnum } from '@/app/components/header/account-setting/model-page/declarations' import type { DataSet } from '@/models/datasets' export type IConfigProps = { className?: string readonly?: boolean modelId: string - providerName: ProviderEnum - onModelChange?: (modelId: string, providerName: ProviderEnum) => void + providerName: string + onModelChange?: (modelId: string, providerName: string) => void plugins: Record onPluginChange?: (key: string, value: boolean) => void dataSets: DataSet[] diff --git a/web/app/components/explore/universal-chat/config/model-config/index.tsx b/web/app/components/explore/universal-chat/config/model-config/index.tsx index a5851c50485f7a..d2fbb2d23510ef 100644 --- a/web/app/components/explore/universal-chat/config/model-config/index.tsx +++ b/web/app/components/explore/universal-chat/config/model-config/index.tsx @@ -2,12 +2,13 @@ import type { FC } from 'react' import React from 'react' import { useTranslation } from 'react-i18next' -import { ModelType, type ProviderEnum } from '@/app/components/header/account-setting/model-page/declarations' -import ModelSelector from '@/app/components/header/account-setting/model-page/model-selector' +import ModelSelector from '@/app/components/header/account-setting/model-provider-page/model-selector' +import { useProviderContext } from '@/context/provider-context' + export type IModelConfigProps = { modelId: string - providerName: ProviderEnum - onChange?: (modelId: string, providerName: ProviderEnum) => void + providerName: string + onChange?: (modelId: string, providerName: string) => void readonly?: boolean } @@ -18,21 +19,16 @@ const ModelConfig: FC = ({ readonly, }) => { const { t } = useTranslation() + const { agentThoughtModelList } = useProviderContext() return (
{t('explore.universalChat.model')}
{ - onChange?.(model.model_name, model.model_provider.provider_name) + defaultModel={{ provider: providerName, model: modelId }} + modelList={agentThoughtModelList} + onSelect={(model) => { + onChange?.(model.model, model.provider) }} readonly={readonly} /> diff --git a/web/app/components/explore/universal-chat/index.tsx b/web/app/components/explore/universal-chat/index.tsx index 24b4e9cbd984b1..5e7cf45b32026b 100644 --- a/web/app/components/explore/universal-chat/index.tsx +++ b/web/app/components/explore/universal-chat/index.tsx @@ -2,7 +2,7 @@ /* eslint-disable @typescript-eslint/no-use-before-define */ 'use client' import type { FC } from 'react' -import React, { useEffect, useRef, useState } from 'react' +import React, { useEffect, useMemo, useRef, useState } from 'react' import cn from 'classnames' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' @@ -39,8 +39,7 @@ import type { DataSet } from '@/models/datasets' import ConfigSummary from '@/app/components/explore/universal-chat/config-view/summary' import { fetchDatasets } from '@/service/datasets' import ItemOperation from '@/app/components/explore/item-operation' -import { useProviderContext } from '@/context/provider-context' -import type { ProviderEnum } from '@/app/components/header/account-setting/model-page/declarations' +import { useAgentThoughtCurrentProviderAndModelAndModelList } from '@/app/components/header/account-setting/model-provider-page/hooks' const APP_ID = 'universal-chat' const DEFAULT_PLUGIN = { @@ -72,12 +71,19 @@ const Main: FC = () => { const { t } = useTranslation() const media = useBreakpoints() const isMobile = media === MediaType.mobile - const { agentThoughtModelList } = useProviderContext() + const modelConfig = useMemo(() => { + if (prevConfig?.providerName && prevConfig.modelId) + return { provider: prevConfig.providerName, model: prevConfig.modelId } + }, [prevConfig]) + const { + currentProvider, + currentModel, + } = useAgentThoughtCurrentProviderAndModelAndModelList(modelConfig) const getInitConfig = (type: 'model' | 'plugin') => { if (type === 'model') { return { - providerName: prevConfig?.providerName || agentThoughtModelList?.[0]?.model_provider.provider_name, - modelId: prevConfig?.modelId || agentThoughtModelList?.[0]?.model_name, + providerName: prevConfig?.providerName || currentProvider?.provider, + modelId: prevConfig?.modelId || currentModel?.model, } } @@ -454,7 +460,7 @@ const Main: FC = () => { const [isResponsingConIsCurrCon, setIsResponsingConCurrCon, getIsResponsingConIsCurrCon] = useGetState(true) const handleSend = async (message: string) => { if (isNewConversation) { - const isModelSelected = modelId && !!agentThoughtModelList.find(item => item.model_name === modelId) + const isModelSelected = modelId && !!currentModel if (!isModelSelected) { notify({ type: 'error', message: t('appDebug.errorMessage.notSelectModel') }) return @@ -693,7 +699,7 @@ const Main: FC = () => { } const initConfig = getInitConfig('model') const [modelId, setModeId] = useState((initConfig as any)?.modelId as string) - const [providerName, setProviderName] = useState((initConfig as any)?.providerName as ProviderEnum) + const [providerName, setProviderName] = useState((initConfig as any)?.providerName) // const currModel = MODEL_LIST.find(item => item.id === modelId) const [plugins, setPlugins] = useState>(getInitConfig('plugin') as Record) @@ -707,7 +713,7 @@ const Main: FC = () => { const configSetDefaultValue = () => { const initConfig = getInitConfig('model') setModeId((initConfig as any)?.modelId as string) - setProviderName((initConfig as any)?.providerName as ProviderEnum) + setProviderName((initConfig as any)?.providerName) setPlugins(getInitConfig('plugin') as any) setDateSets([]) } diff --git a/web/app/components/header/account-setting/model-page/configs/anthropic.tsx b/web/app/components/header/account-setting/model-page/configs/anthropic.tsx deleted file mode 100644 index 6abe1a45c0e93f..00000000000000 --- a/web/app/components/header/account-setting/model-page/configs/anthropic.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import { ProviderEnum } from '../declarations' -import type { ProviderConfig } from '../declarations' -import { Anthropic, AnthropicText } from '@/app/components/base/icons/src/public/llm' -import { IS_CE_EDITION } from '@/config' - -const config: ProviderConfig = { - selector: { - name: { - 'en': 'Anthropic', - 'zh-Hans': 'Anthropic', - }, - icon: , - }, - item: { - key: ProviderEnum.anthropic, - titleIcon: { - 'en': , - 'zh-Hans': , - }, - subTitleIcon: , - desc: { - 'en': 'Anthropic’s powerful models, such as Claude 2 and Claude Instant.', - 'zh-Hans': 'Anthropic 的强大模型,例如 Claude 2 和 Claude Instant。', - }, - bgColor: 'bg-[#F0F0EB]', - }, - modal: { - key: ProviderEnum.anthropic, - title: { - 'en': 'Anthropic', - 'zh-Hans': 'Anthropic', - }, - icon: , - link: { - href: 'https://console.anthropic.com/account/keys', - label: { - 'en': 'Get your API key from Anthropic', - 'zh-Hans': '从 Anthropic 获取 API Key', - }, - }, - validateKeys: ['anthropic_api_key'], - fields: [ - { - type: 'text', - key: 'anthropic_api_key', - required: true, - label: { - 'en': 'API Key', - 'zh-Hans': 'API Key', - }, - placeholder: { - 'en': 'Enter your API key here', - 'zh-Hans': '在此输入您的 API Key', - }, - }, - ...( - IS_CE_EDITION - ? [{ - type: 'text', - key: 'anthropic_api_url', - required: false, - label: { - 'en': 'Custom API Domain', - 'zh-Hans': '自定义 API 域名', - }, - placeholder: { - 'en': 'Enter your API domain, eg: https://example.com/xxx(Optional)', - 'zh-Hans': '在此输入您的 API 域名,如:https://example.com/xxx(选填)', - }, - help: { - 'en': 'Configurable custom Anthropic API server url.', - 'zh-Hans': '可配置自定义 Anthropic API 服务器地址。', - }, - }] - : [] - ), - ], - }, -} -export default config diff --git a/web/app/components/header/account-setting/model-page/configs/azure_openai.tsx b/web/app/components/header/account-setting/model-page/configs/azure_openai.tsx deleted file mode 100644 index d8c332723416ba..00000000000000 --- a/web/app/components/header/account-setting/model-page/configs/azure_openai.tsx +++ /dev/null @@ -1,189 +0,0 @@ -import { ProviderEnum } from '../declarations' -import type { ProviderConfig } from '../declarations' -import { AzureOpenaiService, AzureOpenaiServiceText, OpenaiBlue } from '@/app/components/base/icons/src/public/llm' - -const config: ProviderConfig = { - selector: { - name: { - 'en': 'Azure OpenAI Service', - 'zh-Hans': 'Azure OpenAI Service', - }, - icon: , - }, - item: { - key: ProviderEnum.azure_openai, - titleIcon: { - 'en': , - 'zh-Hans': , - }, - }, - modal: { - key: ProviderEnum.azure_openai, - title: { - 'en': 'Azure OpenAI Service Model', - 'zh-Hans': 'Azure OpenAI Service Model', - }, - icon: , - link: { - href: 'https://azure.microsoft.com/en-us/products/ai-services/openai-service', - label: { - 'en': 'Get your API key from Azure', - 'zh-Hans': '从 Azure 获取 API Key', - }, - }, - defaultValue: { - model_type: 'text-generation', - }, - validateKeys: [ - 'model_name', - 'model_type', - 'openai_api_base', - 'openai_api_key', - 'base_model_name', - ], - fields: [ - { - type: 'text', - key: 'model_name', - required: true, - label: { - 'en': 'Deployment Name', - 'zh-Hans': '部署名称', - }, - placeholder: { - 'en': 'Enter your Deployment Name here, matching the Azure deployment name.', - 'zh-Hans': '在此输入您的部署名称,需要与 Azure 的部署名称匹配', - }, - }, - { - type: 'radio', - key: 'model_type', - required: true, - label: { - 'en': 'Model Type', - 'zh-Hans': '模型类型', - }, - options: [ - { - key: 'text-generation', - label: { - 'en': 'Text Generation', - 'zh-Hans': '文本生成', - }, - }, - { - key: 'embeddings', - label: { - 'en': 'Embeddings', - 'zh-Hans': 'Embeddings', - }, - }, - ], - }, - { - type: 'text', - key: 'openai_api_base', - required: true, - label: { - 'en': 'API Endpoint URL', - 'zh-Hans': 'API 域名', - }, - placeholder: { - 'en': 'Enter your API Endpoint, eg: https://example.com/xxx', - 'zh-Hans': '在此输入您的 API 域名,如:https://example.com/xxx', - }, - }, - { - type: 'text', - key: 'openai_api_key', - required: true, - label: { - 'en': 'API Key', - 'zh-Hans': 'API Key', - }, - placeholder: { - 'en': 'Enter your API key here', - 'zh-Hans': '在此输入您的 API Key', - }, - }, - { - type: 'select', - key: 'base_model_name', - required: true, - label: { - 'en': 'Base Model', - 'zh-Hans': '基础模型', - }, - options: (v) => { - if (v.model_type === 'text-generation') { - return [ - { - key: 'gpt-35-turbo', - label: { - 'en': 'gpt-35-turbo', - 'zh-Hans': 'gpt-35-turbo', - }, - }, - { - key: 'gpt-35-turbo-16k', - label: { - 'en': 'gpt-35-turbo-16k', - 'zh-Hans': 'gpt-35-turbo-16k', - }, - }, - { - key: 'gpt-4', - label: { - 'en': 'gpt-4', - 'zh-Hans': 'gpt-4', - }, - }, - { - key: 'gpt-4-32k', - label: { - 'en': 'gpt-4-32k', - 'zh-Hans': 'gpt-4-32k', - }, - }, - { - key: 'gpt-4-1106-preview', - label: { - 'en': 'gpt-4-1106-preview', - 'zh-Hans': 'gpt-4-1106-preview', - }, - }, - { - key: 'gpt-4-vision-preview', - label: { - 'en': 'gpt-4-vision-preview', - 'zh-Hans': 'gpt-4-vision-preview', - }, - }, - { - key: 'text-davinci-003', - label: { - 'en': 'text-davinci-003', - 'zh-Hans': 'text-davinci-003', - }, - }, - ] - } - if (v.model_type === 'embeddings') { - return [ - { - key: 'text-embedding-ada-002', - label: { - 'en': 'text-embedding-ada-002', - 'zh-Hans': 'text-embedding-ada-002', - }, - }, - ] - } - return [] - }, - }, - ], - }, -} - -export default config diff --git a/web/app/components/header/account-setting/model-page/configs/baichuan.tsx b/web/app/components/header/account-setting/model-page/configs/baichuan.tsx deleted file mode 100644 index 10a8415b24d12f..00000000000000 --- a/web/app/components/header/account-setting/model-page/configs/baichuan.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import { ProviderEnum } from '../declarations' -import type { ProviderConfig } from '../declarations' -import { BaichuanTextCn } from '@/app/components/base/icons/src/image/llm' -import { - Baichuan, - BaichuanText, -} from '@/app/components/base/icons/src/public/llm' - -const config: ProviderConfig = { - selector: { - name: { - 'en': 'BAICHUAN AI', - 'zh-Hans': '百川智能', - }, - icon: , - }, - item: { - key: ProviderEnum.baichuan, - titleIcon: { - 'en': , - 'zh-Hans': , - }, - }, - modal: { - key: ProviderEnum.baichuan, - title: { - 'en': 'BAICHUAN AI', - 'zh-Hans': '百川智能', - }, - icon: , - link: { - href: 'https://platform.baichuan-ai.com/console/apikey', - label: { - 'en': 'Get your API key from BAICHUAN AI', - 'zh-Hans': '从百川智能获取 API Key', - }, - }, - validateKeys: ['api_key', 'secret_key'], - fields: [ - { - type: 'text', - key: 'api_key', - required: true, - label: { - 'en': 'API Key', - 'zh-Hans': 'API Key', - }, - placeholder: { - 'en': 'Enter your API key here', - 'zh-Hans': '在此输入您的 API Key', - }, - }, - { - type: 'text', - key: 'secret_key', - required: true, - label: { - 'en': 'Secret Key', - 'zh-Hans': 'Secret Key', - }, - placeholder: { - 'en': 'Enter your Secret key here', - 'zh-Hans': '在此输入您的 Secret Key', - }, - }, - ], - }, -} - -export default config diff --git a/web/app/components/header/account-setting/model-page/configs/chatglm.tsx b/web/app/components/header/account-setting/model-page/configs/chatglm.tsx deleted file mode 100644 index c6828153eae762..00000000000000 --- a/web/app/components/header/account-setting/model-page/configs/chatglm.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import { ProviderEnum } from '../declarations' -import type { ProviderConfig } from '../declarations' -import { Chatglm, ChatglmText } from '@/app/components/base/icons/src/public/llm' - -const config: ProviderConfig = { - selector: { - name: { - 'en': 'ChatGLM', - 'zh-Hans': 'ChatGLM', - }, - icon: , - }, - item: { - key: ProviderEnum.chatglm, - titleIcon: { - 'en': , - 'zh-Hans': , - }, - disable: { - tip: { - 'en': 'Only supports the ', - 'zh-Hans': '仅支持', - }, - link: { - href: { - 'en': 'https://docs.dify.ai/getting-started/install-self-hosted', - 'zh-Hans': 'https://docs.dify.ai/v/zh-hans/getting-started/install-self-hosted', - }, - label: { - 'en': 'community open-source version', - 'zh-Hans': '社区开源版本', - }, - }, - }, - }, - modal: { - key: ProviderEnum.chatglm, - title: { - 'en': 'ChatGLM', - 'zh-Hans': 'ChatGLM', - }, - icon: , - link: { - href: 'https://github.com/THUDM/ChatGLM-6B#api%E9%83%A8%E7%BD%B2', - label: { - 'en': 'How to deploy ChatGLM', - 'zh-Hans': '如何部署 ChatGLM', - }, - }, - validateKeys: ['api_base'], - fields: [ - { - type: 'text', - key: 'api_base', - required: true, - label: { - 'en': 'Custom API Domain', - 'zh-Hans': '自定义 API 域名', - }, - placeholder: { - 'en': 'Enter your API domain, eg: https://example.com/xxx', - 'zh-Hans': '在此输入您的 API 域名,如:https://example.com/xxx', - }, - }, - ], - }, -} - -export default config diff --git a/web/app/components/header/account-setting/model-page/configs/cohere.tsx b/web/app/components/header/account-setting/model-page/configs/cohere.tsx deleted file mode 100644 index 11bad8a9d7afe5..00000000000000 --- a/web/app/components/header/account-setting/model-page/configs/cohere.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import { ProviderEnum } from '../declarations' -import type { ProviderConfig } from '../declarations' -import { Cohere, CohereText } from '@/app/components/base/icons/src/public/llm' - -const config: ProviderConfig = { - selector: { - name: { - 'en': 'cohere', - 'zh-Hans': 'cohere', - }, - icon: , - }, - item: { - key: ProviderEnum.cohere, - titleIcon: { - 'en': , - 'zh-Hans': , - }, - hit: { - 'en': 'Rerank Model Supported', - 'zh-Hans': '支持 Rerank 模型', - }, - }, - modal: { - key: ProviderEnum.cohere, - title: { - 'en': 'Rerank Model', - 'zh-Hans': 'Rerank 模型', - }, - icon: , - link: { - href: 'https://dashboard.cohere.com/api-keys', - label: { - 'en': 'Get your API key from cohere', - 'zh-Hans': '从 cohere 获取 API Key', - }, - }, - validateKeys: ['api_key'], - fields: [ - { - type: 'text', - key: 'api_key', - required: true, - label: { - 'en': 'API Key', - 'zh-Hans': 'API Key', - }, - placeholder: { - 'en': 'Enter your API key here', - 'zh-Hans': '在此输入您的 API Key', - }, - }, - ], - }, -} - -export default config diff --git a/web/app/components/header/account-setting/model-page/configs/huggingface_hub.tsx b/web/app/components/header/account-setting/model-page/configs/huggingface_hub.tsx deleted file mode 100644 index e7116edd0e96cc..00000000000000 --- a/web/app/components/header/account-setting/model-page/configs/huggingface_hub.tsx +++ /dev/null @@ -1,278 +0,0 @@ -import { ProviderEnum } from '../declarations' -import type { FormValue, ProviderConfig } from '../declarations' -import { Huggingface, HuggingfaceText } from '@/app/components/base/icons/src/public/llm' - -const config: ProviderConfig = { - selector: { - name: { - 'en': 'Hugging Face', - 'zh-Hans': 'Hugging Face', - }, - icon: , - }, - item: { - key: ProviderEnum.huggingface_hub, - titleIcon: { - 'en': , - 'zh-Hans': , - }, - hit: { - 'en': '🐑 Llama 2 Supported', - 'zh-Hans': '🐑 Llama 2 已支持', - }, - }, - modal: { - key: ProviderEnum.huggingface_hub, - title: { - 'en': 'Hugging Face Model', - 'zh-Hans': 'Hugging Face Model', - }, - icon: , - link: { - href: 'https://huggingface.co/settings/tokens', - label: { - 'en': 'Get your API key from Hugging Face Hub', - 'zh-Hans': '从 Hugging Face Hub 获取 API Key', - }, - }, - defaultValue: { - model_type: 'text-generation', - huggingfacehub_api_type: 'hosted_inference_api', - task_type: 'text-generation', - }, - validateKeys: (v?: FormValue) => { - if (v?.huggingfacehub_api_type === 'hosted_inference_api') { - return [ - 'huggingfacehub_api_token', - 'model_name', - ] - } - if (v?.huggingfacehub_api_type === 'inference_endpoints') { - if (v.model_type === 'embeddings') { - return [ - 'huggingfacehub_api_token', - 'huggingface_namespace', - 'model_name', - 'huggingfacehub_endpoint_url', - 'task_type', - ] - } - return [ - 'huggingfacehub_api_token', - 'model_name', - 'huggingfacehub_endpoint_url', - 'task_type', - ] - } - return [] - }, - filterValue: (v?: FormValue) => { - let filteredKeys: string[] = [] - if (v?.huggingfacehub_api_type === 'hosted_inference_api') { - filteredKeys = [ - 'huggingfacehub_api_type', - 'huggingfacehub_api_token', - 'model_name', - 'model_type', - ] - } - if (v?.huggingfacehub_api_type === 'inference_endpoints') { - if (v.model_type === 'embeddings') { - filteredKeys = [ - 'huggingfacehub_api_type', - 'huggingfacehub_api_token', - 'huggingface_namespace', - 'model_name', - 'huggingfacehub_endpoint_url', - 'task_type', - 'model_type', - ] - } - else { - filteredKeys = [ - 'huggingfacehub_api_type', - 'huggingfacehub_api_token', - 'model_name', - 'huggingfacehub_endpoint_url', - 'task_type', - 'model_type', - ] - } - } - return filteredKeys.reduce((prev: FormValue, next: string) => { - prev[next] = v?.[next] || '' - return prev - }, {}) - }, - fields: [ - { - type: 'radio', - key: 'model_type', - required: true, - label: { - 'en': 'Model Type', - 'zh-Hans': '模型类型', - }, - options: [ - { - key: 'text-generation', - label: { - 'en': 'Text Generation', - 'zh-Hans': '文本生成', - }, - }, - // { - // key: 'chat', - // label: { - // 'en': 'Chat', - // 'zh-Hans': '聊天', - // }, - // }, - { - key: 'embeddings', - label: { - 'en': 'Embeddings', - 'zh-Hans': 'Embeddings', - }, - }, - ], - }, - { - type: 'radio', - key: 'huggingfacehub_api_type', - required: true, - label: { - 'en': 'Endpoint Type', - 'zh-Hans': '端点类型', - }, - options: [ - { - key: 'hosted_inference_api', - label: { - 'en': 'Hosted Inference API', - 'zh-Hans': 'Hosted Inference API', - }, - }, - { - key: 'inference_endpoints', - label: { - 'en': 'Inference Endpoints', - 'zh-Hans': 'Inference Endpoints', - }, - }, - ], - }, - { - type: 'text', - key: 'huggingfacehub_api_token', - required: true, - label: { - 'en': 'API Token', - 'zh-Hans': 'API Token', - }, - placeholder: { - 'en': 'Enter your Hugging Face Hub API Token here', - 'zh-Hans': '在此输入您的 Hugging Face Hub API Token', - }, - }, - { - hidden: (value?: FormValue) => !(value?.huggingfacehub_api_type === 'inference_endpoints' && value?.model_type === 'embeddings'), - type: 'text', - key: 'huggingface_namespace', - required: true, - label: { - 'en': 'User Name / Organization Name', - 'zh-Hans': '用户名 / 组织名称', - }, - placeholder: { - 'en': 'Enter your User Name / Organization Name here', - 'zh-Hans': '在此输入您的用户名 / 组织名称', - }, - }, - { - type: 'text', - key: 'model_name', - required: true, - label: { - 'en': 'Model Name', - 'zh-Hans': '模型名称', - }, - placeholder: { - 'en': 'Enter your Model Name here', - 'zh-Hans': '在此输入您的模型名称', - }, - }, - { - hidden: (value?: FormValue) => value?.huggingfacehub_api_type === 'hosted_inference_api', - type: 'text', - key: 'huggingfacehub_endpoint_url', - label: { - 'en': 'Endpoint URL', - 'zh-Hans': '端点 URL', - }, - placeholder: { - 'en': 'Enter your Endpoint URL here', - 'zh-Hans': '在此输入您的端点 URL', - }, - }, - { - hidden: (value?: FormValue) => value?.huggingfacehub_api_type === 'hosted_inference_api' || value?.model_type === 'embeddings', - type: 'radio', - key: 'task_type', - required: true, - label: { - 'en': 'Task', - 'zh-Hans': 'Task', - }, - options: (value?: FormValue) => { - if (value?.model_type === 'chat') { - return [{ - key: 'question-answer', - label: { - 'en': '问答', - 'zh-Hans': 'Question Answer', - }, - }] - } - return [ - { - key: 'text2text-generation', - label: { - 'en': 'Text-to-Text Generation', - 'zh-Hans': 'Text-to-Text Generation', - }, - }, - { - key: 'text-generation', - label: { - 'en': 'Text Generation', - 'zh-Hans': 'Text Generation', - }, - }, - ] - }, - }, - { - hidden: (value?: FormValue) => !(value?.huggingfacehub_api_type === 'inference_endpoints' && value?.model_type === 'embeddings'), - type: 'radio', - key: 'task_type', - required: true, - label: { - 'en': 'Task', - 'zh-Hans': 'Task', - }, - options: [ - { - key: 'feature-extraction', - label: { - 'en': 'Feature Extraction', - 'zh-Hans': 'Feature Extraction', - }, - }, - ], - }, - ], - }, -} - -export default config diff --git a/web/app/components/header/account-setting/model-page/configs/index.ts b/web/app/components/header/account-setting/model-page/configs/index.ts deleted file mode 100644 index 6f0c49daaa0c01..00000000000000 --- a/web/app/components/header/account-setting/model-page/configs/index.ts +++ /dev/null @@ -1,37 +0,0 @@ -import openai from './openai' -import anthropic from './anthropic' -import azure_openai from './azure_openai' -import replicate from './replicate' -import huggingface_hub from './huggingface_hub' -import wenxin from './wenxin' -import tongyi from './tongyi' -import spark from './spark' -import minimax from './minimax' -import chatglm from './chatglm' -import xinference from './xinference' -import openllm from './openllm' -import localai from './localai' -import zhipuai from './zhipuai' -import baichuan from './baichuan' -import cohere from './cohere' -import jina from './jina' - -export default { - openai, - anthropic, - azure_openai, - replicate, - huggingface_hub, - wenxin, - tongyi, - spark, - minimax, - chatglm, - xinference, - openllm, - localai, - zhipuai, - baichuan, - cohere, - jina, -} diff --git a/web/app/components/header/account-setting/model-page/configs/jina.tsx b/web/app/components/header/account-setting/model-page/configs/jina.tsx deleted file mode 100644 index aa77408a1f6e9c..00000000000000 --- a/web/app/components/header/account-setting/model-page/configs/jina.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import { ProviderEnum } from '../declarations' -import type { ProviderConfig } from '../declarations' -import { Jina, JinaText } from '@/app/components/base/icons/src/public/llm' - -const config: ProviderConfig = { - selector: { - name: { - 'en': 'Jina AI', - 'zh-Hans': 'Jina AI', - }, - icon: , - }, - item: { - key: ProviderEnum.jina, - titleIcon: { - 'en': , - 'zh-Hans': , - }, - hit: { - 'en': 'Embedding Model Supported', - 'zh-Hans': '支持 Embedding 模型', - }, - }, - modal: { - key: ProviderEnum.jina, - title: { - 'en': 'Embedding Model', - 'zh-Hans': 'Embedding 模型', - }, - icon: , - link: { - href: 'https://jina.ai/embeddings/', - label: { - 'en': 'Get your API key from Jina AI', - 'zh-Hans': '从 Jina AI 获取 API Key', - }, - }, - validateKeys: ['api_key'], - fields: [ - { - type: 'text', - key: 'api_key', - required: true, - label: { - 'en': 'API Key', - 'zh-Hans': 'API Key', - }, - placeholder: { - 'en': 'Enter your API key here', - 'zh-Hans': '在此输入您的 API Key', - }, - }, - ], - }, -} - -export default config diff --git a/web/app/components/header/account-setting/model-page/configs/localai.tsx b/web/app/components/header/account-setting/model-page/configs/localai.tsx deleted file mode 100644 index 8255fc78de33a8..00000000000000 --- a/web/app/components/header/account-setting/model-page/configs/localai.tsx +++ /dev/null @@ -1,176 +0,0 @@ -import { ProviderEnum } from '../declarations' -import type { FormValue, ProviderConfig } from '../declarations' -import { Localai, LocalaiText } from '@/app/components/base/icons/src/public/llm' - -const config: ProviderConfig = { - selector: { - name: { - 'en': 'LocalAI', - 'zh-Hans': 'LocalAI', - }, - icon: , - }, - item: { - key: ProviderEnum.localai, - titleIcon: { - 'en': , - 'zh-Hans': , - }, - disable: { - tip: { - 'en': 'Only supports the ', - 'zh-Hans': '仅支持', - }, - link: { - href: { - 'en': 'https://docs.dify.ai/getting-started/install-self-hosted', - 'zh-Hans': 'https://docs.dify.ai/v/zh-hans/getting-started/install-self-hosted', - }, - label: { - 'en': 'community open-source version', - 'zh-Hans': '社区开源版本', - }, - }, - }, - }, - modal: { - key: ProviderEnum.localai, - title: { - 'en': 'LocalAI', - 'zh-Hans': 'LocalAI', - }, - icon: , - link: { - href: 'https://github.com/go-skynet/LocalAI', - label: { - 'en': 'How to deploy LocalAI', - 'zh-Hans': '如何部署 LocalAI', - }, - }, - defaultValue: { - model_type: 'text-generation', - completion_type: 'completion', - }, - validateKeys: (v?: FormValue) => { - if (v?.model_type === 'text-generation') { - return [ - 'model_type', - 'model_name', - 'server_url', - 'completion_type', - ] - } - if (v?.model_type === 'embeddings') { - return [ - 'model_type', - 'model_name', - 'server_url', - ] - } - return [] - }, - filterValue: (v?: FormValue) => { - let filteredKeys: string[] = [] - if (v?.model_type === 'text-generation') { - filteredKeys = [ - 'model_type', - 'model_name', - 'server_url', - 'completion_type', - ] - } - if (v?.model_type === 'embeddings') { - filteredKeys = [ - 'model_type', - 'model_name', - 'server_url', - ] - } - return filteredKeys.reduce((prev: FormValue, next: string) => { - prev[next] = v?.[next] || '' - return prev - }, {}) - }, - fields: [ - { - type: 'radio', - key: 'model_type', - required: true, - label: { - 'en': 'Model Type', - 'zh-Hans': '模型类型', - }, - options: [ - { - key: 'text-generation', - label: { - 'en': 'Text Generation', - 'zh-Hans': '文本生成', - }, - }, - { - key: 'embeddings', - label: { - 'en': 'Embeddings', - 'zh-Hans': 'Embeddings', - }, - }, - ], - }, - { - type: 'text', - key: 'model_name', - required: true, - label: { - 'en': 'Model Name', - 'zh-Hans': '模型名称', - }, - placeholder: { - 'en': 'Enter your Model Name here', - 'zh-Hans': '在此输入您的模型名称', - }, - }, - { - hidden: (value?: FormValue) => value?.model_type === 'embeddings', - type: 'radio', - key: 'completion_type', - required: true, - label: { - 'en': 'Completion Type', - 'zh-Hans': 'Completion Type', - }, - options: [ - { - key: 'completion', - label: { - 'en': 'Completion', - 'zh-Hans': 'Completion', - }, - }, - { - key: 'chat_completion', - label: { - 'en': 'Chat Completion', - 'zh-Hans': 'Chat Completion', - }, - }, - ], - }, - { - type: 'text', - key: 'server_url', - required: true, - label: { - 'en': 'Server url', - 'zh-Hans': 'Server url', - }, - placeholder: { - 'en': 'Enter your Server Url, eg: https://example.com/xxx', - 'zh-Hans': '在此输入您的 Server Url,如:https://example.com/xxx', - }, - }, - ], - }, -} - -export default config diff --git a/web/app/components/header/account-setting/model-page/configs/minimax.tsx b/web/app/components/header/account-setting/model-page/configs/minimax.tsx deleted file mode 100644 index 58e481fe01abe4..00000000000000 --- a/web/app/components/header/account-setting/model-page/configs/minimax.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import { ProviderEnum } from '../declarations' -import type { ProviderConfig } from '../declarations' -import { Minimax, MinimaxText } from '@/app/components/base/icons/src/image/llm' - -const config: ProviderConfig = { - selector: { - name: { - 'en': 'MINIMAX', - 'zh-Hans': 'MINIMAX', - }, - icon: , - }, - item: { - key: ProviderEnum.minimax, - titleIcon: { - 'en': , - 'zh-Hans': , - }, - }, - modal: { - key: ProviderEnum.minimax, - title: { - 'en': 'MiniMax', - 'zh-Hans': 'MiniMax', - }, - icon: , - link: { - href: 'https://api.minimax.chat/user-center/basic-information/interface-key', - label: { - 'en': 'Get your API key from MiniMax', - 'zh-Hans': '从 MiniMax 获取 API Key', - }, - }, - validateKeys: [ - 'minimax_api_key', - 'minimax_group_id', - ], - fields: [ - { - type: 'text', - key: 'minimax_api_key', - required: true, - label: { - 'en': 'API Key', - 'zh-Hans': 'API Key', - }, - placeholder: { - 'en': 'Enter your API key here', - 'zh-Hans': '在此输入您的 API Key', - }, - }, - { - type: 'text', - key: 'minimax_group_id', - required: true, - label: { - 'en': 'Group ID', - 'zh-Hans': 'Group ID', - }, - placeholder: { - 'en': 'Enter your Group ID here', - 'zh-Hans': '在此输入您的 Group ID', - }, - }, - ], - }, -} - -export default config diff --git a/web/app/components/header/account-setting/model-page/configs/openai.tsx b/web/app/components/header/account-setting/model-page/configs/openai.tsx deleted file mode 100644 index 104f1e2a5953e2..00000000000000 --- a/web/app/components/header/account-setting/model-page/configs/openai.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import { ProviderEnum } from '../declarations' -import type { ProviderConfig } from '../declarations' -import { OpenaiBlack, OpenaiText, OpenaiTransparent } from '@/app/components/base/icons/src/public/llm' -import { IS_CE_EDITION } from '@/config' - -const config: ProviderConfig = { - selector: { - name: { - 'en': 'OpenAI', - 'zh-Hans': 'OpenAI', - }, - icon: , - }, - item: { - key: ProviderEnum.openai, - titleIcon: { - 'en': , - 'zh-Hans': , - }, - subTitleIcon: , - desc: { - 'en': 'Models provided by OpenAI, such as GPT-3.5-Turbo and GPT-4.', - 'zh-Hans': 'OpenAI 提供的模型,例如 GPT-3.5-Turbo 和 GPT-4。', - }, - bgColor: 'bg-gray-200', - }, - modal: { - key: ProviderEnum.openai, - title: { - 'en': 'OpenAI', - 'zh-Hans': 'OpenAI', - }, - icon: , - link: { - href: 'https://platform.openai.com/account/api-keys', - label: { - 'en': 'Get your API key from OpenAI', - 'zh-Hans': '从 OpenAI 获取 API Key', - }, - }, - validateKeys: ['openai_api_key'], - fields: [ - { - type: 'text', - key: 'openai_api_key', - required: true, - label: { - 'en': 'API Key', - 'zh-Hans': 'API Key', - }, - placeholder: { - 'en': 'Enter your API key here', - 'zh-Hans': '在此输入您的 API Key', - }, - }, - { - type: 'text', - key: 'openai_organization', - required: false, - label: { - 'en': 'Organization ID', - 'zh-Hans': '组织 ID', - }, - placeholder: { - 'en': 'Enter your Organization ID(Optional)', - 'zh-Hans': '在此输入您的组织 ID(选填)', - }, - }, - ...( - IS_CE_EDITION - ? [{ - type: 'text', - key: 'openai_api_base', - required: false, - label: { - 'en': 'Custom API Domain', - 'zh-Hans': '自定义 API 域名', - }, - placeholder: { - 'en': 'Enter your API domain, eg: https://example.com/xxx(Optional)', - 'zh-Hans': '在此输入您的 API 域名,如:https://example.com/xxx(选填)', - }, - help: { - 'en': 'You can configure your server compatible with the OpenAI API specification, or proxy mirror address', - 'zh-Hans': '可配置您的兼容 OpenAI API 规范的服务器,或者代理镜像地址', - }, - }] - : [] - ), - ], - }, -} -export default config diff --git a/web/app/components/header/account-setting/model-page/configs/openllm.tsx b/web/app/components/header/account-setting/model-page/configs/openllm.tsx deleted file mode 100644 index eaff4965135fa0..00000000000000 --- a/web/app/components/header/account-setting/model-page/configs/openllm.tsx +++ /dev/null @@ -1,114 +0,0 @@ -import { ProviderEnum } from '../declarations' -import type { ProviderConfig } from '../declarations' -import { Openllm, OpenllmText } from '@/app/components/base/icons/src/public/llm' - -const config: ProviderConfig = { - selector: { - name: { - 'en': 'OpenLLM', - 'zh-Hans': 'OpenLLM', - }, - icon: , - }, - item: { - key: ProviderEnum.openllm, - titleIcon: { - 'en': , - 'zh-Hans': , - }, - disable: { - tip: { - 'en': 'Only supports the ', - 'zh-Hans': '仅支持', - }, - link: { - href: { - 'en': 'https://docs.dify.ai/getting-started/install-self-hosted', - 'zh-Hans': 'https://docs.dify.ai/v/zh-hans/getting-started/install-self-hosted', - }, - label: { - 'en': 'community open-source version', - 'zh-Hans': '社区开源版本', - }, - }, - }, - }, - modal: { - key: ProviderEnum.openllm, - title: { - 'en': 'OpenLLM', - 'zh-Hans': 'OpenLLM', - }, - icon: , - link: { - href: 'https://github.com/bentoml/OpenLLM', - label: { - 'en': 'How to deploy OpenLLM', - 'zh-Hans': '如何部署 OpenLLM', - }, - }, - defaultValue: { - model_type: 'text-generation', - }, - validateKeys: [ - 'model_type', - 'model_name', - 'server_url', - ], - fields: [ - { - type: 'radio', - key: 'model_type', - required: true, - label: { - 'en': 'Model Type', - 'zh-Hans': '模型类型', - }, - options: [ - { - key: 'text-generation', - label: { - 'en': 'Text Generation', - 'zh-Hans': '文本生成', - }, - }, - { - key: 'embeddings', - label: { - 'en': 'Embeddings', - 'zh-Hans': 'Embeddings', - }, - }, - ], - }, - { - type: 'text', - key: 'model_name', - required: true, - label: { - 'en': 'Model Name', - 'zh-Hans': '模型名称', - }, - placeholder: { - 'en': 'Enter your Model Name here', - 'zh-Hans': '在此输入您的模型名称', - }, - }, - { - type: 'text', - key: 'server_url', - required: true, - label: { - 'en': 'Server url', - 'zh-Hans': 'Server url', - }, - placeholder: { - 'en': 'Enter your Server Url, eg: https://example.com/xxx', - 'zh-Hans': '在此输入您的 Server Url,如:https://example.com/xxx', - }, - }, - ], - }, -} - -export default config diff --git a/web/app/components/header/account-setting/model-page/configs/replicate.tsx b/web/app/components/header/account-setting/model-page/configs/replicate.tsx deleted file mode 100644 index 896e330e48d136..00000000000000 --- a/web/app/components/header/account-setting/model-page/configs/replicate.tsx +++ /dev/null @@ -1,122 +0,0 @@ -import { ProviderEnum } from '../declarations' -import type { ProviderConfig } from '../declarations' -import { Replicate, ReplicateText } from '@/app/components/base/icons/src/public/llm' - -const config: ProviderConfig = { - selector: { - name: { - 'en': 'Replicate', - 'zh-Hans': 'Replicate', - }, - icon: , - }, - item: { - key: ProviderEnum.replicate, - titleIcon: { - 'en': , - 'zh-Hans': , - }, - hit: { - 'en': '🐑 Llama 2 Supported', - 'zh-Hans': '🐑 Llama 2 已支持', - }, - }, - modal: { - key: ProviderEnum.replicate, - title: { - 'en': 'Replicate Model', - 'zh-Hans': 'Replicate Model', - }, - icon: , - link: { - href: 'https://replicate.com/account/api-tokens', - label: { - 'en': 'Get your API key from Replicate', - 'zh-Hans': '从 Replicate 获取 API Key', - }, - }, - defaultValue: { - model_type: 'text-generation', - }, - validateKeys: [ - 'model_type', - 'replicate_api_token', - 'model_name', - 'model_version', - ], - fields: [ - { - type: 'radio', - key: 'model_type', - required: true, - label: { - 'en': 'Model Type', - 'zh-Hans': '模型类型', - }, - options: [ - { - key: 'text-generation', - label: { - 'en': 'Text Generation', - 'zh-Hans': '文本生成', - }, - }, - // { - // key: 'chat', - // label: { - // 'en': 'Chat', - // 'zh-Hans': '聊天', - // }, - // }, - { - key: 'embeddings', - label: { - 'en': 'Embeddings', - 'zh-Hans': 'Embeddings', - }, - }, - ], - }, - { - type: 'text', - key: 'replicate_api_token', - required: true, - label: { - 'en': 'API Key', - 'zh-Hans': 'API Key', - }, - placeholder: { - 'en': 'Enter your Replicate API key here', - 'zh-Hans': '在此输入您的 Replicate API Key', - }, - }, - { - type: 'text', - key: 'model_name', - required: true, - label: { - 'en': 'Model Name', - 'zh-Hans': '模型名称', - }, - placeholder: { - 'en': 'Enter your Model Name here', - 'zh-Hans': '在此输入您的模型名称', - }, - }, - { - type: 'text', - key: 'model_version', - label: { - 'en': 'Model Version', - 'zh-Hans': '模型版本', - }, - placeholder: { - 'en': 'Enter your Model Version here', - 'zh-Hans': '在此输入您的模型版本', - }, - }, - ], - }, -} - -export default config diff --git a/web/app/components/header/account-setting/model-page/configs/spark.tsx b/web/app/components/header/account-setting/model-page/configs/spark.tsx deleted file mode 100644 index e7ede2002260fb..00000000000000 --- a/web/app/components/header/account-setting/model-page/configs/spark.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import { ProviderEnum } from '../declarations' -import type { ProviderConfig } from '../declarations' -import { IflytekSpark, IflytekSparkText, IflytekSparkTextCn } from '@/app/components/base/icons/src/public/llm' - -const config: ProviderConfig = { - selector: { - name: { - 'en': 'iFLYTEK SPARK', - 'zh-Hans': '讯飞星火', - }, - icon: , - }, - item: { - key: ProviderEnum.spark, - titleIcon: { - 'en': , - 'zh-Hans': , - }, - }, - modal: { - key: ProviderEnum.spark, - title: { - 'en': 'iFLYTEK SPARK', - 'zh-Hans': '讯飞星火', - }, - icon: , - link: { - href: 'https://www.xfyun.cn/solutions/xinghuoAPI', - label: { - 'en': 'Get your API key from iFLYTEK SPARK', - 'zh-Hans': '从讯飞星火获取 API Key', - }, - }, - validateKeys: [ - 'app_id', - 'api_key', - 'api_secret', - ], - fields: [ - { - type: 'text', - key: 'app_id', - required: true, - label: { - 'en': 'APPID', - 'zh-Hans': 'APPID', - }, - placeholder: { - 'en': 'Enter your APPID here', - 'zh-Hans': '在此输入您的 APPID', - }, - }, - { - type: 'text', - key: 'api_secret', - required: true, - label: { - 'en': 'APISecret', - 'zh-Hans': 'APISecret', - }, - placeholder: { - 'en': 'Enter your APISecret here', - 'zh-Hans': '在此输入您的 APISecret', - }, - }, - { - type: 'text', - key: 'api_key', - required: true, - label: { - 'en': 'APIKey', - 'zh-Hans': 'APIKey', - }, - placeholder: { - 'en': 'Enter your APIKey here', - 'zh-Hans': '在此输入您的 APIKey', - }, - }, - ], - }, -} - -export default config diff --git a/web/app/components/header/account-setting/model-page/configs/tongyi.tsx b/web/app/components/header/account-setting/model-page/configs/tongyi.tsx deleted file mode 100644 index 3faa812d9423f9..00000000000000 --- a/web/app/components/header/account-setting/model-page/configs/tongyi.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import { ProviderEnum } from '../declarations' -import type { ProviderConfig } from '../declarations' -import { Tongyi, TongyiText, TongyiTextCn } from '@/app/components/base/icons/src/image/llm' - -const config: ProviderConfig = { - selector: { - name: { - 'en': 'TONGYI QIANWEN', - 'zh-Hans': '通义千问', - }, - icon: , - }, - item: { - key: ProviderEnum.tongyi, - titleIcon: { - 'en': , - 'zh-Hans': , - }, - }, - modal: { - key: ProviderEnum.tongyi, - title: { - 'en': 'Tongyi', - 'zh-Hans': '通义千问', - }, - icon: , - link: { - href: 'https://dashscope.console.aliyun.com/api-key_management', - label: { - 'en': 'Get your API key from AliCloud', - 'zh-Hans': '从阿里云获取 API Key', - }, - }, - validateKeys: ['dashscope_api_key'], - fields: [ - { - type: 'text', - key: 'dashscope_api_key', - required: true, - label: { - 'en': 'API Key', - 'zh-Hans': 'API Key', - }, - placeholder: { - 'en': 'Enter your API key here', - 'zh-Hans': '在此输入您的 API Key', - }, - }, - ], - }, -} - -export default config diff --git a/web/app/components/header/account-setting/model-page/configs/wenxin.tsx b/web/app/components/header/account-setting/model-page/configs/wenxin.tsx deleted file mode 100644 index 56dae8769ef059..00000000000000 --- a/web/app/components/header/account-setting/model-page/configs/wenxin.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import { ProviderEnum } from '../declarations' -import type { ProviderConfig } from '../declarations' -import { Wxyy, WxyyText, WxyyTextCn } from '@/app/components/base/icons/src/image/llm' - -const config: ProviderConfig = { - selector: { - name: { - 'en': 'WENXIN YIYAN', - 'zh-Hans': '文心一言', - }, - icon: , - }, - item: { - key: ProviderEnum.wenxin, - titleIcon: { - 'en': , - 'zh-Hans': , - }, - }, - modal: { - key: ProviderEnum.wenxin, - title: { - 'en': 'WENXINYIYAN', - 'zh-Hans': '文心一言', - }, - icon: , - link: { - href: 'https://console.bce.baidu.com/qianfan/ais/console/applicationConsole/application', - label: { - 'en': 'Get your API key from Baidu', - 'zh-Hans': '从百度获取 API Key', - }, - }, - validateKeys: ['api_key', 'secret_key'], - fields: [ - { - type: 'text', - key: 'api_key', - required: true, - label: { - 'en': 'API Key', - 'zh-Hans': 'API Key', - }, - placeholder: { - 'en': 'Enter your API key here', - 'zh-Hans': '在此输入您的 API Key', - }, - }, - { - type: 'text', - key: 'secret_key', - required: true, - label: { - 'en': 'Secret Key', - 'zh-Hans': 'Secret Key', - }, - placeholder: { - 'en': 'Enter your Secret key here', - 'zh-Hans': '在此输入您的 Secret Key', - }, - }, - ], - }, -} - -export default config diff --git a/web/app/components/header/account-setting/model-page/configs/xinference.tsx b/web/app/components/header/account-setting/model-page/configs/xinference.tsx deleted file mode 100644 index d4eecf6bdf5acc..00000000000000 --- a/web/app/components/header/account-setting/model-page/configs/xinference.tsx +++ /dev/null @@ -1,135 +0,0 @@ -import { ProviderEnum } from '../declarations' -import type { ProviderConfig } from '../declarations' -import { XorbitsInference, XorbitsInferenceText } from '@/app/components/base/icons/src/public/llm' - -const config: ProviderConfig = { - selector: { - name: { - 'en': 'Xinference', - 'zh-Hans': 'Xinference', - }, - icon: , - }, - item: { - key: ProviderEnum.xinference, - titleIcon: { - 'en': , - 'zh-Hans': , - }, - disable: { - tip: { - 'en': 'Only supports the ', - 'zh-Hans': '仅支持', - }, - link: { - href: { - 'en': 'https://docs.dify.ai/getting-started/install-self-hosted', - 'zh-Hans': 'https://docs.dify.ai/v/zh-hans/getting-started/install-self-hosted', - }, - label: { - 'en': 'community open-source version', - 'zh-Hans': '社区开源版本', - }, - }, - }, - }, - modal: { - key: ProviderEnum.xinference, - title: { - 'en': 'Xinference', - 'zh-Hans': 'Xinference', - }, - icon: , - link: { - href: 'https://github.com/xorbitsai/inference', - label: { - 'en': 'How to deploy Xinference', - 'zh-Hans': '如何部署 Xinference', - }, - }, - defaultValue: { - model_type: 'text-generation', - }, - validateKeys: [ - 'model_type', - 'model_name', - 'server_url', - 'model_uid', - ], - fields: [ - { - type: 'radio', - key: 'model_type', - required: true, - label: { - 'en': 'Model Type', - 'zh-Hans': '模型类型', - }, - options: [ - { - key: 'text-generation', - label: { - 'en': 'Text Generation', - 'zh-Hans': '文本生成', - }, - }, - { - key: 'embeddings', - label: { - 'en': 'Embeddings', - 'zh-Hans': 'Embeddings', - }, - }, - { - key: 'reranking', - label: { - 'en': 'Rerank', - 'zh-Hans': 'Rerank', - }, - }, - ], - }, - { - type: 'text', - key: 'model_name', - required: true, - label: { - 'en': 'Model Name', - 'zh-Hans': '模型名称', - }, - placeholder: { - 'en': 'Enter your Model Name here', - 'zh-Hans': '在此输入您的模型名称', - }, - }, - { - type: 'text', - key: 'server_url', - required: true, - label: { - 'en': 'Server Url', - 'zh-Hans': 'Server Url', - }, - placeholder: { - 'en': 'Enter your Server url, eg: https://example.com/xxx', - 'zh-Hans': '在此输入您的 Server url,如:https://example.com/xxx', - }, - }, - { - type: 'text', - key: 'model_uid', - required: true, - label: { - 'en': 'Model UID', - 'zh-Hans': 'Model UID', - }, - placeholder: { - 'en': 'Enter your Model UID', - 'zh-Hans': '在此输入您的 Model UID', - }, - }, - ], - }, -} - -export default config diff --git a/web/app/components/header/account-setting/model-page/configs/zhipuai.tsx b/web/app/components/header/account-setting/model-page/configs/zhipuai.tsx deleted file mode 100644 index fcee358e76a2a1..00000000000000 --- a/web/app/components/header/account-setting/model-page/configs/zhipuai.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { ProviderEnum } from '../declarations' -import type { ProviderConfig } from '../declarations' -import { Zhipuai, ZhipuaiText, ZhipuaiTextCn } from '@/app/components/base/icons/src/public/llm' - -const config: ProviderConfig = { - selector: { - name: { - 'en': 'ZHIPU AI', - 'zh-Hans': '智谱 AI', - }, - icon: , - }, - item: { - key: ProviderEnum.zhipuai, - titleIcon: { - 'en': , - 'zh-Hans': , - }, - }, - modal: { - key: ProviderEnum.zhipuai, - title: { - 'en': 'ZHIPU AI', - 'zh-Hans': '智谱 AI', - }, - icon: , - link: { - href: 'https://open.bigmodel.cn/usercenter/apikeys', - label: { - 'en': 'Get your API key from ZHIPU AI', - 'zh-Hans': '从智谱 AI 获取 API Key', - }, - }, - validateKeys: [ - 'api_key', - ], - fields: [ - { - type: 'text', - key: 'api_key', - required: true, - label: { - 'en': 'APIKey', - 'zh-Hans': 'APIKey', - }, - placeholder: { - 'en': 'Enter your APIKey here', - 'zh-Hans': '在此输入您的 APIKey', - }, - }, - ], - }, -} - -export default config diff --git a/web/app/components/header/account-setting/model-page/declarations.ts b/web/app/components/header/account-setting/model-page/declarations.ts deleted file mode 100644 index 79a4e255cdd42c..00000000000000 --- a/web/app/components/header/account-setting/model-page/declarations.ts +++ /dev/null @@ -1,163 +0,0 @@ -import type { ReactElement } from 'react' -import type { ModelModeType } from '@/types/app' - -export type FormValue = Record - -export type TypeWithI18N = { - 'en': T - 'zh-Hans': T -} - -export type Option = { - key: string - label: TypeWithI18N -} - -export type ProviderSelector = { - name: TypeWithI18N - icon: ReactElement -} - -export type Field = { - hidden?: (v?: FormValue) => boolean - type: string - key: string - required?: boolean - label: TypeWithI18N - options?: Option[] | ((v: FormValue) => Option[]) - placeholder?: TypeWithI18N - help?: TypeWithI18N -} - -export enum ProviderEnum { - 'openai' = 'openai', - 'anthropic' = 'anthropic', - 'replicate' = 'replicate', - 'azure_openai' = 'azure_openai', - 'huggingface_hub' = 'huggingface_hub', - 'tongyi' = 'tongyi', - 'wenxin' = 'wenxin', - 'spark' = 'spark', - 'minimax' = 'minimax', - 'chatglm' = 'chatglm', - 'xinference' = 'xinference', - 'openllm' = 'openllm', - 'localai' = 'localai', - 'zhipuai' = 'zhipuai', - 'baichuan' = 'baichuan', - 'cohere' = 'cohere', - 'jina' = 'jina', -} - -export type ProviderConfigItem = { - key: ProviderEnum - titleIcon: TypeWithI18N - subTitleIcon?: ReactElement - desc?: TypeWithI18N - bgColor?: string - hit?: TypeWithI18N - disable?: { - tip: TypeWithI18N - link: { - href: TypeWithI18N - label: TypeWithI18N - } - } -} - -export enum ModelType { - textGeneration = 'text-generation', - embeddings = 'embeddings', - speech2text = 'speech2text', - reranking = 'reranking', -} - -export enum ModelFeature { - agentThought = 'agent_thought', - vision = 'vision', -} - -// backend defined model struct: /console/api/workspaces/current/models/model-type/:model_type -export type BackendModel = { - model_name: string - model_display_name: string // not always exist - model_type: ModelType - model_mode: ModelModeType - model_provider: { - provider_name: ProviderEnum - provider_type: PreferredProviderTypeEnum - quota_type: 'trial' | 'paid' - quota_unit: 'times' | 'tokens' - quota_used: number - quota_limit: number - } - features: ModelFeature[] -} - -export type ProviderConfigModal = { - key: ProviderEnum - title: TypeWithI18N - icon: ReactElement - defaultValue?: FormValue - validateKeys?: string[] | ((v?: FormValue) => string[]) - filterValue?: (v?: FormValue) => FormValue - fields: Field[] - link: { - href: string - label: TypeWithI18N - } -} - -export type ProviderConfig = { - selector: ProviderSelector - item: ProviderConfigItem - modal: ProviderConfigModal -} - -export enum PreferredProviderTypeEnum { - 'system' = 'system', - 'custom' = 'custom', -} -export enum ModelFlexibilityEnum { - 'fixed' = 'fixed', - 'configurable' = 'configurable', -} - -export type ProviderCommon = { - provider_name: ProviderEnum - provider_type: PreferredProviderTypeEnum - is_valid: boolean - last_used: number -} - -export type ProviderWithQuota = { - quota_type: string - quota_unit: string - quota_limit: number - quota_used: number -} & ProviderCommon - -export type ProviderWithConfig = { - config: Record -} & ProviderCommon - -export type Model = { - model_name: string - model_type: string - config: Record -} - -export type ProviderWithModels = { - models: Model[] -} & ProviderCommon - -export type ProviderInstance = ProviderWithQuota | ProviderWithConfig | ProviderWithModels - -export type Provider = { - preferred_provider_type: PreferredProviderTypeEnum - model_flexibility: ModelFlexibilityEnum - providers: ProviderInstance[] -} -export type ProviderMap = { - [k in ProviderEnum]: Provider -} diff --git a/web/app/components/header/account-setting/model-page/index.tsx b/web/app/components/header/account-setting/model-page/index.tsx deleted file mode 100644 index 1e7c8802467ef9..00000000000000 --- a/web/app/components/header/account-setting/model-page/index.tsx +++ /dev/null @@ -1,267 +0,0 @@ -import { useState } from 'react' -import useSWR from 'swr' -import { useTranslation } from 'react-i18next' -import { useContext } from 'use-context-selector' -import type { - FormValue, - ProviderConfigModal, - ProviderEnum, -} from './declarations' -import ModelCard from './model-card' -import ModelItem from './model-item' -import ModelModal from './model-modal' -import SystemModel from './system-model' -import config from './configs' -import { ConfigurableProviders } from './utils' -import { - changeModelProviderPriority, - deleteModelProvider, - deleteModelProviderModel, - fetchModelProviders, - setModelProvider, -} from '@/service/common' -import { useToastContext } from '@/app/components/base/toast' -import Confirm from '@/app/components/base/confirm/common' -import { ModelType } from '@/app/components/header/account-setting/model-page/declarations' -import { useEventEmitterContextContext } from '@/context/event-emitter' -import { useProviderContext } from '@/context/provider-context' -import I18n from '@/context/i18n' -import { AlertTriangle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback' - -const MODEL_CARD_LIST = [ - config.openai, - config.anthropic, -] - -type DeleteModel = { - model_name: string - model_type: string -} - -const ModelPage = () => { - const { t } = useTranslation() - const { locale } = useContext(I18n) - const { - updateModelList, - textGenerationDefaultModel, - embeddingsDefaultModel, - speech2textDefaultModel, - rerankDefaultModel, - } = useProviderContext() - const { data: providers, mutate: mutateProviders } = useSWR('/workspaces/current/model-providers', fetchModelProviders) - const [showModal, setShowModal] = useState(false) - const { notify } = useToastContext() - const { eventEmitter } = useEventEmitterContextContext() - const [modelModalConfig, setModelModalConfig] = useState(undefined) - const [confirmShow, setConfirmShow] = useState(false) - const [deleteModel, setDeleteModel] = useState() - const [modalMode, setModalMode] = useState('add') - - let modelList = [] - - if (locale === 'en') { - modelList = [ - config.azure_openai, - config.replicate, - config.huggingface_hub, - config.cohere, - config.zhipuai, - config.baichuan, - config.spark, - config.minimax, - config.tongyi, - config.wenxin, - config.jina, - config.chatglm, - config.xinference, - config.openllm, - config.localai, - ] - } - else { - modelList = [ - config.huggingface_hub, - config.cohere, - config.zhipuai, - config.spark, - config.baichuan, - config.minimax, - config.azure_openai, - config.replicate, - config.tongyi, - config.wenxin, - config.jina, - config.chatglm, - config.xinference, - config.openllm, - config.localai, - ] - } - - const handleOpenModal = (newModelModalConfig: ProviderConfigModal | undefined, editValue?: FormValue) => { - if (newModelModalConfig) { - setShowModal(true) - const defaultValue = editValue ? { ...newModelModalConfig.defaultValue, ...editValue } : newModelModalConfig.defaultValue - setModelModalConfig({ - ...newModelModalConfig, - defaultValue, - }) - if (editValue) - setModalMode('edit') - else - setModalMode('add') - } - } - const handleCancelModal = () => { - setShowModal(false) - } - const handleUpdateProvidersAndModelList = () => { - updateModelList(ModelType.textGeneration) - updateModelList(ModelType.embeddings) - updateModelList(ModelType.speech2text) - updateModelList(ModelType.reranking) - mutateProviders() - } - const handleSave = async (originValue?: FormValue) => { - if (originValue && modelModalConfig) { - const v = modelModalConfig.filterValue ? modelModalConfig.filterValue(originValue) : originValue - let body, url - if (ConfigurableProviders.includes(modelModalConfig.key)) { - const { model_name, model_type, ...config } = v - body = { - model_name, - model_type, - config, - } - url = `/workspaces/current/model-providers/${modelModalConfig.key}/models` - } - else { - body = { - config: v, - } - url = `/workspaces/current/model-providers/${modelModalConfig.key}` - } - - try { - eventEmitter?.emit('provider-save') - const res = await setModelProvider({ url, body }) - if (res.result === 'success') { - notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') }) - handleUpdateProvidersAndModelList() - handleCancelModal() - } - eventEmitter?.emit('') - } - catch (e) { - eventEmitter?.emit('') - } - } - } - - const handleConfirm = (deleteModel: DeleteModel, providerKey: ProviderEnum) => { - setDeleteModel({ ...deleteModel, providerKey }) - setConfirmShow(true) - } - - const handleOperate = async ({ type, value }: Record, provierKey: ProviderEnum) => { - if (type === 'delete') { - if (!value) { - const res = await deleteModelProvider({ url: `/workspaces/current/model-providers/${provierKey}` }) - if (res.result === 'success') { - notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') }) - handleUpdateProvidersAndModelList() - } - } - else { - handleConfirm(value, provierKey) - } - } - - if (type === 'priority') { - const res = await changeModelProviderPriority({ - url: `/workspaces/current/model-providers/${provierKey}/preferred-provider-type`, - body: { - preferred_provider_type: value, - }, - }) - if (res.result === 'success') { - notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') }) - mutateProviders() - } - } - } - - const handleDeleteModel = async () => { - const { model_name, model_type, providerKey } = deleteModel || {} - const res = await deleteModelProviderModel({ - url: `/workspaces/current/model-providers/${providerKey}/models?model_name=${model_name}&model_type=${model_type}`, - }) - if (res.result === 'success') { - notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') }) - setConfirmShow(false) - handleUpdateProvidersAndModelList() - } - } - - const defaultModelNotConfigured = !textGenerationDefaultModel && !embeddingsDefaultModel && !speech2textDefaultModel && !rerankDefaultModel - - return ( -
-
- { - defaultModelNotConfigured - ? ( -
- - {t('common.modelProvider.notConfigured')} -
- ) - :
{t('common.modelProvider.models')}
- } - mutateProviders()} /> -
-
- { - MODEL_CARD_LIST.map((model, index) => ( - handleOpenModal(model.modal, editValue)} - onOperate={v => handleOperate(v, model.item.key)} - /> - )) - } -
- { - modelList.map((model, index) => ( - handleOpenModal(model.modal, editValue)} - onOperate={v => handleOperate(v, model.item.key)} - onUpdate={mutateProviders} - /> - )) - } - - setConfirmShow(false)} - title={deleteModel?.model_name || ''} - desc={t('common.modelProvider.item.deleteDesc', { modelName: deleteModel?.model_name }) || ''} - onConfirm={handleDeleteModel} - confirmWrapperClassName='!z-30' - /> -
- ) -} - -export default ModelPage diff --git a/web/app/components/header/account-setting/model-page/model-card/Quota.tsx b/web/app/components/header/account-setting/model-page/model-card/Quota.tsx deleted file mode 100644 index 0a0fd5a1c20821..00000000000000 --- a/web/app/components/header/account-setting/model-page/model-card/Quota.tsx +++ /dev/null @@ -1,117 +0,0 @@ -import { useState } from 'react' -import type { FC } from 'react' -import { useTranslation } from 'react-i18next' -import type { Provider, ProviderWithQuota } from '../declarations' -import Tooltip from '@/app/components/base/tooltip' -import { InfoCircle } from '@/app/components/base/icons/src/vender/line/general' -import { getPayUrl } from '@/service/common' -import Button from '@/app/components/base/button' -import { formatNumber } from '@/utils/format' - -type QuotaProps = { - currentProvider: Provider -} -const Quota: FC = ({ - currentProvider, -}) => { - const { t } = useTranslation() - const [loading, setLoading] = useState(false) - const systemTrial = currentProvider.providers.find(p => p.provider_type === 'system' && (p as ProviderWithQuota)?.quota_type === 'trial') as ProviderWithQuota - const systemPaid = currentProvider.providers.find(p => p.provider_type === 'system' && (p as ProviderWithQuota)?.quota_type === 'paid') as ProviderWithQuota - const QUOTA_UNIT_MAP: Record = { - times: t('common.modelProvider.card.callTimes'), - tokens: 'Tokens', - } - - const renderStatus = () => { - const totalQuota = (systemPaid?.is_valid ? systemPaid.quota_limit : 0) + systemTrial.quota_limit - const totalUsed = (systemPaid?.is_valid ? systemPaid.quota_used : 0) + systemTrial.quota_used - - if (totalQuota === totalUsed) { - return ( -
- {t('common.modelProvider.card.quotaExhausted')} -
- ) - } - if (systemPaid?.is_valid) { - return ( -
- {t('common.modelProvider.card.paid')} -
- ) - } - return ( -
- {t('common.modelProvider.card.onTrial')} -
- ) - } - - const renderQuota = () => { - if (systemPaid?.is_valid) - return formatNumber(systemPaid.quota_limit - systemPaid.quota_used) - - if (systemTrial.is_valid) - return formatNumber(systemTrial.quota_limit - systemTrial.quota_used) - } - const renderUnit = () => { - if (systemPaid?.is_valid) - return QUOTA_UNIT_MAP[systemPaid.quota_unit] - - if (systemTrial.is_valid) - return QUOTA_UNIT_MAP[systemTrial.quota_unit] - } - const handleGetPayUrl = async () => { - setLoading(true) - try { - const res = await getPayUrl(`/workspaces/current/model-providers/${systemPaid.provider_name}/checkout-url`) - - window.location.href = res.url - } - finally { - setLoading(false) - } - } - - return ( -
-
-
-
- {t('common.modelProvider.card.quota')} -
- {renderStatus()} -
-
-
{renderQuota()}
-
- {renderUnit()} -
- {t('common.modelProvider.card.tip')}
- } - > - - -
-
- { - systemPaid && ( - - ) - } -
- ) -} - -export default Quota diff --git a/web/app/components/header/account-setting/model-page/model-card/index.tsx b/web/app/components/header/account-setting/model-page/model-card/index.tsx deleted file mode 100644 index 3d5bdd42892bff..00000000000000 --- a/web/app/components/header/account-setting/model-page/model-card/index.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import type { FC } from 'react' -import { useTranslation } from 'react-i18next' -import { useContext } from 'use-context-selector' -import type { - FormValue, - Provider, - ProviderConfigItem, - ProviderWithConfig, -} from '../declarations' -import Indicator from '../../../indicator' -import Selector from '../selector' -import Quota from './Quota' -import { IS_CE_EDITION } from '@/config' -import I18n from '@/context/i18n' -import { Plus } from '@/app/components/base/icons/src/vender/line/general' - -type ModelCardProps = { - currentProvider?: Provider - modelItem: ProviderConfigItem - onOpenModal: (v?: FormValue) => void - onOperate: (v: Record) => void -} - -const ModelCard: FC = ({ - currentProvider, - modelItem, - onOpenModal, - onOperate, -}) => { - const { locale } = useContext(I18n) - const { t } = useTranslation() - const custom = currentProvider?.providers.find(p => p.provider_type === 'custom') as ProviderWithConfig - - return ( -
-
-
-
- {modelItem.titleIcon[locale]} -
-
{modelItem.desc?.[locale]}
-
- {modelItem.subTitleIcon} -
- { - !IS_CE_EDITION && currentProvider && - } - { - custom?.is_valid - ? ( -
- -
API key
-
onOpenModal(custom?.config)} - > - {t('common.operation.edit')} -
- -
- ) - : ( -
onOpenModal()} - > - -
{t('common.modelProvider.addApiKey')}
-
- ) - } -
- ) -} - -export default ModelCard diff --git a/web/app/components/header/account-setting/model-page/model-item/Card.tsx b/web/app/components/header/account-setting/model-page/model-item/Card.tsx deleted file mode 100644 index 5b2dfbd30eceaa..00000000000000 --- a/web/app/components/header/account-setting/model-page/model-item/Card.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import type { FC } from 'react' -import { useTranslation } from 'react-i18next' -import Indicator from '../../../indicator' -import Selector from '../selector' -import type { Model, ProviderEnum } from '../declarations' -import { ProviderEnum as ProviderEnumValue } from '../declarations' -import Button from '@/app/components/base/button' - -type CardProps = { - providerType: ProviderEnum - models: Model[] - onOpenModal: (v: Omit & Model['config']) => void - onOperate: (v: Record) => void -} - -const Card: FC = ({ - providerType, - models, - onOpenModal, - onOperate, -}) => { - const { t } = useTranslation() - - const renderDesc = (model: Model) => { - if (providerType === ProviderEnumValue.azure_openai) - return model.config.openai_api_base - if (providerType === ProviderEnumValue.replicate) - return `version: ${model.config.model_version}` - if (providerType === ProviderEnumValue.huggingface_hub) - return model.config.huggingfacehub_endpoint_url - } - - return ( -
- { - models.map(model => ( -
-
-
- {model.model_name} -
{model.model_type}
-
-
- {renderDesc(model)} -
-
-
- - - onOperate({ ...v, value: model })} - className={open => `${open && '!bg-gray-100 shadow-none'} flex justify-center items-center w-7 h-7 bg-white rounded-md border-[0.5px] border-gray-200 shadow-xs cursor-pointer hover:bg-gray-100`} - deleteText={t('common.operation.remove') || ''} - /> -
-
- )) - } -
- ) -} - -export default Card diff --git a/web/app/components/header/account-setting/model-page/model-item/FreeQuota.tsx b/web/app/components/header/account-setting/model-page/model-item/FreeQuota.tsx deleted file mode 100644 index b66563d0186094..00000000000000 --- a/web/app/components/header/account-setting/model-page/model-item/FreeQuota.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import { useState } from 'react' -import type { FC } from 'react' -import { useTranslation } from 'react-i18next' -import { useContext } from 'use-context-selector' -import type { ProviderConfigItem, TypeWithI18N } from '../declarations' -import { ProviderEnum as ProviderEnumValue } from '../declarations' -import s from './index.module.css' -import I18n from '@/context/i18n' -import Button from '@/app/components/base/button' -import { submitFreeQuota } from '@/service/common' -import { LinkExternal01 } from '@/app/components/base/icons/src/vender/line/general' - -const TIP_MAP: { [k: string]: TypeWithI18N } = { - [ProviderEnumValue.minimax]: { - 'en': 'Earn 1 million tokens for free', - 'zh-Hans': '免费获取 100 万个 token', - }, - [ProviderEnumValue.spark]: { - 'en': 'Earn 3 million tokens (v3.0) for free', - 'zh-Hans': '免费获取 300 万个 token (v3.0)', - }, - [ProviderEnumValue.zhipuai]: { - 'en': 'Earn 10 million tokens for free', - 'zh-Hans': '免费获取 1000 万个 token', - }, -} -type FreeQuotaProps = { - modelItem: ProviderConfigItem - onUpdate: () => void -} -const FreeQuota: FC = ({ - modelItem, - onUpdate, -}) => { - const { locale } = useContext(I18n) - const { t } = useTranslation() - const [loading, setLoading] = useState(false) - - const handleClick = async () => { - try { - setLoading(true) - const res = await submitFreeQuota(`/workspaces/current/model-providers/${modelItem.key}/free-quota-submit`) - - if (res.type === 'redirect' && res.redirect_url) - window.location.href = res.redirect_url - else if (res.type === 'submit' && res.result === 'success') - onUpdate() - } - finally { - setLoading(false) - } - } - - return ( -
- 📣 -
{TIP_MAP[modelItem.key][locale]}
-
·
- - {t('common.modelProvider.freeQuota.howToEarn')} - - - -
-
- ) -} - -export default FreeQuota diff --git a/web/app/components/header/account-setting/model-page/model-item/QuotaCard.tsx b/web/app/components/header/account-setting/model-page/model-item/QuotaCard.tsx deleted file mode 100644 index 71dcaf3365940e..00000000000000 --- a/web/app/components/header/account-setting/model-page/model-item/QuotaCard.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import type { FC } from 'react' -import { useTranslation } from 'react-i18next' -import { formatNumber } from '@/utils/format' - -type QuotaCardProps = { - remainTokens: number -} - -const QuotaCard: FC = ({ - remainTokens, -}) => { - const { t } = useTranslation() - - return ( -
-
-
- {t('common.modelProvider.item.freeQuota')} -
-
- {formatNumber(remainTokens)} -
Tokens
-
-
-
- ) -} - -export default QuotaCard diff --git a/web/app/components/header/account-setting/model-page/model-item/Setting.tsx b/web/app/components/header/account-setting/model-page/model-item/Setting.tsx deleted file mode 100644 index 7c24452ecbb767..00000000000000 --- a/web/app/components/header/account-setting/model-page/model-item/Setting.tsx +++ /dev/null @@ -1,104 +0,0 @@ -import type { FC } from 'react' -import { useTranslation } from 'react-i18next' -import { useContext } from 'use-context-selector' -import type { FormValue, Provider, ProviderConfigItem, ProviderWithConfig, ProviderWithQuota } from '../declarations' -import { ProviderEnum } from '../declarations' -import Indicator from '../../../indicator' -import Selector from '../selector' -import FreeQuota from './FreeQuota' -import I18n from '@/context/i18n' -import Button from '@/app/components/base/button' -import { IS_CE_EDITION } from '@/config' - -type SettingProps = { - currentProvider?: Provider - modelItem: ProviderConfigItem - onOpenModal: (v?: FormValue) => void - onOperate: (v: Record) => void - onUpdate: () => void -} - -const Setting: FC = ({ - currentProvider, - modelItem, - onOpenModal, - onOperate, - onUpdate, -}) => { - const { locale } = useContext(I18n) - const { t } = useTranslation() - const configurable = currentProvider?.model_flexibility === 'configurable' - const systemFree = currentProvider?.providers.find(p => p.provider_type === 'system' && (p as ProviderWithQuota).quota_type === 'free') as ProviderWithQuota - const custom = currentProvider?.providers.find(p => p.provider_type === 'custom') as ProviderWithConfig - - return ( -
- { - (modelItem.key === ProviderEnum.minimax || modelItem.key === ProviderEnum.spark || modelItem.key === ProviderEnum.zhipuai) && systemFree && !systemFree?.is_valid && !IS_CE_EDITION && locale === 'zh-Hans' && ( - - ) - } - { - modelItem.disable && !IS_CE_EDITION && ( -
- {modelItem.disable.tip[locale]} - - {modelItem.disable.link.label[locale]} - -
-
- ) - } - { - configurable && ( - - ) - } - { - !configurable && custom?.config && ( -
- - - `${open && '!bg-gray-100 shadow-none'} flex justify-center items-center w-7 h-7 bg-white rounded-md border-[0.5px] border-gray-200 shadow-xs cursor-pointer hover:bg-gray-100`} - /> -
- ) - } - { - !configurable && !custom?.config && ( - - ) - } -
- ) -} - -export default Setting diff --git a/web/app/components/header/account-setting/model-page/model-item/index.module.css b/web/app/components/header/account-setting/model-page/model-item/index.module.css deleted file mode 100644 index 88c9fd015ea319..00000000000000 --- a/web/app/components/header/account-setting/model-page/model-item/index.module.css +++ /dev/null @@ -1,4 +0,0 @@ -.vender { - background: linear-gradient(131deg, #2250F2 0%, #0EBCF3 100%); - background-clip: text; -} \ No newline at end of file diff --git a/web/app/components/header/account-setting/model-page/model-item/index.tsx b/web/app/components/header/account-setting/model-page/model-item/index.tsx deleted file mode 100644 index 228aa22fa930ed..00000000000000 --- a/web/app/components/header/account-setting/model-page/model-item/index.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import type { FC } from 'react' -import { useContext } from 'use-context-selector' -import type { - FormValue, - Provider, - ProviderConfigItem, - ProviderWithModels, - ProviderWithQuota, -} from '../declarations' -import Setting from './Setting' -import Card from './Card' -import QuotaCard from './QuotaCard' -import I18n from '@/context/i18n' -import { IS_CE_EDITION } from '@/config' - -type ModelItemProps = { - currentProvider?: Provider - modelItem: ProviderConfigItem - onOpenModal: (v?: FormValue) => void - onOperate: (v: Record) => void - onUpdate: () => void -} - -const ModelItem: FC = ({ - currentProvider, - modelItem, - onOpenModal, - onOperate, - onUpdate, -}) => { - const { locale } = useContext(I18n) - const custom = currentProvider?.providers.find(p => p.provider_type === 'custom') as ProviderWithModels - const systemFree = currentProvider?.providers.find(p => p.provider_type === 'system' && (p as ProviderWithQuota).quota_type === 'free') as ProviderWithQuota - - return ( -
-
-
- {modelItem.titleIcon[locale]} - { - modelItem.hit && ( -
{modelItem.hit[locale]}
- ) - } -
- -
- { - !!custom?.models?.length && ( - - ) - } - { - systemFree?.is_valid && !IS_CE_EDITION && ( - - ) - } -
- ) -} - -export default ModelItem diff --git a/web/app/components/header/account-setting/model-page/model-modal/Form.tsx b/web/app/components/header/account-setting/model-page/model-modal/Form.tsx deleted file mode 100644 index bd101702959718..00000000000000 --- a/web/app/components/header/account-setting/model-page/model-modal/Form.tsx +++ /dev/null @@ -1,208 +0,0 @@ -import { useEffect, useState } from 'react' -import type { Dispatch, FC, SetStateAction } from 'react' -import { useContext } from 'use-context-selector' -import { type Field, type FormValue, type ProviderConfigModal, ProviderEnum } from '../declarations' -import { useValidate } from '../../key-validator/hooks' -import { ValidatingTip } from '../../key-validator/ValidateStatus' -import { validateModelProviderFn } from '../utils' -import Input from './Input' -import I18n from '@/context/i18n' -import { SimpleSelect } from '@/app/components/base/select' - -type FormProps = { - modelModal?: ProviderConfigModal - initValue?: FormValue - fields: Field[] - onChange: (v: FormValue) => void - onValidatedError: (v: string) => void - mode: string - cleared: boolean - onClearedChange: Dispatch> - onValidating: (validating: boolean) => void -} - -const nameClassName = ` -py-2 text-sm text-gray-900 -` - -const Form: FC = ({ - modelModal, - initValue = {}, - fields, - onChange, - onValidatedError, - mode, - cleared, - onClearedChange, - onValidating, -}) => { - const { locale } = useContext(I18n) - const [value, setValue] = useState(initValue) - const [validate, validating, validatedStatusState] = useValidate(value) - const [changeKey, setChangeKey] = useState('') - - useEffect(() => { - onValidatedError(validatedStatusState.message || '') - }, [validatedStatusState, onValidatedError]) - useEffect(() => { - onValidating(validating) - }, [validating, onValidating]) - - const updateValue = (v: FormValue) => { - setValue(v) - onChange(v) - } - - const handleMultiFormChange = (v: FormValue, newChangeKey: string) => { - updateValue(v) - setChangeKey(newChangeKey) - - const validateKeys = (typeof modelModal?.validateKeys === 'function' ? modelModal?.validateKeys(v) : modelModal?.validateKeys) || [] - if (validateKeys.length) { - validate({ - before: () => { - for (let i = 0; i < validateKeys.length; i++) { - if (!v[validateKeys[i]]) - return false - } - return true - }, - run: () => { - return validateModelProviderFn(modelModal!.key, modelModal?.filterValue ? modelModal?.filterValue(v) : v) - }, - }) - } - } - - const handleClear = (saveValue?: FormValue) => { - const needClearFields = modelModal?.fields.filter(field => field.type !== 'radio') - const newValue: Record = {} - needClearFields?.forEach((field) => { - newValue[field.key] = '' - }) - updateValue({ ...value, ...newValue, ...saveValue }) - onClearedChange(true) - } - - const handleFormChange = (k: string, v: string) => { - if (mode === 'edit' && !cleared) { - handleClear({ [k]: v }) - } - else { - const extraValue: Record = {} - if ( - ( - (k === 'model_type' && v === 'embeddings' && value.huggingfacehub_api_type === 'inference_endpoints') - || (k === 'huggingfacehub_api_type' && v === 'inference_endpoints' && value.model_type === 'embeddings') - ) - && modelModal?.key === ProviderEnum.huggingface_hub - ) - extraValue.task_type = 'feature-extraction' - - if ( - ( - (k === 'model_type' && v === 'text-generation' && value.huggingfacehub_api_type === 'inference_endpoints') - || (k === 'huggingfacehub_api_type' && v === 'inference_endpoints' && value.model_type === 'text-generation') - ) - && modelModal?.key === ProviderEnum.huggingface_hub - ) - extraValue.task_type = 'text-generation' - - if ( - ( - (k === 'model_type' && v === 'chat' && value.huggingfacehub_api_type === 'inference_endpoints') - || (k === 'huggingfacehub_api_type' && v === 'inference_endpoints' && value.model_type === 'chat') - ) - && modelModal?.key === ProviderEnum.huggingface_hub - ) - extraValue.task_type = 'question-answer' - - handleMultiFormChange({ ...value, [k]: v, ...extraValue }, k) - } - } - - const handleFocus = () => { - if (mode === 'edit' && !cleared) - handleClear() - } - - const renderField = (field: Field) => { - const hidden = typeof field.hidden === 'function' ? field.hidden(value) : field.hidden - - if (hidden) - return null - - if (field.type === 'text') { - return ( -
-
{field.label[locale]}
- handleMultiFormChange(v, field.key)} - onFocus={handleFocus} - validatedStatusState={validatedStatusState} - /> - {validating && changeKey === field.key && } -
- ) - } - - if (field.type === 'radio') { - const options = typeof field.options === 'function' ? field.options(value) : field.options - - return ( -
-
{field.label[locale]}
-
- { - options?.map(option => ( -
handleFormChange(field.key, option.key)} - key={`${field.key}-${option.key}`} - > -
-
{option.label[locale]}
-
- )) - } -
- {validating && changeKey === field.key && } -
- ) - } - - if (field.type === 'select') { - const options = typeof field.options === 'function' ? field.options(value) : field.options - - return ( -
-
{field.label[locale]}
- ({ value: option.key, name: option.label[locale] }))} - onSelect={item => handleFormChange(field.key, item.value as string)} - /> - {validating && changeKey === field.key && } -
- ) - } - } - - return ( -
- { - fields.map(field => renderField(field)) - } -
- ) -} - -export default Form diff --git a/web/app/components/header/account-setting/model-page/model-modal/Input.tsx b/web/app/components/header/account-setting/model-page/model-modal/Input.tsx deleted file mode 100644 index f78622f1182c8f..00000000000000 --- a/web/app/components/header/account-setting/model-page/model-modal/Input.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import type { FC } from 'react' -import { useContext } from 'use-context-selector' -import type { Field, FormValue } from '../declarations' -import { ValidatedSuccessIcon } from '../../key-validator/ValidateStatus' -import { ValidatedStatus } from '../../key-validator/declarations' -import type { ValidatedStatusState } from '../../key-validator/declarations' -import I18n from '@/context/i18n' - -type InputProps = { - field: Field - value: FormValue - onChange: (v: FormValue) => void - onFocus: () => void - validatedStatusState: ValidatedStatusState -} -const Input: FC = ({ - field, - value, - onChange, - onFocus, - validatedStatusState, -}) => { - const { locale } = useContext(I18n) - const showValidatedIcon = validatedStatusState.status === ValidatedStatus.Success && value[field.key] - - const getValidatedIcon = () => { - if (showValidatedIcon) - return
- } - - const handleChange = (v: string) => { - const newFormValue = { ...value, [field.key]: v } - onChange(newFormValue) - } - - return ( -
- handleChange(e.target.value)} - onFocus={onFocus} - value={value[field.key] || ''} - /> - {getValidatedIcon()} -
- ) -} - -export default Input diff --git a/web/app/components/header/account-setting/model-page/model-modal/index.tsx b/web/app/components/header/account-setting/model-page/model-modal/index.tsx deleted file mode 100644 index acc7bf0eb975a0..00000000000000 --- a/web/app/components/header/account-setting/model-page/model-modal/index.tsx +++ /dev/null @@ -1,170 +0,0 @@ -import { useCallback, useState } from 'react' -import type { FC } from 'react' -import { useTranslation } from 'react-i18next' -import { useContext } from 'use-context-selector' -import type { FormValue, ProviderConfigModal } from '../declarations' -import { ConfigurableProviders } from '../utils' -import Form from './Form' -import I18n from '@/context/i18n' -import Button from '@/app/components/base/button' -import { Lock01 } from '@/app/components/base/icons/src/vender/solid/security' -import { LinkExternal02 } from '@/app/components/base/icons/src/vender/line/general' -import { AlertCircle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback' -import { useEventEmitterContextContext } from '@/context/event-emitter' -import { - PortalToFollowElem, - PortalToFollowElemContent, -} from '@/app/components/base/portal-to-follow-elem' - -type ModelModalProps = { - isShow: boolean - onCancel: () => void - modelModal?: ProviderConfigModal - onSave: (v?: FormValue) => void - mode: string -} - -const ModelModal: FC = ({ - isShow, - onCancel, - modelModal, - onSave, - mode, -}) => { - const { t } = useTranslation() - const { locale } = useContext(I18n) - const { eventEmitter } = useEventEmitterContextContext() - const [value, setValue] = useState() - const [loading, setLoading] = useState(false) - const [errorMessage, setErrorMessage] = useState('') - const [cleared, setCleared] = useState(false) - const [prevIsShow, setPrevIsShow] = useState(isShow) - const [validating, setValidating] = useState(false) - - if (prevIsShow !== isShow) { - setCleared(false) - setPrevIsShow(isShow) - } - - eventEmitter?.useSubscription((v) => { - if (v === 'provider-save') - setLoading(true) - else - setLoading(false) - }) - const handleValidatedError = useCallback((newErrorMessage: string) => { - setErrorMessage(newErrorMessage) - }, []) - const handleValidating = useCallback((newValidating: boolean) => { - setValidating(newValidating) - }, []) - const validateRequiredValue = () => { - const validateValue = value || modelModal?.defaultValue - if (modelModal) { - const { fields } = modelModal - const requiredFields = fields.filter(field => !(typeof field.hidden === 'function' ? field.hidden(validateValue) : field.hidden) && field.required) - - for (let i = 0; i < requiredFields.length; i++) { - const currentField = requiredFields[i] - if (!validateValue?.[currentField.key]) { - setErrorMessage(t('appDebug.errorMessage.valueOfVarRequired', { key: currentField.label[locale] }) || '') - return false - } - } - return true - } - } - const handleSave = () => { - if (validateRequiredValue()) - onSave(value || modelModal?.defaultValue) - } - - const renderTitlePrefix = () => { - let prefix - if (mode === 'edit') - prefix = t('common.operation.edit') - else - prefix = ConfigurableProviders.includes(modelModal!.key) ? t('common.operation.create') : t('common.operation.setup') - - return `${prefix} ${modelModal?.title[locale]}` - } - - if (!isShow) - return null - - return ( - - -
-
-
-
-
{renderTitlePrefix()}
- {modelModal?.icon} -
- setValue(newValue)} - onValidatedError={handleValidatedError} - mode={mode} - cleared={cleared} - onClearedChange={setCleared} - onValidating={handleValidating} - /> -
- - {modelModal?.link.label[locale]} - - -
- - -
-
-
-
- { - errorMessage - ? ( -
- - {errorMessage} -
- ) - : ( -
- - {t('common.modelProvider.encrypted.front')} - - PKCS1_OAEP - - {t('common.modelProvider.encrypted.back')} -
- ) - } -
-
-
-
-
- ) -} - -export default ModelModal diff --git a/web/app/components/header/account-setting/model-page/model-selector/index.tsx b/web/app/components/header/account-setting/model-page/model-selector/index.tsx deleted file mode 100644 index 9fb570f80fef00..00000000000000 --- a/web/app/components/header/account-setting/model-page/model-selector/index.tsx +++ /dev/null @@ -1,347 +0,0 @@ -import type { FC } from 'react' -import React, { Fragment, useEffect, useState } from 'react' -import useSWR from 'swr' -import { Popover, Transition } from '@headlessui/react' -import { useTranslation } from 'react-i18next' -import _ from 'lodash-es' -import cn from 'classnames' -import ModelModal from '../model-modal' -import cohereConfig from '../configs/cohere' -import s from './style.module.css' -import type { BackendModel, FormValue, ProviderEnum } from '@/app/components/header/account-setting/model-page/declarations' -import { ModelType } from '@/app/components/header/account-setting/model-page/declarations' -import { ChevronDown } from '@/app/components/base/icons/src/vender/line/arrows' -import { Check, LinkExternal01, SearchLg } from '@/app/components/base/icons/src/vender/line/general' -import { XCircle } from '@/app/components/base/icons/src/vender/solid/general' -import { AlertCircle } from '@/app/components/base/icons/src/vender/line/alertsAndFeedback' -import Tooltip from '@/app/components/base/tooltip' -import ModelIcon from '@/app/components/header/account-setting/model-provider-page/model-icon' -import ModelName from '@/app/components/app/configuration/config-model/model-name' -import ProviderName from '@/app/components/app/configuration/config-model/provider-name' -import { useProviderContext } from '@/context/provider-context' -import ModelModeTypeLabel from '@/app/components/app/configuration/config-model/model-mode-type-label' -import type { ModelModeType } from '@/types/app' -import { CubeOutline } from '@/app/components/base/icons/src/vender/line/shapes' -import { useModalContext } from '@/context/modal-context' -import { useEventEmitterContextContext } from '@/context/event-emitter' -import { fetchDefaultModal, setModelProvider } from '@/service/common' -import { useToastContext } from '@/app/components/base/toast' - -type Props = { - value: { - providerName: ProviderEnum - modelName: string - } | undefined - modelType: ModelType - isShowModelModeType?: boolean - isShowAddModel?: boolean - supportAgentThought?: boolean - onChange: (value: BackendModel) => void - popClassName?: string - readonly?: boolean - triggerIconSmall?: boolean - whenEmptyGoToSetting?: boolean - onUpdate?: () => void -} - -type ModelOption = { - type: 'model' - value: string - providerName: ProviderEnum - modelDisplayName: string - model_mode: ModelModeType -} | { - type: 'provider' - value: ProviderEnum -} - -const ModelSelector: FC = ({ - value, - modelType, - isShowModelModeType, - isShowAddModel, - supportAgentThought, - onChange, - popClassName, - readonly, - triggerIconSmall, - whenEmptyGoToSetting, - onUpdate, -}) => { - const { t } = useTranslation() - const { setShowAccountSettingModal } = useModalContext() - const { - textGenerationModelList, - embeddingsModelList, - speech2textModelList, - rerankModelList, - agentThoughtModelList, - updateModelList, - } = useProviderContext() - const [search, setSearch] = useState('') - const modelList = supportAgentThought - ? agentThoughtModelList - : ({ - [ModelType.textGeneration]: textGenerationModelList, - [ModelType.embeddings]: embeddingsModelList, - [ModelType.speech2text]: speech2textModelList, - [ModelType.reranking]: rerankModelList, - })[modelType] - const currModel = modelList.find(item => item.model_name === value?.modelName && item.model_provider.provider_name === value.providerName) - const allModelNames = (() => { - if (!search) - return {} - - const res: Record = {} - modelList.forEach(({ model_name, model_display_name }) => { - res[model_name] = model_display_name - }) - return res - })() - const filteredModelList = search - ? modelList.filter(({ model_name }) => { - if (allModelNames[model_name].includes(search)) - return true - - return false - }) - : modelList - - const hasRemoved = (value && value.modelName && value.providerName) && !modelList.find(({ model_name, model_provider }) => model_name === value.modelName && model_provider.provider_name === value.providerName) - - const modelOptions: ModelOption[] = (() => { - const providers = _.uniq(filteredModelList.map(item => item.model_provider.provider_name)) - const res: ModelOption[] = [] - providers.forEach((providerName) => { - res.push({ - type: 'provider', - value: providerName, - }) - const models = filteredModelList.filter(m => m.model_provider.provider_name === providerName) - models.forEach(({ model_name, model_display_name, model_mode }) => { - res.push({ - type: 'model', - providerName, - value: model_name, - modelDisplayName: model_display_name, - model_mode, - }) - }) - }) - return res - })() - const { eventEmitter } = useEventEmitterContextContext() - const [showRerankModal, setShowRerankModal] = useState(false) - const [shouldFetchRerankDefaultModel, setShouldFetchRerankDefaultModel] = useState(false) - const { notify } = useToastContext() - const { data: rerankDefaultModel } = useSWR(shouldFetchRerankDefaultModel ? '/workspaces/current/default-model?model_type=reranking' : null, fetchDefaultModal) - const handleOpenRerankModal = (e: React.MouseEvent) => { - e.stopPropagation() - setShowRerankModal(true) - } - const handleRerankModalSave = async (originValue?: FormValue) => { - if (originValue) { - try { - eventEmitter?.emit('provider-save') - const res = await setModelProvider({ - url: `/workspaces/current/model-providers/${cohereConfig.modal.key}`, - body: { - config: originValue, - }, - }) - if (res.result === 'success') { - notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') }) - updateModelList(ModelType.reranking) - setShowRerankModal(false) - setShouldFetchRerankDefaultModel(true) - if (onUpdate) - onUpdate() - } - eventEmitter?.emit('') - } - catch (e) { - eventEmitter?.emit('') - } - } - } - - useEffect(() => { - if (rerankDefaultModel && whenEmptyGoToSetting) - onChange(rerankDefaultModel) - }, [rerankDefaultModel]) - - return ( -
- - - { - ({ open }) => ( - <> - { - (value && value.modelName && value.providerName) - ? ( - <> - -
- - {isShowModelModeType && ( - - )} -
- - ) - : whenEmptyGoToSetting - ? ( -
-
- - {t('common.modelProvider.selector.rerankTip')} -
- -
- ) - : ( -
{t('common.modelProvider.selectModel')}
- ) - } - { - hasRemoved && ( - {t('common.modelProvider.selector.tip')}
- } - > - - - ) - } - { - !readonly && !whenEmptyGoToSetting && ( - - ) - } - { - whenEmptyGoToSetting && (value && value.modelName && value.providerName) && ( - - ) - } - - ) - } - - {!readonly && ( - - -
-
-
-
- setSearch(e.target.value)} - className={` - block w-full h-8 bg-transparent text-[13px] text-gray-700 - outline-none appearance-none border-none - `} - placeholder={t('common.modelProvider.searchModel') || ''} - /> -
- { - search && ( -
setSearch('')}> - -
- ) - } -
-
- { - modelOptions.map((model) => { - if (model.type === 'provider') { - return ( -
- -
- ) - } - - if (model.type === 'model') { - return ( - { - const selectedModel = modelList.find((item) => { - return item.model_name === model.value && item.model_provider.provider_name === model.providerName - }) - onChange(selectedModel as BackendModel) - }} - > - -
- - {isShowModelModeType && ( - - )} -
- { (value?.providerName === model.providerName && value?.modelName === model.value) && } -
- ) - } - - return null - }) - } - {modelList.length !== 0 && (search && filteredModelList.length === 0) && ( -
{t('common.modelProvider.noModelFound', { model: search })}
- )} - - {isShowAddModel && ( -
setShowAccountSettingModal({ payload: 'provider' })} - > - -
{t('common.model.addMoreModel')}
-
- )} -
-
- )} - - setShowRerankModal(false)} - onSave={handleRerankModalSave} - mode={'add'} - /> -
- ) -} - -export default ModelSelector diff --git a/web/app/components/header/account-setting/model-page/model-selector/portal-select.tsx b/web/app/components/header/account-setting/model-page/model-selector/portal-select.tsx deleted file mode 100644 index 3e99c4928d4263..00000000000000 --- a/web/app/components/header/account-setting/model-page/model-selector/portal-select.tsx +++ /dev/null @@ -1,359 +0,0 @@ -import type { FC } from 'react' -import React, { Fragment, useEffect, useRef, useState } from 'react' -import useSWR from 'swr' -import { useTranslation } from 'react-i18next' -import _ from 'lodash-es' -import cn from 'classnames' -import ModelModal from '../model-modal' -import cohereConfig from '../configs/cohere' -import s from './style.module.css' -import type { BackendModel, FormValue, ProviderEnum } from '@/app/components/header/account-setting/model-page/declarations' -import { ModelType } from '@/app/components/header/account-setting/model-page/declarations' -import { ChevronDown } from '@/app/components/base/icons/src/vender/line/arrows' -import { Check, LinkExternal01, SearchLg } from '@/app/components/base/icons/src/vender/line/general' -import { XCircle } from '@/app/components/base/icons/src/vender/solid/general' -import { AlertCircle } from '@/app/components/base/icons/src/vender/line/alertsAndFeedback' -import Tooltip from '@/app/components/base/tooltip' -import ModelIcon from '@/app/components/header/account-setting/model-provider-page/model-icon' -import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' -import ModelName from '@/app/components/app/configuration/config-model/model-name' -import ProviderName from '@/app/components/app/configuration/config-model/provider-name' -import { useProviderContext } from '@/context/provider-context' -import ModelModeTypeLabel from '@/app/components/app/configuration/config-model/model-mode-type-label' -import type { ModelModeType } from '@/types/app' -import { CubeOutline } from '@/app/components/base/icons/src/vender/line/shapes' -import { useModalContext } from '@/context/modal-context' -import { useEventEmitterContextContext } from '@/context/event-emitter' -import { fetchDefaultModal, setModelProvider } from '@/service/common' -import { useToastContext } from '@/app/components/base/toast' -import { - PortalToFollowElem, - PortalToFollowElemContent, - PortalToFollowElemTrigger, -} from '@/app/components/base/portal-to-follow-elem' - -type Props = { - value: { - providerName: ProviderEnum - modelName: string - } | undefined - modelType: ModelType - isShowModelModeType?: boolean - isShowAddModel?: boolean - supportAgentThought?: boolean - onChange: (value: BackendModel) => void - popClassName?: string - readonly?: boolean - triggerIconSmall?: boolean - whenEmptyGoToSetting?: boolean - onUpdate?: () => void - widthSameToTrigger?: boolean -} - -type ModelOption = { - type: 'model' - value: string - providerName: ProviderEnum - modelDisplayName: string - model_mode: ModelModeType -} | { - type: 'provider' - value: ProviderEnum -} - -const ModelSelector: FC = ({ - value, - modelType, - isShowModelModeType, - isShowAddModel, - supportAgentThought, - onChange, - popClassName, - readonly, - triggerIconSmall, - whenEmptyGoToSetting, - onUpdate, - widthSameToTrigger, -}) => { - const { t } = useTranslation() - const { setShowAccountSettingModal } = useModalContext() - const { - textGenerationModelList, - embeddingsModelList, - speech2textModelList, - rerankModelList, - agentThoughtModelList, - updateModelList, - } = useProviderContext() - const [search, setSearch] = useState('') - const modelList = supportAgentThought - ? agentThoughtModelList - : ({ - [ModelType.textGeneration]: textGenerationModelList, - [ModelType.embeddings]: embeddingsModelList, - [ModelType.speech2text]: speech2textModelList, - [ModelType.reranking]: rerankModelList, - })[modelType] - const currModel = modelList.find(item => item.model_name === value?.modelName && item.model_provider.provider_name === value.providerName) - const allModelNames = (() => { - if (!search) - return {} - - const res: Record = {} - modelList.forEach(({ model_name, model_display_name }) => { - res[model_name] = model_display_name - }) - return res - })() - const filteredModelList = search - ? modelList.filter(({ model_name }) => { - if (allModelNames[model_name].includes(search)) - return true - - return false - }) - : modelList - - const hasRemoved = (value && value.modelName && value.providerName) && !modelList.find(({ model_name, model_provider }) => model_name === value.modelName && model_provider.provider_name === value.providerName) - - const modelOptions: ModelOption[] = (() => { - const providers = _.uniq(filteredModelList.map(item => item.model_provider.provider_name)) - const res: ModelOption[] = [] - providers.forEach((providerName) => { - res.push({ - type: 'provider', - value: providerName, - }) - const models = filteredModelList.filter(m => m.model_provider.provider_name === providerName) - models.forEach(({ model_name, model_display_name, model_mode }) => { - res.push({ - type: 'model', - providerName, - value: model_name, - modelDisplayName: model_display_name, - model_mode, - }) - }) - }) - return res - })() - const { eventEmitter } = useEventEmitterContextContext() - const [showRerankModal, setShowRerankModal] = useState(false) - const [shouldFetchRerankDefaultModel, setShouldFetchRerankDefaultModel] = useState(false) - const { notify } = useToastContext() - const { data: rerankDefaultModel } = useSWR(shouldFetchRerankDefaultModel ? '/workspaces/current/default-model?model_type=reranking' : null, fetchDefaultModal) - const handleOpenRerankModal = (e: React.MouseEvent) => { - e.stopPropagation() - setShowRerankModal(true) - } - const handleRerankModalSave = async (originValue?: FormValue) => { - if (originValue) { - try { - eventEmitter?.emit('provider-save') - const res = await setModelProvider({ - url: `/workspaces/current/model-providers/${cohereConfig.modal.key}`, - body: { - config: originValue, - }, - }) - if (res.result === 'success') { - notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') }) - updateModelList(ModelType.reranking) - setShowRerankModal(false) - setShouldFetchRerankDefaultModel(true) - if (onUpdate) - onUpdate() - } - eventEmitter?.emit('') - } - catch (e) { - eventEmitter?.emit('') - } - } - } - - const [open, setOpen] = useState(false) - const triggerRef = useRef(null) - - useEffect(() => { - if (rerankDefaultModel && whenEmptyGoToSetting) - onChange(rerankDefaultModel) - }, [rerankDefaultModel]) - - return ( - -
- setOpen(v => !v)} className={cn('flex items-center px-2.5 w-full h-9 rounded-lg', readonly ? '!cursor-auto bg-gray-100 opacity-50' : 'bg-gray-100', hasRemoved && '!bg-[#FEF3F2]')}> - { -
- { - (value && value.modelName && value.providerName) - ? ( - <> - -
- - {isShowModelModeType && ( - - )} -
- - ) - : whenEmptyGoToSetting - ? ( -
-
- - {t('common.modelProvider.selector.rerankTip')} -
- -
- ) - : ( -
{t('common.modelProvider.selectModel')}
- ) - } - { - hasRemoved && ( - {t('common.modelProvider.selector.tip')}
- } - > - - - ) - } - { - !readonly && !whenEmptyGoToSetting && ( - - ) - } - { - whenEmptyGoToSetting && (value && value.modelName && value.providerName) && ( - - ) - } -
- } - - {!readonly && ( - -
-
-
-
- setSearch(e.target.value)} - className={` - block w-full h-8 bg-transparent text-[13px] text-gray-700 - outline-none appearance-none border-none - `} - placeholder={t('common.modelProvider.searchModel') || ''} - /> -
- { - search && ( -
setSearch('')}> - -
- ) - } -
-
- { - modelOptions.map((model) => { - if (model.type === 'provider') { - return ( -
- -
- ) - } - - if (model.type === 'model') { - return ( -
{ - const selectedModel = modelList.find((item) => { - return item.model_name === model.value && item.model_provider.provider_name === model.providerName - }) - onChange(selectedModel as BackendModel) - setOpen(false) - }} - > - -
- - {isShowModelModeType && ( - - )} -
- {(value?.providerName === model.providerName && value?.modelName === model.value) && } -
- ) - } - - return null - }) - } - {modelList.length !== 0 && (search && filteredModelList.length === 0) && ( -
{t('common.modelProvider.noModelFound', { model: search })}
- )} - - {isShowAddModel && ( -
setShowAccountSettingModal({ payload: 'provider' })} - > - -
{t('common.model.addMoreModel')}
-
- )} -
- )} -
- setShowRerankModal(false)} - onSave={handleRerankModalSave} - mode={'add'} - /> - - ) -} - -export default ModelSelector diff --git a/web/app/components/header/account-setting/model-page/model-selector/style.module.css b/web/app/components/header/account-setting/model-page/model-selector/style.module.css deleted file mode 100644 index 91591220f9101e..00000000000000 --- a/web/app/components/header/account-setting/model-page/model-selector/style.module.css +++ /dev/null @@ -1,7 +0,0 @@ -.modelModeLabel { - visibility: hidden; -} - -.optionItem:hover .modelModeLabel { - visibility: visible; -} \ No newline at end of file diff --git a/web/app/components/header/account-setting/model-page/selector/index.tsx b/web/app/components/header/account-setting/model-page/selector/index.tsx deleted file mode 100644 index 76301eed26b67f..00000000000000 --- a/web/app/components/header/account-setting/model-page/selector/index.tsx +++ /dev/null @@ -1,97 +0,0 @@ -import { Fragment } from 'react' -import type { FC } from 'react' -import { Popover, Transition } from '@headlessui/react' -import { useTranslation } from 'react-i18next' -import { Check, DotsHorizontal, Trash03 } from '@/app/components/base/icons/src/vender/line/general' - -const itemClassName = ` -flex items-center px-3 h-9 text-sm text-gray-700 rounded-lg cursor-pointer -` - -type SelectorProps = { - value?: string - onOperate: (v: Record) => void - hiddenOptions?: boolean - className?: (v: boolean) => string - deleteText?: string -} -const Selector: FC = ({ - value, - onOperate, - hiddenOptions, - className, - deleteText, -}) => { - const { t } = useTranslation() - const options = [ - { - key: 'custom', - text: 'API', - }, - { - key: 'system', - text: t('common.modelProvider.quota'), - }, - ] - - return ( - - - { - ({ open }) => ( -
- -
- ) - } -
- - - { - !hiddenOptions && ( - <> -
-
{t('common.modelProvider.card.priorityUse')}
- { - options.map(option => ( - -
onOperate({ type: 'priority', value: option.key })}> -
{option.text}
- {value === option.key && } -
-
- )) - } -
-
- - ) - } -
- -
onOperate({ type: 'delete' })}> - - {deleteText || t('common.modelProvider.card.removeKey')} -
-
-
- - - - ) -} - -export default Selector diff --git a/web/app/components/header/account-setting/model-page/system-model/index.tsx b/web/app/components/header/account-setting/model-page/system-model/index.tsx deleted file mode 100644 index b55dc7d8e5ef63..00000000000000 --- a/web/app/components/header/account-setting/model-page/system-model/index.tsx +++ /dev/null @@ -1,213 +0,0 @@ -import type { FC } from 'react' -import { useState } from 'react' -import { useTranslation } from 'react-i18next' -import ModelSelector from '../model-selector' -import type { - BackendModel, ProviderEnum, -} from '../declarations' -import Tooltip from '@/app/components/base/tooltip' -import { HelpCircle, Settings01 } from '@/app/components/base/icons/src/vender/line/general' -import { - PortalToFollowElem, - PortalToFollowElemContent, - PortalToFollowElemTrigger, -} from '@/app/components/base/portal-to-follow-elem' -import { useProviderContext } from '@/context/provider-context' -import { updateDefaultModel } from '@/service/common' -import { ModelType } from '@/app/components/header/account-setting/model-page/declarations' -import { useToastContext } from '@/app/components/base/toast' -import Button from '@/app/components/base/button' - -type SystemModelProps = { - onUpdate: () => void -} -const SystemModel: FC = ({ - onUpdate, -}) => { - const { t } = useTranslation() - const { - textGenerationDefaultModel, - mutateTextGenerationDefaultModel, - embeddingsDefaultModel, - mutateEmbeddingsDefaultModel, - speech2textDefaultModel, - mutateSpeech2textDefaultModel, - rerankDefaultModel, - mutateRerankDefaultModel, - } = useProviderContext() - const { notify } = useToastContext() - const [open, setOpen] = useState(false) - const [selectedModel, setSelectedModel] = useState>({ - [ModelType.textGeneration]: textGenerationDefaultModel && { providerName: textGenerationDefaultModel.model_provider.provider_name, modelName: textGenerationDefaultModel.model_name }, - [ModelType.embeddings]: embeddingsDefaultModel && { providerName: embeddingsDefaultModel.model_provider.provider_name, modelName: embeddingsDefaultModel.model_name }, - [ModelType.speech2text]: speech2textDefaultModel && { providerName: speech2textDefaultModel.model_provider.provider_name, modelName: speech2textDefaultModel.model_name }, - [ModelType.reranking]: rerankDefaultModel && { providerName: rerankDefaultModel.model_provider.provider_name, modelName: rerankDefaultModel.model_name }, - }) - - const mutateDefaultModel = (types: ModelType[]) => { - types.forEach((type) => { - if (type === ModelType.textGeneration) - mutateTextGenerationDefaultModel() - if (type === ModelType.embeddings) - mutateEmbeddingsDefaultModel() - if (type === ModelType.speech2text) - mutateSpeech2textDefaultModel() - if (type === ModelType.reranking) - mutateRerankDefaultModel() - }) - } - const handleChangeDefaultModel = async (type: ModelType, v: BackendModel) => { - setSelectedModel({ - ...selectedModel, - [type]: { - providerName: v.model_provider.provider_name, - modelName: v.model_name, - }, - }) - } - const handleSave = async () => { - const kesArray = Object.keys(selectedModel) as ModelType[] - const res = await updateDefaultModel({ - url: '/workspaces/current/default-model', - body: { - model_settings: kesArray.map((key) => { - return { - model_type: key, - provider_name: selectedModel?.[key]?.providerName, - model_name: selectedModel?.[key]?.modelName, - } - }), - }, - }) - if (res.result === 'success') { - notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') }) - mutateDefaultModel(kesArray) - } - } - - return ( - - setOpen(v => !v)}> -
- - {t('common.modelProvider.systemModelSettings')} -
-
- -
-
-
- {t('common.modelProvider.systemReasoningModel.key')} - {t('common.modelProvider.systemReasoningModel.tip')}
- } - > - - -
-
- handleChangeDefaultModel(ModelType.textGeneration, v)} - /> -
-
-
-
- {t('common.modelProvider.embeddingModel.key')} - {t('common.modelProvider.embeddingModel.tip')}
- } - > - - -
-
- handleChangeDefaultModel(ModelType.embeddings, v)} - /> -
-
-
-
- {t('common.modelProvider.rerankModel.key')} - {t('common.modelProvider.rerankModel.tip')}
- } - > - - -
-
- handleChangeDefaultModel(ModelType.reranking, v)} - whenEmptyGoToSetting - onUpdate={onUpdate} - /> -
-
-
-
- {t('common.modelProvider.speechToTextModel.key')} - {t('common.modelProvider.speechToTextModel.tip')}
- } - > - - -
-
- handleChangeDefaultModel(ModelType.speech2text, v)} - /> -
-
-
- - -
-
- - - ) -} - -export default SystemModel diff --git a/web/app/components/header/account-setting/model-page/utils.ts b/web/app/components/header/account-setting/model-page/utils.ts deleted file mode 100644 index 32b3619db22714..00000000000000 --- a/web/app/components/header/account-setting/model-page/utils.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { ValidatedStatus } from '../key-validator/declarations' -import { ProviderEnum } from './declarations' -import { validateModelProvider } from '@/service/common' - -export const ConfigurableProviders = [ProviderEnum.azure_openai, ProviderEnum.replicate, ProviderEnum.huggingface_hub, ProviderEnum.xinference, ProviderEnum.openllm, ProviderEnum.localai] - -export const validateModelProviderFn = async (providerName: ProviderEnum, v: any) => { - let body, url - - if (ConfigurableProviders.includes(providerName)) { - const { model_name, model_type, ...config } = v - body = { - model_name, - model_type, - config, - } - url = `/workspaces/current/model-providers/${providerName}/models/validate` - } - else { - body = { - config: v, - } - url = `/workspaces/current/model-providers/${providerName}/validate` - } - try { - const res = await validateModelProvider({ url, body }) - if (res.result === 'success') - return Promise.resolve({ status: ValidatedStatus.Success }) - else - return Promise.resolve({ status: ValidatedStatus.Error, message: res.error }) - } - catch (e: any) { - return Promise.resolve({ status: ValidatedStatus.Error, message: e.message }) - } -} diff --git a/web/app/components/header/account-setting/model-provider-page/declarations.ts b/web/app/components/header/account-setting/model-provider-page/declarations.ts index d76ac9a8836806..733d0b4a6d9e3d 100644 --- a/web/app/components/header/account-setting/model-provider-page/declarations.ts +++ b/web/app/components/header/account-setting/model-provider-page/declarations.ts @@ -35,10 +35,17 @@ export enum ConfigurateMethodEnum { export enum ModelFeatureEnum { toolCall = 'tool-call', multiToolCall = 'multi-tool-call', - agentThought = 'agent_thought', + agentThought = 'agent-thought', vision = 'vision', } +export enum ModelFeatureTextEnum { + toolCall = 'Tool Call', + multiToolCall = 'Multi Tool Call', + agentThought = 'Agent Thought', + vision = 'Vision', +} + export enum ModelStatusEnum { active = 'active', noConfigure = 'no-configure', @@ -75,7 +82,7 @@ export type ModelItem = { model: string label: TypeWithI18N model_type: ModelTypeEnum - features: ModelFeatureEnum[] + features?: ModelFeatureEnum[] fetch_from: ConfigurateMethodEnum status: ModelStatusEnum model_properties: Record @@ -171,15 +178,16 @@ export type CustomConfigrationModelFixedFields = { } export type ModelParameterRule = { - default: number | string | boolean - help: TypeWithI18N + default?: number | string | boolean | string[] + help?: TypeWithI18N label: TypeWithI18N - min: number - max: number + min?: number + max?: number name: string - precision: number + precision?: number required: false type: string - use_template: string + use_template?: string options?: string[] + tagPlaceholder?: TypeWithI18N } diff --git a/web/app/components/header/account-setting/model-provider-page/hooks.ts b/web/app/components/header/account-setting/model-provider-page/hooks.ts index ad1c8cb761a759..b1135c9c64d39e 100644 --- a/web/app/components/header/account-setting/model-provider-page/hooks.ts +++ b/web/app/components/header/account-setting/model-provider-page/hooks.ts @@ -23,6 +23,7 @@ import { fetchModelList, fetchModelProviderCredentials, } from '@/service/common' +import { useProviderContext } from '@/context/provider-context' type UseDefaultModelAndModelList = ( defaultModel: DefaultModelResponse | undefined, @@ -126,6 +127,34 @@ export const useCurrentProviderAndModel = (modelList: Model[], defaultModel?: De } } +export const useTextGenerationCurrentProviderAndModelAndModelList = (defaultModel?: DefaultModel) => { + const { textGenerationModelList } = useProviderContext() + const { + currentProvider, + currentModel, + } = useCurrentProviderAndModel(textGenerationModelList, defaultModel) + + return { + currentProvider, + currentModel, + textGenerationModelList, + } +} + +export const useAgentThoughtCurrentProviderAndModelAndModelList = (defaultModel?: DefaultModel) => { + const { agentThoughtModelList } = useProviderContext() + const { + currentProvider, + currentModel, + } = useCurrentProviderAndModel(agentThoughtModelList, defaultModel) + + return { + currentProvider, + currentModel, + agentThoughtModelList, + } +} + export const useModelListAndDefaultModel = (type: ModelTypeIndex) => { const { data: modelList } = useModelList(type) const { data: defaultModel } = useDefaultModel(type) diff --git a/web/app/components/header/account-setting/model-provider-page/model-name/index.tsx b/web/app/components/header/account-setting/model-provider-page/model-name/index.tsx index e20449702ae8d7..3f0730451d3578 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-name/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-name/index.tsx @@ -12,16 +12,22 @@ type ModelNameProps = { modelItem: ModelItem className?: string showModelType?: boolean + modelTypeClassName?: string showMode?: boolean + modeClassName?: string showFeatures?: boolean + featuresClassName?: string showContextSize?: boolean } const ModelName: FC = ({ modelItem, className, showModelType, + modelTypeClassName, showMode, + modeClassName, showFeatures, + featuresClassName, showContextSize, }) => { const language = useLanguage() @@ -34,21 +40,21 @@ const ModelName: FC = ({ `} >
{modelItem.label[language]}
{ showModelType && ( - + {modelTypeFormat(modelItem.model_type)} ) } { modelItem.model_properties.mode && showMode && ( - + {(modelItem.model_properties.mode as string).toLocaleUpperCase()} ) @@ -58,6 +64,7 @@ const ModelName: FC = ({ )) } diff --git a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/index.tsx b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/index.tsx index b03597c0782509..32358dbee827af 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/index.tsx @@ -4,11 +4,12 @@ import useSWR from 'swr' import { useTranslation } from 'react-i18next' import type { FormValue, + ModelParameterRule, } from '../declarations' import ModelIcon from '../model-icon' import ModelName from '../model-name' import ModelSelector from '../model-selector' -import { useCurrentProviderAndModel } from '../hooks' +import { useTextGenerationCurrentProviderAndModelAndModelList } from '../hooks' import ParameterItem from './parameter-item' import type { ParameterValue } from './parameter-item' import { @@ -18,7 +19,6 @@ import { } from '@/app/components/base/portal-to-follow-elem' import { SlidersH } from '@/app/components/base/icons/src/vender/line/mediaAndDevices' import { AlertTriangle } from '@/app/components/base/icons/src/vender/line/alertsAndFeedback' -import { useProviderContext } from '@/context/provider-context' import { CubeOutline } from '@/app/components/base/icons/src/vender/line/shapes' import { fetchModelParameterRules } from '@/service/common' import Loading from '@/app/components/base/loading' @@ -33,9 +33,26 @@ type ModelParameterModalProps = { onCompletionParamsChange: (newParams: FormValue) => void disabled: boolean } +const stopParameerRule: ModelParameterRule = { + default: [], + help: { + en_US: 'Up to four sequences where the API will stop generating further tokens. The returned text will not contain the stop sequence.', + zh_Hans: '最多四个序列,API 将停止生成更多的 token。返回的文本将不包含停止序列。', + }, + label: { + en_US: 'Stop sequences', + zh_Hans: '停止序列 stop_sequences', + }, + name: 'stop', + required: false, + type: 'tag', + tagPlaceholder: { + en_US: 'Enter sequence and press Tab', + zh_Hans: '输入序列并按 Tab 键', + }, +} const ModelParameterModal: FC = ({ isAdvancedMode, - mode, modelId, provider, setModel, @@ -46,15 +63,16 @@ const ModelParameterModal: FC = ({ const { t } = useTranslation() const [open, setOpen] = useState(false) const { data: parameterRulesData, isLoading } = useSWR(`/workspaces/current/model-providers/${provider}/models/parameter-rules?model=${modelId}`, fetchModelParameterRules) - const { textGenerationModelList } = useProviderContext() const { currentProvider, currentModel, - } = useCurrentProviderAndModel( textGenerationModelList, + } = useTextGenerationCurrentProviderAndModelAndModelList( { provider, model: modelId }, ) + const parameterRules = parameterRulesData?.data || [] + const handleParamChange = (key: string, value: ParameterValue) => { onCompletionParamsChange({ ...completionParams, @@ -109,7 +127,9 @@ const ModelParameterModal: FC = ({ className='mr-1.5 text-gray-900' modelItem={currentModel} showMode={isAdvancedMode} + modeClassName='!text-[#444CE7] !border-[#A4BCFD]' showFeatures={isAdvancedMode} + featuresClassName='!text-[#444CE7] !border-[#A4BCFD]' /> ) } @@ -157,8 +177,11 @@ const ModelParameterModal: FC = ({ ) } { - parameterRulesData?.data && ( - parameterRulesData.data.map(parameter => ( + !isLoading && ( + [ + ...parameterRules, + ...(isAdvancedMode ? [stopParameerRule] : []), + ].map(parameter => ( = ({ const language = useLanguage() const [localValue, setLocalValue] = useState(value) const mergedValue = value === undefined ? localValue : value + const renderValue = mergedValue === undefined ? parameterRule.default : mergedValue - const handleChange = (v: number | string | boolean) => { + const handleChange = (v: ParameterValue) => { setLocalValue(v) if (value !== undefined && onChange) onChange(v) @@ -37,10 +39,10 @@ const ParameterItem: FC = ({ const handleNumberInputChange = (e: React.ChangeEvent) => { let num = +e.target.value - if (num > parameterRule.max) + if (parameterRule.max !== undefined && num > parameterRule.max) num = parameterRule.max - if (num < parameterRule.min) + if (parameterRule.min !== undefined && num < parameterRule.min) num = parameterRule.min handleChange(num) @@ -62,22 +64,29 @@ const ParameterItem: FC = ({ handleChange(option.value) } + const handleTagChange = (newSequences: string[]) => { + handleChange(newSequences) + } + const handleSwitch = (checked: boolean) => { if (onSwitch) { let assignValue: ParameterValue = localValue if (localValue === undefined) { if (parameterRule.type === 'int' || parameterRule.type === 'float') - assignValue = 0 + assignValue = parameterRule.default !== undefined ? parameterRule.default : 0 if (parameterRule.type === 'string' && !parameterRule.options?.length) - assignValue = '' + assignValue = parameterRule.default || '' if (parameterRule.type === 'string' && parameterRule.options?.length) assignValue = parameterRule.options[0] if (parameterRule.type === 'boolean') - assignValue = false + assignValue = parameterRule.default !== undefined ? parameterRule.default : false + + if (parameterRule.type === 'tag') + assignValue = parameterRule.default !== undefined ? parameterRule.default : [] } onSwitch(checked, assignValue) @@ -85,29 +94,42 @@ const ParameterItem: FC = ({ } return ( -
-
-
- {parameterRule.label[language]} +
+
+
+
+ {parameterRule.label[language]} +
+ { + parameterRule.help && ( + {parameterRule.help[language]}
+ )} + > + + + ) + } + { + !parameterRule.required && ( + + ) + }
- {parameterRule.help[language]}
- )} - > - - { - !parameterRule.required && ( - + parameterRule.type === 'tag' && ( +
+ {parameterRule?.tagPlaceholder?.[language]} +
) }
@@ -116,10 +138,10 @@ const ParameterItem: FC = ({
= ({ type='number' max={parameterRule.max} min={parameterRule.min} - step={+`0.${parameterRule.precision}`} - value={(mergedValue || parameterRule.default || '') as string} + step={+`0.${parameterRule.precision || 0}`} + value={renderValue === undefined ? 0 : +renderValue} onChange={handleNumberInputChange} />
@@ -138,7 +160,7 @@ const ParameterItem: FC = ({ parameterRule.type === 'boolean' && ( True @@ -150,7 +172,7 @@ const ParameterItem: FC = ({ parameterRule.type === 'string' && !parameterRule.options?.length && ( ) @@ -160,12 +182,23 @@ const ParameterItem: FC = ({ ({ value: option, name: option }))} /> ) } + { + parameterRule.type === 'tag' && ( +
+ +
+ ) + }
) } diff --git a/web/app/components/header/account-setting/model-provider-page/model-selector/feature-icon.tsx b/web/app/components/header/account-setting/model-provider-page/model-selector/feature-icon.tsx index 734be2077087f6..c64b8ca736c837 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-selector/feature-icon.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-selector/feature-icon.tsx @@ -1,48 +1,73 @@ import type { FC } from 'react' +import { useTranslation } from 'react-i18next' import ModelBadge from '../model-badge' -import { ModelFeatureEnum } from '../declarations' +import { + ModelFeatureEnum, + ModelFeatureTextEnum, +} from '../declarations' import { MagicBox, MagicEyes, MagicWand, Robot, } from '@/app/components/base/icons/src/vender/solid/mediaAndDevices' +import TooltipPlus from '@/app/components/base/tooltip-plus' type FeatureIconProps = { feature: ModelFeatureEnum + className?: string } const FeatureIcon: FC = ({ + className, feature, }) => { + const { t } = useTranslation() + if (feature === ModelFeatureEnum.agentThought) { return ( - - - + + + + + ) } if (feature === ModelFeatureEnum.toolCall) { return ( - - - + + + + + ) } if (feature === ModelFeatureEnum.multiToolCall) { return ( - - - + + + + + ) } if (feature === ModelFeatureEnum.vision) { return ( - - - + + + + + ) } diff --git a/web/app/components/header/account-setting/model-provider-page/system-model-selector/index.tsx b/web/app/components/header/account-setting/model-provider-page/system-model-selector/index.tsx index 86b5b6888ae3e5..f3cae471cb8b03 100644 --- a/web/app/components/header/account-setting/model-provider-page/system-model-selector/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/system-model-selector/index.tsx @@ -84,8 +84,8 @@ const SystemModel: FC = ({ model_settings: [ModelTypeEnum.textGeneration, ModelTypeEnum.textEmbedding, ModelTypeEnum.rerank, ModelTypeEnum.speech2text].map((modelType) => { return { model_type: modelType, - provider_name: getCurrentDefaultModelByModelType(modelType)?.provider, - model_name: getCurrentDefaultModelByModelType(modelType)?.model, + provider: getCurrentDefaultModelByModelType(modelType)?.provider, + model: getCurrentDefaultModelByModelType(modelType)?.model, } }), }, diff --git a/web/context/provider-context.tsx b/web/context/provider-context.tsx index 3ad92bfd77329e..522126f54832e4 100644 --- a/web/context/provider-context.tsx +++ b/web/context/provider-context.tsx @@ -2,9 +2,10 @@ import { createContext, useContext } from 'use-context-selector' import useSWR from 'swr' -import { useEffect, useState } from 'react' +import { useEffect, useMemo, useState } from 'react' import { fetchModelList, fetchSupportRetrievalMethods } from '@/service/common' import { + ModelFeatureEnum, ModelStatusEnum, ModelTypeEnum, } from '@/app/components/header/account-setting/model-provider-page/declarations' @@ -65,9 +66,23 @@ export const ProviderContextProvider = ({ const { data: textGenerationModelList } = useSWR(`${fetchModelListUrlPrefix}${ModelTypeEnum.textGeneration}`, fetchModelList) const { data: supportRetrievalMethods } = useSWR('/datasets/retrieval-setting', fetchSupportRetrievalMethods) - // const agentThoughtModelList = textGenerationModelList?.data?.filter((item) => { - // return item.features?.includes(ModelFeature.agentThought) - // }) + const agentThoughtModelList = useMemo(() => { + const result = [] + if (textGenerationModelList?.data) { + textGenerationModelList?.data.forEach((item) => { + const agentThoughtModels = item.models.filter(model => model.features?.includes(ModelFeatureEnum.agentThought)) + + if (agentThoughtModelList.length) { + result.push({ + ...item, + models: agentThoughtModels, + }) + } + }) + } + + return [] + }, [textGenerationModelList]) const [plan, setPlan] = useState(defaultPlan) const [isFetchedPlan, setIsFetchedPlan] = useState(false) @@ -96,7 +111,7 @@ export const ProviderContextProvider = ({ return ( model.status === ModelStatusEnum.active), supportRetrievalMethods: supportRetrievalMethods?.retrieval_method || [], plan, diff --git a/web/hooks/use-pay.tsx b/web/hooks/use-pay.tsx index eb7132a0cba0d5..6ec47959404e28 100644 --- a/web/hooks/use-pay.tsx +++ b/web/hooks/use-pay.tsx @@ -6,7 +6,6 @@ import { useTranslation } from 'react-i18next' import useSWR from 'swr' import { useContext } from 'use-context-selector' import I18n from '@/context/i18n' -import { ProviderEnum } from '@/app/components/header/account-setting/model-page/declarations' import { fetchDataSourceNotionBinding, fetchFreeQuotaVerify, @@ -24,7 +23,7 @@ export const useAnthropicCheckPay = () => { const paymentResult = searchParams.get('payment_result') useEffect(() => { - if (providerName === ProviderEnum.anthropic && (paymentResult === 'succeeded' || paymentResult === 'cancelled')) { + if (providerName === 'anthropic' && (paymentResult === 'succeeded' || paymentResult === 'cancelled')) { setConfirm({ type: paymentResult === 'succeeded' ? 'success' : 'danger', title: paymentResult === 'succeeded' ? t('common.actionMsg.paySucceeded') : t('common.actionMsg.payCancelled'), @@ -54,8 +53,8 @@ export const useBillingPay = () => { return confirm } -const QUOTA_RECEIVE_STATUS = { - [ProviderEnum.spark]: { +const QUOTA_RECEIVE_STATUS: Record = { + spark: { success: { 'en': 'Successful collection, the quota will be automatically increased after 5 minutes.', 'zh-Hans': '领取成功,将在 5 分钟后自动增加配额', @@ -65,7 +64,7 @@ const QUOTA_RECEIVE_STATUS = { 'zh-Hans': '领取失败', }, }, - [ProviderEnum.zhipuai]: { + zhipuai: { success: { 'en': 'Successful collection', 'zh-Hans': '领取成功', @@ -77,14 +76,14 @@ const QUOTA_RECEIVE_STATUS = { }, } -const FREE_CHECK_PROVIDER = [ProviderEnum.spark, ProviderEnum.zhipuai] +const FREE_CHECK_PROVIDER = ['spark', 'zhipuai'] export const useCheckFreeQuota = () => { const { locale } = useContext(I18n) const router = useRouter() const [shouldVerify, setShouldVerify] = useState(false) const searchParams = useSearchParams() const type = searchParams.get('type') - const provider = searchParams.get('provider') as (ProviderEnum.spark | ProviderEnum.zhipuai) + const provider = searchParams.get('provider') const result = searchParams.get('result') const token = searchParams.get('token') @@ -101,14 +100,14 @@ export const useCheckFreeQuota = () => { }, [error, router]) useEffect(() => { - if (type === 'provider_apply_callback' && FREE_CHECK_PROVIDER.includes(provider) && result === 'success') + if (type === 'provider_apply_callback' && FREE_CHECK_PROVIDER.includes(provider as string) && result === 'success') setShouldVerify(true) }, [type, provider, result]) return (data && provider) ? { type: data.flag ? 'success' : 'danger', - title: data.flag ? QUOTA_RECEIVE_STATUS[provider].success[locale] : QUOTA_RECEIVE_STATUS[provider].fail[locale], + title: data.flag ? QUOTA_RECEIVE_STATUS[provider as string].success[locale] : QUOTA_RECEIVE_STATUS[provider].fail[locale], desc: !data.flag ? data.reason : undefined, } : null diff --git a/web/i18n/lang/common.en.ts b/web/i18n/lang/common.en.ts index 55ffb39a5a3892..e10a5a5d52f93e 100644 --- a/web/i18n/lang/common.en.ts +++ b/web/i18n/lang/common.en.ts @@ -294,6 +294,7 @@ const translation = { config: 'Config', modelAndParameters: 'Model and Parameters', model: 'Model', + featureSupported: '{{feature}} supported', }, dataSource: { add: 'Add a data source', diff --git a/web/i18n/lang/common.zh.ts b/web/i18n/lang/common.zh.ts index c92c00a86ce6c0..8ef5866b1893b9 100644 --- a/web/i18n/lang/common.zh.ts +++ b/web/i18n/lang/common.zh.ts @@ -294,6 +294,7 @@ const translation = { config: '配置', modelAndParameters: '模型及参数', model: '模型', + featureSupported: '支持 {{feature}} 功能', }, dataSource: { add: '添加数据源', From 41696e3f271aa2f1f93479b8f30e063fa73c557e Mon Sep 17 00:00:00 2001 From: StyleZhang Date: Wed, 27 Dec 2023 16:37:22 +0800 Subject: [PATCH 21/46] fix: provider context --- web/context/provider-context.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/context/provider-context.tsx b/web/context/provider-context.tsx index 522126f54832e4..0d4fc1fad7037e 100644 --- a/web/context/provider-context.tsx +++ b/web/context/provider-context.tsx @@ -72,7 +72,7 @@ export const ProviderContextProvider = ({ textGenerationModelList?.data.forEach((item) => { const agentThoughtModels = item.models.filter(model => model.features?.includes(ModelFeatureEnum.agentThought)) - if (agentThoughtModelList.length) { + if (agentThoughtModels.length) { result.push({ ...item, models: agentThoughtModels, From 9b1363d92c330ccdaf87cf84e93f442ddb90f6c0 Mon Sep 17 00:00:00 2001 From: StyleZhang Date: Wed, 27 Dec 2023 17:09:46 +0800 Subject: [PATCH 22/46] fix: provider context --- .../account-setting/model-provider-page/model-name/index.tsx | 3 +++ web/context/provider-context.tsx | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/web/app/components/header/account-setting/model-provider-page/model-name/index.tsx b/web/app/components/header/account-setting/model-provider-page/model-name/index.tsx index 3f0730451d3578..b28ad249653ed6 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-name/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-name/index.tsx @@ -32,6 +32,9 @@ const ModelName: FC = ({ }) => { const language = useLanguage() + if (!modelItem) + return null + return (
{ - const result = [] + const result: Model[] = [] if (textGenerationModelList?.data) { textGenerationModelList?.data.forEach((item) => { const agentThoughtModels = item.models.filter(model => model.features?.includes(ModelFeatureEnum.agentThought)) @@ -79,6 +79,8 @@ export const ProviderContextProvider = ({ }) } }) + + return result } return [] From 6dd914f017d5d503ca9cc6ed0972050f22205b3b Mon Sep 17 00:00:00 2001 From: StyleZhang Date: Wed, 27 Dec 2023 18:55:41 +0800 Subject: [PATCH 23/46] fix quota --- web/app/components/app/log/list.tsx | 1 + .../config-view/summary/index.tsx | 1 + .../model-provider-page/hooks.ts | 47 ++++++++++++ .../model-provider-page/model-icon/index.tsx | 6 ++ .../model-provider-page/model-name/index.tsx | 2 +- .../model-parameter-modal/index.tsx | 1 + .../model-selector/model-trigger.tsx | 1 + .../model-selector/popup-item.tsx | 1 + .../provider-added-card/model-list.tsx | 1 + .../provider-added-card/quota-panel.tsx | 37 +++++++--- .../provider-card/index.module.css | 4 ++ .../provider-card/index.tsx | 72 +++++++++++++++++-- .../provider-icon/index.tsx | 4 -- web/i18n/lang/common.en.ts | 3 + web/i18n/lang/common.zh.ts | 3 + 15 files changed, 162 insertions(+), 22 deletions(-) create mode 100644 web/app/components/header/account-setting/model-provider-page/provider-card/index.module.css diff --git a/web/app/components/app/log/list.tsx b/web/app/components/app/log/list.tsx index b692e944bf81ad..a17c431c233399 100644 --- a/web/app/components/app/log/list.tsx +++ b/web/app/components/app/log/list.tsx @@ -231,6 +231,7 @@ function DetailPanel = ({
diff --git a/web/app/components/header/account-setting/model-provider-page/hooks.ts b/web/app/components/header/account-setting/model-provider-page/hooks.ts index b1135c9c64d39e..e5859611634779 100644 --- a/web/app/components/header/account-setting/model-provider-page/hooks.ts +++ b/web/app/components/header/account-setting/model-provider-page/hooks.ts @@ -22,6 +22,8 @@ import { fetchDefaultModal, fetchModelList, fetchModelProviderCredentials, + getPayUrl, + submitFreeQuota, } from '@/service/common' import { useProviderContext } from '@/context/provider-context' @@ -190,3 +192,48 @@ export const useUpdateModelList = () => { return updateModelList } + +export const useAnthropicBuyQuota = () => { + const [loading, setLoading] = useState(false) + + const handleGetPayUrl = async () => { + if (loading) + return + + setLoading(true) + try { + const res = await getPayUrl('/workspaces/current/model-providers/anthropic/checkout-url') + + window.location.href = res.url + } + finally { + setLoading(false) + } + } + + return handleGetPayUrl +} + +export const useFreeQuota = (onSuccess: () => void) => { + const [loading, setLoading] = useState(false) + + const handleClick = async (type: string) => { + if (loading) + return + + try { + setLoading(true) + const res = await submitFreeQuota(`/workspaces/current/model-providers/${type}/free-quota-submit`) + + if (res.type === 'redirect' && res.redirect_url) + window.location.href = res.redirect_url + else if (res.type === 'submit' && res.result === 'success') + onSuccess() + } + finally { + setLoading(false) + } + } + + return handleClick +} diff --git a/web/app/components/header/account-setting/model-provider-page/model-icon/index.tsx b/web/app/components/header/account-setting/model-provider-page/model-icon/index.tsx index 87ec7d2b5ae42d..e9e9bcccace680 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-icon/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-icon/index.tsx @@ -5,17 +5,23 @@ import type { } from '../declarations' import { useLanguage } from '../hooks' import { CubeOutline } from '@/app/components/base/icons/src/vender/line/shapes' +import { OpenaiViolet } from '@/app/components/base/icons/src/public/llm' type ModelIconProps = { provider?: Model | ModelProvider + modelName?: string className?: string } const ModelIcon: FC = ({ provider, className, + modelName, }) => { const language = useLanguage() + if (provider?.provider === 'openai' && modelName?.startsWith('gpt-4')) + return + if (provider?.icon_small) { return ( = ({ return (
diff --git a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/index.tsx b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/index.tsx index 32358dbee827af..f851c28121e0d6 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/index.tsx @@ -118,6 +118,7 @@ const ModelParameterModal: FC = ({ ) } diff --git a/web/app/components/header/account-setting/model-provider-page/model-selector/model-trigger.tsx b/web/app/components/header/account-setting/model-provider-page/model-selector/model-trigger.tsx index 8937e12e90817e..61eff74b7e0d42 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-selector/model-trigger.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-selector/model-trigger.tsx @@ -28,6 +28,7 @@ const ModelTrigger: FC = ({ = ({ ${modelItem.status !== ModelStatusEnum.active && 'opacity-60'} `} provider={model} + modelName={modelItem.model} /> = ({ = ({ provider, }) => { const { t } = useTranslation() + const handlePay = useAnthropicBuyQuota() const customConfig = provider.custom_configuration const priorityUseType = provider.preferred_provider_type + const systemConfig = provider.system_configuration + const currentQuota = systemConfig.quota_configurations.find(item => item.quota_type === systemConfig.current_quota_type) return ( -
+
{t('common.modelProvider.quota')}
- 200 - Call times -
- + { + provider.provider === 'anthropic' && ( + + ) + } { priorityUseType === PreferredProviderTypeEnum.system && customConfig.status === CustomConfigurationStatusEnum.active && ( diff --git a/web/app/components/header/account-setting/model-provider-page/provider-card/index.module.css b/web/app/components/header/account-setting/model-provider-page/provider-card/index.module.css new file mode 100644 index 00000000000000..88c9fd015ea319 --- /dev/null +++ b/web/app/components/header/account-setting/model-provider-page/provider-card/index.module.css @@ -0,0 +1,4 @@ +.vender { + background: linear-gradient(131deg, #2250F2 0%, #0EBCF3 100%); + background-clip: text; +} \ No newline at end of file diff --git a/web/app/components/header/account-setting/model-provider-page/provider-card/index.tsx b/web/app/components/header/account-setting/model-provider-page/provider-card/index.tsx index befa16dd7ee2bc..3bd7aa572beee5 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-card/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-card/index.tsx @@ -1,28 +1,58 @@ import type { FC } from 'react' +import { useSWRConfig } from 'swr' import { useTranslation } from 'react-i18next' -import type { ModelProvider } from '../declarations' +import type { + ModelProvider, + TypeWithI18N, +} from '../declarations' import { ConfigurateMethodEnum } from '../declarations' import { DEFAULT_BACKGROUND_COLOR, modelTypeFormat, } from '../utils' -import { useLanguage } from '../hooks' +import { + useAnthropicBuyQuota, + useFreeQuota, + useLanguage, +} from '../hooks' import ModelBadge from '../model-badge' import ProviderIcon from '../provider-icon' +import s from './index.module.css' 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' type ProviderCardProps = { provider: ModelProvider onOpenModal: (configurateMethod: ConfigurateMethodEnum) => void } + +const TIP_MAP: { [k: string]: TypeWithI18N } = { + minimax: { + en_US: 'Earn 1 million tokens for free', + zh_Hans: '免费获取 100 万个 token', + }, + spark: { + en_US: 'Earn 3 million tokens (v3.0) for free', + zh_Hans: '免费获取 300 万个 token (v3.0)', + }, + zhipuai: { + en_US: 'Earn 10 million tokens for free', + zh_Hans: '免费获取 1000 万个 token', + }, +} const ProviderCard: FC = ({ provider, onOpenModal, }) => { const { t } = useTranslation() const language = useLanguage() + const { mutate } = useSWRConfig() + const handlePay = useAnthropicBuyQuota() + const handleFreeQuotaSuccess = () => { + mutate('/workspaces/current/model-providers') + } + const handleFreeQuota = useFreeQuota(handleFreeQuotaSuccess) const configurateMethods = provider.configurate_methods.filter(method => method !== ConfigurateMethodEnum.fetchFromRemote) return ( @@ -49,7 +79,28 @@ const ProviderCard: FC = ({ )) } + { + ['mininmax', 'spark', 'zhipuai'].includes(provider.provider) && ( +
+ 📣 +
{TIP_MAP[provider.provider][language]}
+
+ ) + }
+ { + ['mininmax', 'spark', 'zhipuai'].includes(provider.provider) && ( +
+ +
+ ) + }
{ configurateMethods.map((method) => { @@ -77,10 +128,17 @@ const ProviderCard: FC = ({ ) }) } - {/* */} + { + provider.provider === 'anthropic' && ( + + ) + }
diff --git a/web/app/components/header/account-setting/model-provider-page/provider-icon/index.tsx b/web/app/components/header/account-setting/model-provider-page/provider-icon/index.tsx index 4d4dac97551f47..6b423ce75c9f74 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-icon/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-icon/index.tsx @@ -1,7 +1,6 @@ import type { FC } from 'react' import type { ModelProvider } from '../declarations' import { useLanguage } from '../hooks' -import { CubeOutline } from '@/app/components/base/icons/src/vender/line/shapes' type ProviderIconProps = { provider: ModelProvider @@ -25,9 +24,6 @@ const ProviderIcon: FC = ({ return (
-
- -
{provider.label[language]}
diff --git a/web/i18n/lang/common.en.ts b/web/i18n/lang/common.en.ts index e10a5a5d52f93e..8a0c07b9336912 100644 --- a/web/i18n/lang/common.en.ts +++ b/web/i18n/lang/common.en.ts @@ -295,6 +295,9 @@ const translation = { modelAndParameters: 'Model and Parameters', model: 'Model', featureSupported: '{{feature}} supported', + callTimes: 'Call times', + buyQuota: 'Buy Quota', + getFreeTokens: 'Get free Tokens', }, dataSource: { add: 'Add a data source', diff --git a/web/i18n/lang/common.zh.ts b/web/i18n/lang/common.zh.ts index 8ef5866b1893b9..cab14457580a50 100644 --- a/web/i18n/lang/common.zh.ts +++ b/web/i18n/lang/common.zh.ts @@ -295,6 +295,9 @@ const translation = { modelAndParameters: '模型及参数', model: '模型', featureSupported: '支持 {{feature}} 功能', + callTimes: '调用次数', + buyQuota: '购买额度', + getFreeTokens: '获得免费 Tokens', }, dataSource: { add: '添加数据源', From 13c9794ec4dcbc4e2da5d5e8a02e16abf90f910b Mon Sep 17 00:00:00 2001 From: StyleZhang Date: Wed, 27 Dec 2023 20:05:31 +0800 Subject: [PATCH 24/46] change priority --- .../model-selector/index.tsx | 2 + .../provider-added-card/credential-panel.tsx | 23 +++++++- .../provider-added-card/index.tsx | 3 +- .../provider-added-card/priority-selector.tsx | 4 +- .../provider-added-card/priority-use-tip.tsx | 8 ++- .../provider-added-card/quota-panel.tsx | 58 +++++++++++++++---- .../provider-card/index.tsx | 18 ++++-- web/i18n/lang/common.en.ts | 1 + web/i18n/lang/common.zh.ts | 1 + 9 files changed, 94 insertions(+), 24 deletions(-) diff --git a/web/app/components/header/account-setting/model-provider-page/model-selector/index.tsx b/web/app/components/header/account-setting/model-provider-page/model-selector/index.tsx index 5a3b69e6794dfb..59496cc45f4973 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-selector/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-selector/index.tsx @@ -27,6 +27,7 @@ const ModelSelector: FC = ({ modelList, popupClassName, onSelect, + readonly, }) => { const [open, setOpen] = useState(false) const { @@ -55,6 +56,7 @@ const ModelSelector: FC = ({ setOpen(v => !v)} className='block' + disabled={readonly} > { currentModel && currentProvider && ( diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/credential-panel.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/credential-panel.tsx index 0683e896791899..87752dba6079c0 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/credential-panel.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/credential-panel.tsx @@ -1,15 +1,19 @@ import type { FC } from 'react' import { useTranslation } from 'react-i18next' +import { useSWRConfig } from 'swr' import type { ModelProvider } from '../declarations' import { CustomConfigurationStatusEnum, PreferredProviderTypeEnum, } from '../declarations' +import { useUpdateModelList } from '../hooks' import PrioritySelector from './priority-selector' import PriorityUseTip from './priority-use-tip' import Indicator from '@/app/components/header/indicator' import { Settings01 } from '@/app/components/base/icons/src/vender/line/general' import Button from '@/app/components/base/button' +import { changeModelProviderPriority } from '@/service/common' +import { useToastContext } from '@/app/components/base/toast' type CredentialPanelProps = { provider: ModelProvider @@ -20,12 +24,27 @@ const CredentialPanel: FC = ({ onSetup, }) => { const { t } = useTranslation() + const { notify } = useToastContext() + const { mutate } = useSWRConfig() + const updateModelList = useUpdateModelList() const customConfig = provider.custom_configuration const systemConfig = provider.system_configuration const priorityUseType = provider.preferred_provider_type const customConfiged = customConfig.status === CustomConfigurationStatusEnum.active - const handleChangePriority = () => {} + const handleChangePriority = async (key: PreferredProviderTypeEnum) => { + const res = await changeModelProviderPriority({ + url: `/workspaces/current/model-providers/${provider.provider}/preferred-provider-type`, + body: { + preferred_provider_type: key, + }, + }) + if (res.result === 'success') { + notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') }) + mutate('/workspaces/current/model-providers') + updateModelList(1) + } + } return (
@@ -44,8 +63,8 @@ const CredentialPanel: FC = ({ { systemConfig.enabled && customConfiged && ( ) } diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/index.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/index.tsx index aa1af799cdc494..0cd4f7b3ee801d 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/index.tsx @@ -37,6 +37,7 @@ const ProviderAddedCard: FC = ({ const configurateMethods = provider.configurate_methods.filter(method => method !== ConfigurateMethodEnum.fetchFromRemote) const systemConfig = provider.system_configuration const hasModelList = fetched && !!modelList.length + const showQuota = systemConfig.enabled || ['minimax', 'spark', 'zhipuai', 'anthropic'].includes(provider.provider) const handleOpenModelList = async () => { if (fetched) { @@ -78,7 +79,7 @@ const ProviderAddedCard: FC = ({
{ - systemConfig.enabled && ( + showQuota && ( diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/priority-selector.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/priority-selector.tsx index a5d84ada0e0658..3b9f73c55c0f68 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/priority-selector.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/priority-selector.tsx @@ -8,7 +8,7 @@ import Button from '@/app/components/base/button' type SelectorProps = { value?: string - onSelect: (v: Record) => void + onSelect: (key: PreferredProviderTypeEnum) => void } const Selector: FC = ({ value, @@ -54,7 +54,7 @@ const Selector: FC = ({
onSelect({ type: 'priority', value: option.key })} + onClick={() => onSelect(option.key)} >
{option.text}
{value === option.key && } diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/priority-use-tip.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/priority-use-tip.tsx index e46543f92db39e..294a13ecfcbc6b 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/priority-use-tip.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/priority-use-tip.tsx @@ -1,9 +1,15 @@ +import { useTranslation } from 'react-i18next' import { ChevronDownDouble } from '@/app/components/base/icons/src/vender/line/arrows' import Tooltip from '@/app/components/base/tooltip' const PriorityUseTip = () => { + const { t } = useTranslation() + return ( - +
diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/quota-panel.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/quota-panel.tsx index ede6ff9e5639be..4a8731e89a2a46 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/quota-panel.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/quota-panel.tsx @@ -1,4 +1,5 @@ import type { FC } from 'react' +import { useSWRConfig } from 'swr' import { useTranslation } from 'react-i18next' import type { ModelProvider } from '../declarations' import { @@ -6,9 +7,13 @@ import { PreferredProviderTypeEnum, QuotaUnitEnum, } from '../declarations' -import { useAnthropicBuyQuota } from '../hooks' +import { + useAnthropicBuyQuota, + useFreeQuota, +} from '../hooks' import PriorityUseTip from './priority-use-tip' import { InfoCircle } from '@/app/components/base/icons/src/vender/line/general' +import Button from '@/app/components/base/button' type QuotaPanelProps = { provider: ModelProvider @@ -17,11 +22,16 @@ const QuotaPanel: FC = ({ provider, }) => { const { t } = useTranslation() + const { mutate } = useSWRConfig() const handlePay = useAnthropicBuyQuota() + const handleFreeQuotaSuccess = () => { + mutate('/workspaces/current/model-providers') + } + const handleFreeQuota = useFreeQuota(handleFreeQuotaSuccess) const customConfig = provider.custom_configuration const priorityUseType = provider.preferred_provider_type const systemConfig = provider.system_configuration - const currentQuota = 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) return (
@@ -29,17 +39,41 @@ const QuotaPanel: FC = ({ {t('common.modelProvider.quota')}
-
- {(currentQuota?.quota_limit || 0) - (currentQuota?.quota_used || 0)} - { - currentQuota?.quota_unit === QuotaUnitEnum.tokens && 'Tokens' - } - { - currentQuota?.quota_unit === QuotaUnitEnum.times && t('common.modelProvider.callTimes') - } -
{ - provider.provider === 'anthropic' && ( + currentQuota && ( +
+ {(currentQuota?.quota_limit || 0) - (currentQuota?.quota_used || 0)} + { + currentQuota?.quota_unit === QuotaUnitEnum.tokens && 'Tokens' + } + { + currentQuota?.quota_unit === QuotaUnitEnum.times && t('common.modelProvider.callTimes') + } +
+ ) + } + { + !currentQuota && provider.provider === 'anthropic' && ( + + ) + } + { + !currentQuota && ['minimax', 'spark', 'zhipuai'].includes(provider.provider) && ( + + ) + } + { + provider.provider === 'anthropic' && systemConfig.enabled && (