model selector in endpoint modal

This commit is contained in:
JzoNg 2024-11-12 16:40:43 +08:00
parent 75a037bc2a
commit b188800f16
3 changed files with 98 additions and 28 deletions

View File

@ -1,6 +1,6 @@
export type FormValue = Record<string, any> export type FormValue = Record<string, any>
export interface TypeWithI18N<T = string> { export type TypeWithI18N<T = string> = {
en_US: T en_US: T
zh_Hans: T zh_Hans: T
[key: string]: T [key: string]: T
@ -15,9 +15,12 @@ export enum FormTypeEnum {
boolean = 'boolean', boolean = 'boolean',
files = 'files', files = 'files',
file = 'file', file = 'file',
modelSelector = 'model-selector',
toolSelector = 'tool-selector',
appSelector = 'app-selector',
} }
export interface FormOption { export type FormOption = {
label: TypeWithI18N label: TypeWithI18N
value: string value: string
show_on: FormShowOnObject[] show_on: FormShowOnObject[]
@ -89,12 +92,12 @@ export enum CustomConfigurationStatusEnum {
noConfigure = 'no-configure', noConfigure = 'no-configure',
} }
export interface FormShowOnObject { export type FormShowOnObject = {
variable: string variable: string
value: string value: string
} }
export interface CredentialFormSchemaBase { export type CredentialFormSchemaBase = {
variable: string variable: string
label: TypeWithI18N label: TypeWithI18N
type: FormTypeEnum type: FormTypeEnum
@ -112,7 +115,7 @@ export type CredentialFormSchemaRadio = CredentialFormSchemaBase & { options: Fo
export type CredentialFormSchemaSecretInput = CredentialFormSchemaBase & { placeholder?: TypeWithI18N } export type CredentialFormSchemaSecretInput = CredentialFormSchemaBase & { placeholder?: TypeWithI18N }
export type CredentialFormSchema = CredentialFormSchemaTextInput | CredentialFormSchemaSelect | CredentialFormSchemaRadio | CredentialFormSchemaSecretInput export type CredentialFormSchema = CredentialFormSchemaTextInput | CredentialFormSchemaSelect | CredentialFormSchemaRadio | CredentialFormSchemaSecretInput
export interface ModelItem { export type ModelItem = {
model: string model: string
label: TypeWithI18N label: TypeWithI18N
model_type: ModelTypeEnum model_type: ModelTypeEnum
@ -141,7 +144,7 @@ export enum QuotaUnitEnum {
credits = 'credits', credits = 'credits',
} }
export interface QuotaConfiguration { export type QuotaConfiguration = {
quota_type: CurrentSystemQuotaTypeEnum quota_type: CurrentSystemQuotaTypeEnum
quota_unit: QuotaUnitEnum quota_unit: QuotaUnitEnum
quota_limit: number quota_limit: number
@ -150,7 +153,7 @@ export interface QuotaConfiguration {
is_valid: boolean is_valid: boolean
} }
export interface ModelProvider { export type ModelProvider = {
provider: string provider: string
label: TypeWithI18N label: TypeWithI18N
description?: TypeWithI18N description?: TypeWithI18N
@ -184,7 +187,7 @@ export interface ModelProvider {
} }
} }
export interface Model { export type Model = {
provider: string provider: string
icon_large: TypeWithI18N icon_large: TypeWithI18N
icon_small: TypeWithI18N icon_small: TypeWithI18N
@ -193,7 +196,7 @@ export interface Model {
status: ModelStatusEnum status: ModelStatusEnum
} }
export interface DefaultModelResponse { export type DefaultModelResponse = {
model: string model: string
model_type: ModelTypeEnum model_type: ModelTypeEnum
provider: { provider: {
@ -203,17 +206,17 @@ export interface DefaultModelResponse {
} }
} }
export interface DefaultModel { export type DefaultModel = {
provider: string provider: string
model: string model: string
} }
export interface CustomConfigurationModelFixedFields { export type CustomConfigurationModelFixedFields = {
__model_name: string __model_name: string
__model_type: ModelTypeEnum __model_type: ModelTypeEnum
} }
export interface ModelParameterRule { export type ModelParameterRule = {
default?: number | string | boolean | string[] default?: number | string | boolean | string[]
help?: TypeWithI18N help?: TypeWithI18N
label: TypeWithI18N label: TypeWithI18N
@ -228,7 +231,7 @@ export interface ModelParameterRule {
tagPlaceholder?: TypeWithI18N tagPlaceholder?: TypeWithI18N
} }
export interface ModelLoadBalancingConfigEntry { export type ModelLoadBalancingConfigEntry = {
/** model balancing config entry id */ /** model balancing config entry id */
id?: string id?: string
/** is config entry enabled */ /** is config entry enabled */
@ -243,7 +246,7 @@ export interface ModelLoadBalancingConfigEntry {
ttl?: number ttl?: number
} }
export interface ModelLoadBalancingConfig { export type ModelLoadBalancingConfig = {
enabled: boolean enabled: boolean
configs: ModelLoadBalancingConfigEntry[] configs: ModelLoadBalancingConfigEntry[]
} }

View File

@ -1,4 +1,4 @@
import { useState } from 'react' import { useCallback, useState } from 'react'
import type { FC } from 'react' import type { FC } from 'react'
import { ValidatingTip } from '../../key-validator/ValidateStatus' import { ValidatingTip } from '../../key-validator/ValidateStatus'
import type { import type {
@ -17,6 +17,8 @@ import cn from '@/utils/classnames'
import { SimpleSelect } from '@/app/components/base/select' import { SimpleSelect } from '@/app/components/base/select'
import Tooltip from '@/app/components/base/tooltip' import Tooltip from '@/app/components/base/tooltip'
import Radio from '@/app/components/base/radio' import Radio from '@/app/components/base/radio'
import ModelParameterModal from '@/app/components/header/account-setting/model-provider-page/model-parameter-modal'
type FormProps = { type FormProps = {
className?: string className?: string
itemClassName?: string itemClassName?: string
@ -67,6 +69,24 @@ const Form: FC<FormProps> = ({
onChange({ ...value, [key]: val, ...shouldClearVariable }) onChange({ ...value, [key]: val, ...shouldClearVariable })
} }
const handleModelChanged = useCallback((key: string, model: { provider: string; modelId: string; mode?: string }) => {
const newValue = {
...value[key],
provider: model.provider,
name: model.modelId,
mode: model.mode,
}
onChange({ ...value, [key]: newValue })
}, [onChange, value])
const handleCompletionParamsChange = useCallback((key: string, newParams: Record<string, any>) => {
const newValue = {
...value[key],
completion_params: newParams,
}
onChange({ ...value, [key]: newValue })
}, [onChange, value])
const renderField = (formSchema: CredentialFormSchema) => { const renderField = (formSchema: CredentialFormSchema) => {
const tooltip = formSchema.tooltip const tooltip = formSchema.tooltip
const tooltipContent = (tooltip && ( const tooltipContent = (tooltip && (
@ -94,7 +114,7 @@ const Form: FC<FormProps> = ({
const disabled = readonly || (isEditMode && (variable === '__model_type' || variable === '__model_name')) const disabled = readonly || (isEditMode && (variable === '__model_type' || variable === '__model_name'))
return ( return (
<div key={variable} className={cn(itemClassName, 'py-3')}> <div key={variable} className={cn(itemClassName, 'py-3')}>
<div className={cn(fieldLabelClassName, 'flex items-center py-2 text-sm text-gray-900')}> <div className={cn(fieldLabelClassName, 'flex items-center py-2 system-sm-regular text-text-secondary')}>
{label[language] || label.en_US} {label[language] || label.en_US}
{ {
required && ( required && (
@ -135,7 +155,7 @@ const Form: FC<FormProps> = ({
return ( return (
<div key={variable} className={cn(itemClassName, 'py-3')}> <div key={variable} className={cn(itemClassName, 'py-3')}>
<div className={cn(fieldLabelClassName, 'flex items-center py-2 text-sm text-gray-900')}> <div className={cn(fieldLabelClassName, 'flex items-center py-2 system-sm-regular text-text-secondary')}>
{label[language] || label.en_US} {label[language] || label.en_US}
{ {
required && ( required && (
@ -165,7 +185,7 @@ const Form: FC<FormProps> = ({
flex justify-center items-center mr-2 w-4 h-4 border border-gray-300 rounded-full flex justify-center items-center mr-2 w-4 h-4 border border-gray-300 rounded-full
${value[variable] === option.value && 'border-[5px] border-primary-600'} ${value[variable] === option.value && 'border-[5px] border-primary-600'}
`} /> `} />
<div className='text-sm text-gray-900'>{option.label[language] || option.label.en_US}</div> <div className='system-sm-regular text-text-secondary'>{option.label[language] || option.label.en_US}</div>
</div> </div>
)) ))
} }
@ -176,7 +196,7 @@ const Form: FC<FormProps> = ({
) )
} }
if (formSchema.type === 'select') { if (formSchema.type === FormTypeEnum.select) {
const { const {
options, options,
variable, variable,
@ -191,7 +211,7 @@ const Form: FC<FormProps> = ({
return ( return (
<div key={variable} className={cn(itemClassName, 'py-3')}> <div key={variable} className={cn(itemClassName, 'py-3')}>
<div className={cn(fieldLabelClassName, 'flex items-center py-2 text-sm text-gray-900')}> <div className={cn(fieldLabelClassName, 'flex items-center py-2 system-sm-regular text-text-secondary')}>
{label[language] || label.en_US} {label[language] || label.en_US}
{ {
@ -202,6 +222,7 @@ const Form: FC<FormProps> = ({
{tooltipContent} {tooltipContent}
</div> </div>
<SimpleSelect <SimpleSelect
wrapperClassName='h-8'
className={cn(inputClassName)} className={cn(inputClassName)}
disabled={readonly} disabled={readonly}
defaultValue={(isShowDefaultValue && ((value[variable] as string) === '' || value[variable] === undefined || value[variable] === null)) ? formSchema.default : value[variable]} defaultValue={(isShowDefaultValue && ((value[variable] as string) === '' || value[variable] === undefined || value[variable] === null)) ? formSchema.default : value[variable]}
@ -220,7 +241,7 @@ const Form: FC<FormProps> = ({
) )
} }
if (formSchema.type === 'boolean') { if (formSchema.type === FormTypeEnum.boolean) {
const { const {
variable, variable,
label, label,
@ -233,9 +254,9 @@ const Form: FC<FormProps> = ({
return ( return (
<div key={variable} className={cn(itemClassName, 'py-3')}> <div key={variable} className={cn(itemClassName, 'py-3')}>
<div className='flex items-center justify-between py-2 text-sm text-gray-900'> <div className='flex items-center justify-between py-2 system-sm-regular text-text-secondary'>
<div className='flex items-center space-x-2'> <div className='flex items-center space-x-2'>
<span className={cn(fieldLabelClassName, 'flex items-center py-2 text-sm text-gray-900')}>{label[language] || label.en_US}</span> <span className={cn(fieldLabelClassName, 'flex items-center py-2 system-sm-regular text-text-secondary')}>{label[language] || label.en_US}</span>
{ {
required && ( required && (
<span className='ml-1 text-red-500'>*</span> <span className='ml-1 text-red-500'>*</span>
@ -256,6 +277,52 @@ const Form: FC<FormProps> = ({
</div> </div>
) )
} }
if (formSchema.type === FormTypeEnum.modelSelector) {
const {
variable,
label,
required,
} = formSchema as (CredentialFormSchemaTextInput | CredentialFormSchemaSecretInput)
return (
<div key={variable} className={cn(itemClassName, 'py-3')}>
<div className={cn(fieldLabelClassName, 'flex items-center py-2 system-sm-regular text-text-secondary')}>
{label[language] || label.en_US}
{
required && (
<span className='ml-1 text-red-500'>*</span>
)
}
{tooltipContent}
</div>
<ModelParameterModal
popupClassName='!w-[387px]'
isAdvancedMode
isInWorkflow
provider={value[variable]?.provider}
modelId={value[variable]?.name}
mode={value[variable]?.mode}
completionParams={value[variable]?.completion_params}
setModel={model => handleModelChanged(variable, model)}
onCompletionParamsChange={params => handleCompletionParamsChange(variable, params)}
hideDebugWithMultipleModel
debugWithMultipleModel={false}
readonly={readonly}
/>
{fieldMoreInfo?.(formSchema)}
{validating && changeKey === variable && <ValidatingTip />}
</div>
)
}
if (formSchema.type === FormTypeEnum.toolSelector) {
// TODO
}
if (formSchema.type === FormTypeEnum.appSelector) {
// TODO
}
} }
return ( return (

View File

@ -26,14 +26,14 @@ const Input: FC<InputProps> = ({
max, max,
}) => { }) => {
const toLimit = (v: string) => { const toLimit = (v: string) => {
const minNum = parseFloat(`${min}`) const minNum = Number.parseFloat(`${min}`)
const maxNum = parseFloat(`${max}`) const maxNum = Number.parseFloat(`${max}`)
if (!isNaN(minNum) && parseFloat(v) < minNum) { if (!isNaN(minNum) && Number.parseFloat(v) < minNum) {
onChange(`${min}`) onChange(`${min}`)
return return
} }
if (!isNaN(maxNum) && parseFloat(v) > maxNum) if (!isNaN(maxNum) && Number.parseFloat(v) > maxNum)
onChange(`${max}`) onChange(`${max}`)
} }
return ( return (
@ -41,7 +41,7 @@ const Input: FC<InputProps> = ({
<input <input
tabIndex={0} tabIndex={0}
className={` className={`
block px-3 w-full h-9 bg-gray-100 text-sm rounded-lg border border-transparent block px-3 w-full h-8 bg-gray-100 text-sm rounded-lg border border-transparent
appearance-none outline-none caret-primary-600 appearance-none outline-none caret-primary-600
hover:border-[rgba(0,0,0,0.08)] hover:bg-gray-50 hover:border-[rgba(0,0,0,0.08)] hover:bg-gray-50
focus:bg-white focus:border-gray-300 focus:shadow-xs focus:bg-white focus:border-gray-300 focus:shadow-xs