Co-authored-by: StyleZhang <jasonapring2015@outlook.com>tags/0.3.9
| @@ -372,7 +372,7 @@ const Debug: FC<IDebug> = ({ | |||
| {/* Chat */} | |||
| {mode === AppType.chat && ( | |||
| <div className="mt-[34px] h-full flex flex-col"> | |||
| <div className={cn(doShowSuggestion ? 'pb-[140px]' : (isResponsing ? 'pb-[113px]' : 'pb-[66px]'), 'relative mt-1.5 grow h-[200px] overflow-hidden')}> | |||
| <div className={cn(doShowSuggestion ? 'pb-[140px]' : (isResponsing ? 'pb-[113px]' : 'pb-[76px]'), 'relative mt-1.5 grow h-[200px] overflow-hidden')}> | |||
| <div className="h-full overflow-y-auto overflow-x-hidden" ref={chatListDomRef}> | |||
| <Chat | |||
| chatList={chatList} | |||
| @@ -16,6 +16,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 { ProviderType } from '@/types/app' | |||
| import type { AppDetailResponse } from '@/models/app' | |||
| import { ToastContext } from '@/app/components/base/toast' | |||
| import { fetchTenantInfo } from '@/service/common' | |||
| @@ -67,7 +68,7 @@ const Configuration: FC = () => { | |||
| frequency_penalty: 1, // -2-2 | |||
| }) | |||
| const [modelConfig, doSetModelConfig] = useState<ModelConfig>({ | |||
| provider: 'openai', | |||
| provider: ProviderType.openai, | |||
| model_id: 'gpt-3.5-turbo', | |||
| configs: { | |||
| prompt_template: '', | |||
| @@ -84,8 +85,9 @@ const Configuration: FC = () => { | |||
| doSetModelConfig(newModelConfig) | |||
| } | |||
| const setModelId = (modelId: string) => { | |||
| const setModelId = (modelId: string, provider: ProviderType) => { | |||
| const newModelConfig = produce(modelConfig, (draft: any) => { | |||
| draft.provider = provider | |||
| draft.model_id = modelId | |||
| }) | |||
| setModelConfig(newModelConfig) | |||
| @@ -19,6 +19,7 @@ const AutoHeightTextarea = forwardRef( | |||
| { value, onChange, placeholder, className, minHeight = 36, maxHeight = 96, autoFocus, controlFocus, onKeyDown, onKeyUp }: IProps, | |||
| outerRef: any, | |||
| ) => { | |||
| // eslint-disable-next-line react-hooks/rules-of-hooks | |||
| const ref = outerRef || useRef<HTMLTextAreaElement>(null) | |||
| const doFocus = () => { | |||
| @@ -54,13 +55,20 @@ const AutoHeightTextarea = forwardRef( | |||
| return ( | |||
| <div className='relative'> | |||
| <div className={cn(className, 'invisible whitespace-pre-wrap break-all overflow-y-auto')} style={{ minHeight, maxHeight }}> | |||
| <div className={cn(className, 'invisible whitespace-pre-wrap break-all overflow-y-auto')} style={{ | |||
| minHeight, | |||
| maxHeight, | |||
| paddingRight: (value && value.trim().length > 10000) ? 140 : 130, | |||
| }}> | |||
| {!value ? placeholder : value.replace(/\n$/, '\n ')} | |||
| </div> | |||
| <textarea | |||
| ref={ref} | |||
| autoFocus={autoFocus} | |||
| className={cn(className, 'absolute inset-0 resize-none overflow-hidden')} | |||
| className={cn(className, 'absolute inset-0 resize-none overflow-auto')} | |||
| style={{ | |||
| paddingRight: (value && value.trim().length > 10000) ? 140 : 130, | |||
| }} | |||
| placeholder={placeholder} | |||
| onChange={onChange} | |||
| onKeyDown={onKeyDown} | |||
| @@ -0,0 +1,24 @@ | |||
| .icon { | |||
| width: 24px; | |||
| height: 24px; | |||
| margin-right: 12px; | |||
| background: url(../../../assets/anthropic.svg) center center no-repeat; | |||
| background-size: contain; | |||
| } | |||
| .bar { | |||
| background: linear-gradient(90deg, rgba(41, 112, 255, 0.9) 0%, rgba(21, 94, 239, 0.9) 100%); | |||
| } | |||
| .bar-error { | |||
| background: linear-gradient(90deg, rgba(240, 68, 56, 0.72) 0%, rgba(217, 45, 32, 0.9) 100%); | |||
| } | |||
| .bar-item { | |||
| width: 10%; | |||
| border-right: 1px solid rgba(255, 255, 255, 0.5); | |||
| } | |||
| .bar-item:last-of-type { | |||
| border-right: 0; | |||
| } | |||
| @@ -0,0 +1,65 @@ | |||
| import { useTranslation } from 'react-i18next' | |||
| import cn from 'classnames' | |||
| import s from './index.module.css' | |||
| import type { ProviderHosted } from '@/models/common' | |||
| type AnthropicHostedProviderProps = { | |||
| provider: ProviderHosted | |||
| } | |||
| const AnthropicHostedProvider = ({ | |||
| provider, | |||
| }: AnthropicHostedProviderProps) => { | |||
| const { t } = useTranslation() | |||
| const exhausted = provider.quota_used > provider.quota_limit | |||
| return ( | |||
| <div className={` | |||
| border-[0.5px] border-gray-200 rounded-xl | |||
| ${exhausted ? 'bg-[#FFFBFA]' : 'bg-gray-50'} | |||
| `}> | |||
| <div className='pt-4 px-4 pb-3'> | |||
| <div className='flex items-center mb-3'> | |||
| <div className={s.icon} /> | |||
| <div className='grow text-sm font-medium text-gray-800'> | |||
| {t('common.provider.anthropicHosted.anthropicHosted')} | |||
| </div> | |||
| <div className={` | |||
| px-2 h-[22px] flex items-center rounded-md border | |||
| text-xs font-semibold | |||
| ${exhausted ? 'border-[#D92D20] text-[#D92D20]' : 'border-primary-600 text-primary-600'} | |||
| `}> | |||
| {exhausted ? t('common.provider.anthropicHosted.exhausted') : t('common.provider.anthropicHosted.onTrial')} | |||
| </div> | |||
| </div> | |||
| <div className='text-[13px] text-gray-500'>{t('common.provider.anthropicHosted.desc')}</div> | |||
| </div> | |||
| <div className='flex items-center h-[42px] px-4 border-t-[0.5px] border-t-[rgba(0, 0, 0, 0.05)]'> | |||
| <div className='text-[13px] text-gray-700'>{t('common.provider.anthropicHosted.callTimes')}</div> | |||
| <div className='relative grow h-2 flex bg-gray-200 rounded-md mx-2 overflow-hidden'> | |||
| <div | |||
| className={cn(s.bar, exhausted && s['bar-error'], 'absolute top-0 left-0 right-0 bottom-0')} | |||
| style={{ width: `${(provider.quota_used / provider.quota_limit * 100).toFixed(2)}%` }} | |||
| /> | |||
| {Array(10).fill(0).map((i, k) => ( | |||
| <div key={k} className={s['bar-item']} /> | |||
| ))} | |||
| </div> | |||
| <div className={` | |||
| text-[13px] font-medium ${exhausted ? 'text-[#D92D20]' : 'text-gray-700'} | |||
| `}>{provider.quota_used}/{provider.quota_limit}</div> | |||
| </div> | |||
| { | |||
| exhausted && ( | |||
| <div className=' | |||
| px-4 py-3 leading-[18px] flex items-center text-[13px] text-gray-700 font-medium | |||
| bg-[#FFFAEB] border-t border-t-[rgba(0, 0, 0, 0.05)] rounded-b-xl | |||
| '> | |||
| {t('common.provider.anthropicHosted.usedUp')} | |||
| </div> | |||
| ) | |||
| } | |||
| </div> | |||
| ) | |||
| } | |||
| export default AnthropicHostedProvider | |||
| @@ -0,0 +1,90 @@ | |||
| import { useEffect, useState } from 'react' | |||
| import { useTranslation } from 'react-i18next' | |||
| import Link from 'next/link' | |||
| import { ArrowTopRightOnSquareIcon } from '@heroicons/react/24/outline' | |||
| import ProviderInput from '../provider-input' | |||
| import type { ValidatedStatusState } from '../provider-input/useValidateToken' | |||
| import useValidateToken, { ValidatedStatus } from '../provider-input/useValidateToken' | |||
| import { | |||
| ValidatedErrorIcon, | |||
| ValidatedErrorOnOpenaiTip, | |||
| ValidatedSuccessIcon, | |||
| ValidatingTip, | |||
| } from '../provider-input/Validate' | |||
| import type { Provider, ProviderAnthropicToken } from '@/models/common' | |||
| type AnthropicProviderProps = { | |||
| provider: Provider | |||
| onValidatedStatus: (status?: ValidatedStatusState) => void | |||
| onTokenChange: (token: ProviderAnthropicToken) => void | |||
| } | |||
| const AnthropicProvider = ({ | |||
| provider, | |||
| onValidatedStatus, | |||
| onTokenChange, | |||
| }: AnthropicProviderProps) => { | |||
| const { t } = useTranslation() | |||
| const [token, setToken] = useState<ProviderAnthropicToken>((provider.token as ProviderAnthropicToken) || { anthropic_api_key: '' }) | |||
| const [validating, validatedStatus, setValidatedStatus, validate] = useValidateToken(provider.provider_name) | |||
| const handleFocus = () => { | |||
| if (token.anthropic_api_key === (provider.token as ProviderAnthropicToken).anthropic_api_key) { | |||
| setToken({ anthropic_api_key: '' }) | |||
| onTokenChange({ anthropic_api_key: '' }) | |||
| setValidatedStatus({}) | |||
| } | |||
| } | |||
| const handleChange = (v: string) => { | |||
| const apiKey = { anthropic_api_key: v } | |||
| setToken(apiKey) | |||
| onTokenChange(apiKey) | |||
| validate(apiKey, { | |||
| beforeValidating: () => { | |||
| if (!v) { | |||
| setValidatedStatus({}) | |||
| return false | |||
| } | |||
| return true | |||
| }, | |||
| }) | |||
| } | |||
| useEffect(() => { | |||
| if (typeof onValidatedStatus === 'function') | |||
| onValidatedStatus(validatedStatus) | |||
| }, [validatedStatus]) | |||
| const getValidatedIcon = () => { | |||
| if (validatedStatus?.status === ValidatedStatus.Error || validatedStatus.status === ValidatedStatus.Exceed) | |||
| return <ValidatedErrorIcon /> | |||
| if (validatedStatus.status === ValidatedStatus.Success) | |||
| return <ValidatedSuccessIcon /> | |||
| } | |||
| const getValidatedTip = () => { | |||
| if (validating) | |||
| return <ValidatingTip /> | |||
| if (validatedStatus?.status === ValidatedStatus.Error) | |||
| return <ValidatedErrorOnOpenaiTip errorMessage={validatedStatus.message ?? ''} /> | |||
| } | |||
| return ( | |||
| <div className='px-4 pt-3 pb-4'> | |||
| <ProviderInput | |||
| value={token.anthropic_api_key} | |||
| name={t('common.provider.apiKey')} | |||
| placeholder={t('common.provider.enterYourKey')} | |||
| onChange={handleChange} | |||
| onFocus={handleFocus} | |||
| validatedIcon={getValidatedIcon()} | |||
| validatedTip={getValidatedTip()} | |||
| /> | |||
| <Link className="inline-flex items-center mt-3 text-xs font-normal cursor-pointer text-primary-600 w-fit" href="https://docs.anthropic.com/claude/reference/getting-started-with-the-api" target={'_blank'}> | |||
| {t('common.provider.anthropic.keyFrom')} | |||
| <ArrowTopRightOnSquareIcon className='w-3 h-3 ml-1 text-primary-600' aria-hidden="true" /> | |||
| </Link> | |||
| </div> | |||
| ) | |||
| } | |||
| export default AnthropicProvider | |||
| @@ -5,6 +5,7 @@ import { useTranslation } from 'react-i18next' | |||
| import Link from 'next/link' | |||
| import ProviderItem from './provider-item' | |||
| import OpenaiHostedProvider from './openai-hosted-provider' | |||
| import AnthropicHostedProvider from './anthropic-hosted-provider' | |||
| import type { ProviderHosted } from '@/models/common' | |||
| import { fetchProviders } from '@/service/common' | |||
| import { IS_CE_EDITION } from '@/config' | |||
| @@ -18,6 +19,10 @@ const providersMap: { [k: string]: any } = { | |||
| icon: 'azure', | |||
| name: 'Azure OpenAI Service', | |||
| }, | |||
| 'anthropic-custom': { | |||
| icon: 'anthropic', | |||
| name: 'Anthropic', | |||
| }, | |||
| } | |||
| // const providersList = [ | |||
| @@ -65,6 +70,8 @@ const ProviderPage = () => { | |||
| } | |||
| }) | |||
| const providerHosted = data?.filter(provider => provider.provider_name === 'openai' && provider.provider_type === 'system')?.[0] | |||
| const anthropicHosted = data?.filter(provider => provider.provider_name === 'anthropic' && provider.provider_type === 'system')?.[0] | |||
| const providedOpenaiProvider = data?.find(provider => provider.is_enabled && (provider.provider_name === 'openai' || provider.provider_name === 'azure_openai')) | |||
| return ( | |||
| <div className='pb-7'> | |||
| @@ -78,6 +85,16 @@ const ProviderPage = () => { | |||
| </> | |||
| ) | |||
| } | |||
| { | |||
| anthropicHosted && !IS_CE_EDITION && ( | |||
| <> | |||
| <div> | |||
| <AnthropicHostedProvider provider={anthropicHosted as ProviderHosted} /> | |||
| </div> | |||
| <div className='my-5 w-full h-0 border-[0.5px] border-gray-100' /> | |||
| </> | |||
| ) | |||
| } | |||
| <div> | |||
| { | |||
| providers?.map(providerItem => ( | |||
| @@ -89,6 +106,7 @@ const ProviderPage = () => { | |||
| activeId={activeProviderId} | |||
| onActive={aid => setActiveProviderId(aid)} | |||
| onSave={() => mutate()} | |||
| providedOpenaiProvider={providedOpenaiProvider} | |||
| /> | |||
| )) | |||
| } | |||
| @@ -5,14 +5,20 @@ import { useTranslation } from 'react-i18next' | |||
| import Indicator from '../../../indicator' | |||
| import OpenaiProvider from '../openai-provider' | |||
| import AzureProvider from '../azure-provider' | |||
| import AnthropicProvider from '../anthropic-provider' | |||
| import type { ValidatedStatusState } from '../provider-input/useValidateToken' | |||
| import { ValidatedStatus } from '../provider-input/useValidateToken' | |||
| import s from './index.module.css' | |||
| import type { Provider, ProviderAzureToken } from '@/models/common' | |||
| import type { Provider, ProviderAnthropicToken, ProviderAzureToken } from '@/models/common' | |||
| import { ProviderName } from '@/models/common' | |||
| import { updateProviderAIKey } from '@/service/common' | |||
| import { ToastContext } from '@/app/components/base/toast' | |||
| import Tooltip from '@/app/components/base/tooltip' | |||
| const providerNameMap: Record<string, string> = { | |||
| openai: 'OpenAI', | |||
| azure_openai: 'Azure OpenAI Service', | |||
| } | |||
| type IProviderItemProps = { | |||
| icon: string | |||
| name: string | |||
| @@ -20,6 +26,7 @@ type IProviderItemProps = { | |||
| activeId: string | |||
| onActive: (v: string) => void | |||
| onSave: () => void | |||
| providedOpenaiProvider?: Provider | |||
| } | |||
| const ProviderItem = ({ | |||
| activeId, | |||
| @@ -28,15 +35,18 @@ const ProviderItem = ({ | |||
| provider, | |||
| onActive, | |||
| onSave, | |||
| providedOpenaiProvider, | |||
| }: IProviderItemProps) => { | |||
| const { t } = useTranslation() | |||
| const [validatedStatus, setValidatedStatus] = useState<ValidatedStatusState>() | |||
| const [loading, setLoading] = useState(false) | |||
| const { notify } = useContext(ToastContext) | |||
| const [token, setToken] = useState<ProviderAzureToken | string>( | |||
| const [token, setToken] = useState<ProviderAzureToken | string | ProviderAnthropicToken>( | |||
| provider.provider_name === 'azure_openai' | |||
| ? { openai_api_base: '', openai_api_key: '' } | |||
| : '', | |||
| : provider.provider_name === 'anthropic' | |||
| ? { anthropic_api_key: '' } | |||
| : '', | |||
| ) | |||
| const id = `${provider.provider_name}-${provider.provider_type}` | |||
| const isOpen = id === activeId | |||
| @@ -54,6 +64,8 @@ const ProviderItem = ({ | |||
| } | |||
| if (provider.provider_name === ProviderName.OPENAI) | |||
| return provider.token | |||
| if (provider.provider_name === ProviderName.ANTHROPIC) | |||
| return provider.token?.anthropic_api_key | |||
| } | |||
| const handleUpdateToken = async () => { | |||
| if (loading) | |||
| @@ -81,7 +93,7 @@ const ProviderItem = ({ | |||
| <div className={cn(s[`icon-${icon}`], 'mr-3 w-6 h-6 rounded-md')} /> | |||
| <div className='grow text-sm font-medium text-gray-800'>{name}</div> | |||
| { | |||
| providerTokenHasSetted() && !comingSoon && !isOpen && ( | |||
| providerTokenHasSetted() && !comingSoon && !isOpen && provider.provider_name !== ProviderName.ANTHROPIC && ( | |||
| <div className='flex items-center mr-4'> | |||
| {!isValid && <div className='text-xs text-[#D92D20]'>{t('common.provider.invalidApiKey')}</div>} | |||
| <Indicator color={!isValid ? 'red' : 'green'} className='ml-2' /> | |||
| @@ -89,7 +101,27 @@ const ProviderItem = ({ | |||
| ) | |||
| } | |||
| { | |||
| !comingSoon && !isOpen && ( | |||
| (providerTokenHasSetted() && !comingSoon && !isOpen && provider.provider_name === ProviderName.ANTHROPIC) && ( | |||
| <div className='flex items-center mr-4'> | |||
| { | |||
| providedOpenaiProvider?.is_valid | |||
| ? !isValid | |||
| ? <div className='text-xs text-[#D92D20]'>{t('common.provider.invalidApiKey')}</div> | |||
| : null | |||
| : <div className='text-xs text-[#DC6803]'>{t('common.provider.anthropic.notEnabled')}</div> | |||
| } | |||
| <Indicator color={ | |||
| providedOpenaiProvider?.is_valid | |||
| ? isValid | |||
| ? 'green' | |||
| : 'red' | |||
| : 'yellow' | |||
| } className='ml-2' /> | |||
| </div> | |||
| ) | |||
| } | |||
| { | |||
| !comingSoon && !isOpen && provider.provider_name !== ProviderName.ANTHROPIC && ( | |||
| <div className=' | |||
| px-3 h-[28px] bg-white border border-gray-200 rounded-md cursor-pointer | |||
| text-xs font-medium text-gray-700 flex items-center | |||
| @@ -98,6 +130,34 @@ const ProviderItem = ({ | |||
| </div> | |||
| ) | |||
| } | |||
| { | |||
| (!comingSoon && !isOpen && provider.provider_name === ProviderName.ANTHROPIC) | |||
| ? providedOpenaiProvider?.is_enabled | |||
| ? ( | |||
| <div className=' | |||
| px-3 h-[28px] bg-white border border-gray-200 rounded-md cursor-pointer | |||
| text-xs font-medium text-gray-700 flex items-center | |||
| ' onClick={() => providedOpenaiProvider.is_valid && onActive(id)}> | |||
| {providerTokenHasSetted() ? t('common.provider.editKey') : t('common.provider.addKey')} | |||
| </div> | |||
| ) | |||
| : ( | |||
| <Tooltip | |||
| htmlContent={<div className='w-[320px]'> | |||
| {t('common.provider.anthropic.enableTip')} | |||
| </div>} | |||
| position='bottom' | |||
| selector='anthropic-provider-enable-top-tooltip'> | |||
| <div className=' | |||
| px-3 h-[28px] bg-white border border-gray-200 rounded-md cursor-not-allowed | |||
| text-xs font-medium text-gray-700 flex items-center opacity-50 | |||
| '> | |||
| {t('common.provider.addKey')} | |||
| </div> | |||
| </Tooltip> | |||
| ) | |||
| : null | |||
| } | |||
| { | |||
| comingSoon && !isOpen && ( | |||
| <div className=' | |||
| @@ -147,6 +207,29 @@ const ProviderItem = ({ | |||
| /> | |||
| ) | |||
| } | |||
| { | |||
| provider.provider_name === ProviderName.ANTHROPIC && isOpen && ( | |||
| <AnthropicProvider | |||
| provider={provider} | |||
| onValidatedStatus={v => setValidatedStatus(v)} | |||
| onTokenChange={v => setToken(v)} | |||
| /> | |||
| ) | |||
| } | |||
| { | |||
| provider.provider_name === ProviderName.ANTHROPIC && !isOpen && providerTokenHasSetted() && providedOpenaiProvider?.is_valid && ( | |||
| <div className='px-4 py-3 text-[13px] font-medium text-gray-700'> | |||
| {t('common.provider.anthropic.using')} {providerNameMap[providedOpenaiProvider.provider_name as string]} | |||
| </div> | |||
| ) | |||
| } | |||
| { | |||
| provider.provider_name === ProviderName.ANTHROPIC && !isOpen && providerTokenHasSetted() && !providedOpenaiProvider?.is_valid && ( | |||
| <div className='px-4 py-3 bg-[#FFFAEB] text-[13px] font-medium text-gray-700'> | |||
| {t('common.provider.anthropic.enableTip')} | |||
| </div> | |||
| ) | |||
| } | |||
| </div> | |||
| ) | |||
| } | |||
| @@ -620,7 +620,7 @@ const Main: FC<IMainProps> = ({ | |||
| { | |||
| hasSetInputs && ( | |||
| <div className={cn(doShowSuggestion ? 'pb-[140px]' : (isResponsing ? 'pb-[113px]' : 'pb-[66px]'), 'relative grow h-[200px] pc:w-[794px] max-w-full mobile:w-full mx-auto mb-3.5 overflow-hidden')}> | |||
| <div className={cn(doShowSuggestion ? 'pb-[140px]' : (isResponsing ? 'pb-[113px]' : 'pb-[76px]'), 'relative grow h-[200px] pc:w-[794px] max-w-full mobile:w-full mx-auto mb-3.5 overflow-hidden')}> | |||
| <div className='h-full overflow-y-auto' ref={chatListDomRef}> | |||
| <Chat | |||
| chatList={chatList} | |||
| @@ -609,7 +609,7 @@ const Main: FC<IMainProps> = ({ | |||
| { | |||
| hasSetInputs && ( | |||
| <div className={cn(doShowSuggestion ? 'pb-[140px]' : (isResponsing ? 'pb-[113px]' : 'pb-[66px]'), 'relative grow h-[200px] pc:w-[794px] max-w-full mobile:w-full mx-auto mb-3.5 overflow-hidden')}> | |||
| <div className={cn(doShowSuggestion ? 'pb-[140px]' : (isResponsing ? 'pb-[113px]' : 'pb-[76px]'), 'relative grow h-[200px] pc:w-[794px] max-w-full mobile:w-full mx-auto mb-3.5 overflow-hidden')}> | |||
| <div className='h-full overflow-y-auto' ref={chatListDomRef}> | |||
| <Chat | |||
| chatList={chatList} | |||
| @@ -54,7 +54,7 @@ const translation = { | |||
| maxTokenTip: | |||
| 'Max tokens depending on the model. Prompt and completion share this limit. One token is roughly 1 English character.', | |||
| maxTokenSettingTip: 'Your max token setting is high, potentially limiting space for prompts, queries, and data. Consider setting it below 2/3.', | |||
| setToCurrentModelMaxTokenTip: 'Max token is updated to the maximum token of the current model 4,000.', | |||
| setToCurrentModelMaxTokenTip: 'Max token is updated to the maximum token of the current model {{maxToken}}.', | |||
| }, | |||
| tone: { | |||
| Creative: 'Creative', | |||
| @@ -180,6 +180,22 @@ const translation = { | |||
| useYourModel: 'Currently using own Model Provider.', | |||
| close: 'Close', | |||
| }, | |||
| anthropicHosted: { | |||
| anthropicHosted: 'Anthropic Claude', | |||
| onTrial: 'ON TRIAL', | |||
| exhausted: 'QUOTA EXHAUSTED', | |||
| desc: 'Powerful model, which excels at a wide range of tasks from sophisticated dialogue and creative content generation to detailed instruction.', | |||
| callTimes: 'Call times', | |||
| usedUp: 'Trial quota used up. Add own Model Provider.', | |||
| useYourModel: 'Currently using own Model Provider.', | |||
| close: 'Close', | |||
| }, | |||
| anthropic: { | |||
| using: 'The embedding capability is using', | |||
| enableTip: 'To enable the Anthropic model, you need to bind to OpenAI or Azure OpenAI Service first.', | |||
| notEnabled: 'Not enabled', | |||
| keyFrom: 'Get your API key from Anthropic', | |||
| }, | |||
| encrypted: { | |||
| front: 'Your API KEY will be encrypted and stored using', | |||
| back: ' technology.', | |||
| @@ -54,7 +54,7 @@ const translation = { | |||
| maxTokenTip: | |||
| '生成的最大令牌数取决于模型。提示和完成共享令牌数限制。一个令牌约等于 1 个英文或 半个中文字符。', | |||
| maxTokenSettingTip: '您设置的最大 tokens 数较大,可能会导致 prompt、用户问题、数据集内容没有 token 空间进行处理,建议设置到 2/3 以下。', | |||
| setToCurrentModelMaxTokenTip: '最大令牌数更新为当前模型最大的令牌数 4,000。', | |||
| setToCurrentModelMaxTokenTip: '最大令牌数更新为当前模型最大的令牌数 {{maxToken}}。', | |||
| }, | |||
| tone: { | |||
| Creative: '创意', | |||
| @@ -180,6 +180,22 @@ const translation = { | |||
| useYourModel: '当前正在使用你自己的模型供应商。', | |||
| close: '关闭', | |||
| }, | |||
| anthropicHosted: { | |||
| anthropicHosted: 'Anthropic Claude', | |||
| onTrial: '体验', | |||
| exhausted: '超出限额', | |||
| desc: '功能强大的模型,擅长执行从复杂对话和创意内容生成到详细指导的各种任务。', | |||
| callTimes: '调用次数', | |||
| usedUp: '试用额度已用完,请在下方添加自己的模型供应商', | |||
| useYourModel: '当前正在使用你自己的模型供应商。', | |||
| close: '关闭', | |||
| }, | |||
| anthropic: { | |||
| using: '嵌入能力正在使用', | |||
| enableTip: '要启用 Anthropic 模型,您需要先绑定 OpenAI 或 Azure OpenAI 服务。', | |||
| notEnabled: '未启用', | |||
| keyFrom: '从 Anthropic 获取您的 API 密钥', | |||
| }, | |||
| encrypted: { | |||
| front: '密钥将使用 ', | |||
| back: ' 技术进行加密和存储。', | |||
| @@ -59,14 +59,19 @@ export type Member = Pick<UserProfileResponse, 'id' | 'name' | 'email' | 'last_l | |||
| export enum ProviderName { | |||
| OPENAI = 'openai', | |||
| AZURE_OPENAI = 'azure_openai', | |||
| ANTHROPIC = 'anthropic', | |||
| } | |||
| export type ProviderAzureToken = { | |||
| openai_api_base?: string | |||
| openai_api_key?: string | |||
| } | |||
| export type ProviderAnthropicToken = { | |||
| anthropic_api_key?: string | |||
| } | |||
| export type ProviderTokenType = { | |||
| [ProviderName.OPENAI]: string | |||
| [ProviderName.AZURE_OPENAI]: ProviderAzureToken | |||
| [ProviderName.ANTHROPIC]: ProviderAnthropicToken | |||
| } | |||
| export type Provider = { | |||
| [Name in ProviderName]: { | |||
| @@ -3,7 +3,7 @@ import { del, get, patch, post, put } from './base' | |||
| import type { | |||
| AccountIntegrate, CommonResponse, DataSourceNotion, | |||
| IWorkspace, LangGeniusVersionResponse, Member, | |||
| OauthResponse, Provider, ProviderAzureToken, TenantInfoResponse, | |||
| OauthResponse, Provider, ProviderAnthropicToken, ProviderAzureToken, TenantInfoResponse, | |||
| UserProfileOriginResponse, | |||
| } from '@/models/common' | |||
| import type { | |||
| @@ -58,7 +58,7 @@ export const fetchProviders: Fetcher<Provider[] | null, { url: string; params: R | |||
| export const validateProviderKey: Fetcher<ValidateOpenAIKeyResponse, { url: string; body: { token: string } }> = ({ url, body }) => { | |||
| return post(url, { body }) as Promise<ValidateOpenAIKeyResponse> | |||
| } | |||
| export const updateProviderAIKey: Fetcher<UpdateOpenAIKeyResponse, { url: string; body: { token: string | ProviderAzureToken } }> = ({ url, body }) => { | |||
| export const updateProviderAIKey: Fetcher<UpdateOpenAIKeyResponse, { url: string; body: { token: string | ProviderAzureToken | ProviderAnthropicToken } }> = ({ url, body }) => { | |||
| return post(url, { body }) as Promise<UpdateOpenAIKeyResponse> | |||
| } | |||
| @@ -1,3 +1,8 @@ | |||
| export enum ProviderType { | |||
| openai = 'openai', | |||
| anthropic = 'anthropic', | |||
| } | |||
| export enum AppType { | |||
| 'chat' = 'chat', | |||
| 'completion' = 'completion', | |||