Unverified Commit 60e0bbd7 authored by zxhlyh's avatar zxhlyh Committed by GitHub

Feat/provider add zhipuai (#1192)

Co-authored-by: 's avatarJoel <iamjoel007@gmail.com>
parent 827c97f0
'use client'
import { useCallback, useEffect, useRef, useState } from 'react'
import { useRouter } from 'next/navigation'
import { useEffect, useRef } from 'react'
import useSWRInfinite from 'swr/infinite'
import { useTranslation } from 'react-i18next'
import AppCard from './AppCard'
......@@ -10,11 +9,7 @@ import type { AppListResponse } from '@/models/app'
import { fetchAppList } from '@/service/apps'
import { useAppContext } from '@/context/app-context'
import { NEED_REFRESH_APP_LIST_KEY } from '@/config'
import Confirm from '@/app/components/base/confirm/common'
import {
useAnthropicCheckPay,
useSparkCheckQuota,
} from '@/hooks/use-pay'
import { CheckModal } from '@/hooks/use-pay'
const getKey = (pageIndex: number, previousPageData: AppListResponse) => {
if (!pageIndex || previousPageData.has_more)
......@@ -27,15 +22,6 @@ const Apps = () => {
const { isCurrentWorkspaceManager } = useAppContext()
const { data, isLoading, setSize, mutate } = useSWRInfinite(getKey, fetchAppList, { revalidateFirstPage: false })
const anchorRef = useRef<HTMLDivElement>(null)
const router = useRouter()
const [showPayStatusModal, setShowPayStatusModal] = useState(true)
const anthropicConfirmInfo = useAnthropicCheckPay()
const sparkConfirmInfo = useSparkCheckQuota()
const handleCancelShowPayStatusModal = useCallback(() => {
setShowPayStatusModal(false)
router.replace('/', { forceOptimisticNavigation: false })
}, [router])
useEffect(() => {
document.title = `${t('app.title')} - Dify`
......@@ -64,33 +50,7 @@ const Apps = () => {
{data?.map(({ data: apps }) => apps.map(app => (
<AppCard key={app.id} app={app} onRefresh={mutate} />
)))}
{
showPayStatusModal && anthropicConfirmInfo && (
<Confirm
isShow
onCancel={handleCancelShowPayStatusModal}
onConfirm={handleCancelShowPayStatusModal}
type={anthropicConfirmInfo.type}
title={anthropicConfirmInfo.title}
showOperateCancel={false}
confirmText={(anthropicConfirmInfo.type === 'danger' && t('common.operation.ok')) || ''}
/>
)
}
{
showPayStatusModal && sparkConfirmInfo && (
<Confirm
isShow
onCancel={handleCancelShowPayStatusModal}
onConfirm={handleCancelShowPayStatusModal}
type={sparkConfirmInfo.type}
title={sparkConfirmInfo.title}
desc={sparkConfirmInfo.desc}
showOperateCancel={false}
confirmText={(sparkConfirmInfo.type === 'danger' && t('common.operation.ok')) || ''}
/>
)
}
<CheckModal />
</nav>
<div ref={anchorRef} className='h-0'> </div>
</>
......
......@@ -213,7 +213,7 @@ const ConfigModel: FC<IConfigModelProps> = ({
const handleParamChange = (key: string, value: number) => {
const currParamsRule = getAllParams()[provider]?.[modelId]
let notOutRangeValue = parseFloat(value.toFixed(2))
let notOutRangeValue = parseFloat((value || 0).toFixed(2))
notOutRangeValue = Math.max(currParamsRule[key].min, notOutRangeValue)
notOutRangeValue = Math.min(currParamsRule[key].max, notOutRangeValue)
......
'use client'
import type { FC } from 'react'
import React from 'react'
import React, { useEffect } from 'react'
import Tooltip from '@/app/components/base/tooltip'
import Slider from '@/app/components/base/slider'
export const getFitPrecisionValue = (num: number, precision: number | null) => {
if (!precision || !(`${num}`).includes('.'))
return num
const currNumPrecision = (`${num}`).split('.')[1].length
if (currNumPrecision > precision)
return parseFloat(num.toFixed(precision))
return num
}
export type IParamIteProps = {
id: string
name: string
......@@ -12,10 +23,26 @@ export type IParamIteProps = {
step?: number
min?: number
max: number
precision: number | null
onChange: (key: string, value: number) => void
}
const ParamIte: FC<IParamIteProps> = ({ id, name, tip, step = 0.1, min = 0, max, value, onChange }) => {
const TIMES_TEMPLATE = '1000000000000'
const ParamItem: FC<IParamIteProps> = ({ id, name, tip, step = 0.1, min = 0, max, precision, value, onChange }) => {
const getToIntTimes = (num: number) => {
if (precision)
return parseInt(TIMES_TEMPLATE.slice(0, precision + 1), 10)
if (num < 5)
return 10
return 1
}
const times = getToIntTimes(max)
useEffect(() => {
if (precision)
onChange(id, getFitPrecisionValue(value, precision))
}, [value, precision])
return (
<div className="flex items-center justify-between">
<div className="flex items-center">
......@@ -29,17 +56,21 @@ const ParamIte: FC<IParamIteProps> = ({ id, name, tip, step = 0.1, min = 0, max,
</div>
<div className="flex items-center">
<div className="mr-4 w-[120px]">
<Slider value={max < 5 ? value * 10 : value} min={min < 0 ? min * 10 : min} max={max < 5 ? max * 10 : max} onChange={value => onChange(id, value / (max < 5 ? 10 : 1))} />
<Slider value={value * times} min={min * times} max={max * times} onChange={(value) => {
onChange(id, value / times)
}} />
</div>
<input type="number" min={min} max={max} step={step} className="block w-[64px] h-9 leading-9 rounded-lg border-0 pl-1 pl py-1.5 bg-gray-50 text-gray-900 placeholder:text-gray-400 focus:ring-1 focus:ring-inset focus:ring-primary-600" value={value} onChange={(e) => {
const value = parseFloat(e.target.value)
if (value < min || value > max)
return
let value = getFitPrecisionValue(isNaN(parseFloat(e.target.value)) ? min : parseFloat(e.target.value), precision)
if (value < min)
value = min
if (value > max)
value = max
onChange(id, value)
}} />
</div>
</div>
)
}
export default React.memo(ParamIte)
export default React.memo(ParamItem)
This diff is collapsed.
This diff is collapsed.
// GENERATE BY script
// DON NOT EDIT IT MANUALLY
import * as React from 'react'
import data from './Zhipuai.json'
import IconBase from '@/app/components/base/icons/IconBase'
import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
props,
ref,
) => <IconBase {...props} ref={ref} data={data as IconData} />)
Icon.displayName = 'Zhipuai'
export default Icon
This diff is collapsed.
// GENERATE BY script
// DON NOT EDIT IT MANUALLY
import * as React from 'react'
import data from './ZhipuaiText.json'
import IconBase from '@/app/components/base/icons/IconBase'
import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
props,
ref,
) => <IconBase {...props} ref={ref} data={data as IconData} />)
Icon.displayName = 'ZhipuaiText'
export default Icon
// GENERATE BY script
// DON NOT EDIT IT MANUALLY
import * as React from 'react'
import data from './ZhipuaiTextCn.json'
import IconBase from '@/app/components/base/icons/IconBase'
import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
props,
ref,
) => <IconBase {...props} ref={ref} data={data as IconData} />)
Icon.displayName = 'ZhipuaiTextCn'
export default Icon
......@@ -29,3 +29,6 @@ export { default as ReplicateText } from './ReplicateText'
export { default as Replicate } from './Replicate'
export { default as XorbitsInferenceText } from './XorbitsInferenceText'
export { default as XorbitsInference } from './XorbitsInference'
export { default as ZhipuaiTextCn } from './ZhipuaiTextCn'
export { default as ZhipuaiText } from './ZhipuaiText'
export { default as Zhipuai } from './Zhipuai'
......@@ -11,6 +11,7 @@ import chatglm from './chatglm'
import xinference from './xinference'
import openllm from './openllm'
import localai from './localai'
import zhipuai from './zhipuai'
export default {
openai,
......@@ -26,4 +27,5 @@ export default {
xinference,
openllm,
localai,
zhipuai,
}
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: <Zhipuai className='w-full h-full' />,
},
item: {
key: ProviderEnum.zhipuai,
titleIcon: {
'en': <ZhipuaiText className='-ml-1 h-7' />,
'zh-Hans': <ZhipuaiTextCn className='h-8' />,
},
},
modal: {
key: ProviderEnum.zhipuai,
title: {
'en': 'ZHIPU AI',
'zh-Hans': '智谱 AI',
},
icon: <Zhipuai className='w-6 h-6' />,
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
......@@ -42,6 +42,7 @@ export enum ProviderEnum {
'xinference' = 'xinference',
'openllm' = 'openllm',
'localai' = 'localai',
'zhipuai' = 'zhipuai',
}
export type ProviderConfigItem = {
......
......@@ -78,8 +78,9 @@ const ModelPage = () => {
config.azure_openai,
config.replicate,
config.huggingface_hub,
config.minimax,
config.zhipuai,
config.spark,
config.minimax,
config.tongyi,
config.wenxin,
config.chatglm,
......@@ -91,8 +92,9 @@ const ModelPage = () => {
else {
modelList = [
config.huggingface_hub,
config.minimax,
config.zhipuai,
config.spark,
config.minimax,
config.azure_openai,
config.replicate,
config.tongyi,
......
......@@ -19,6 +19,10 @@ const TIP_MAP: { [k: string]: TypeWithI18N } = {
'en': 'Earn 3 million tokens for free',
'zh-Hans': '免费获取 300 万个 token',
},
[ProviderEnumValue.zhipuai]: {
'en': 'Earn 10 million tokens for free',
'zh-Hans': '免费获取 1000 万个 token',
},
}
type FreeQuotaProps = {
modelItem: ProviderConfigItem
......
......@@ -34,7 +34,7 @@ const Setting: FC<SettingProps> = ({
return (
<div className='flex items-center'>
{
(modelItem.key === ProviderEnum.minimax || modelItem.key === ProviderEnum.spark) && systemFree && !systemFree?.is_valid && !IS_CE_EDITION && locale === 'zh-Hans' && (
(modelItem.key === ProviderEnum.minimax || modelItem.key === ProviderEnum.spark || modelItem.key === ProviderEnum.zhipuai) && systemFree && !systemFree?.is_valid && !IS_CE_EDITION && locale === 'zh-Hans' && (
<FreeQuota
modelItem={modelItem}
onUpdate={onUpdate}
......
'use client'
import { useEffect, useState } from 'react'
import { useCallback, useEffect, useState } from 'react'
import { useRouter, useSearchParams } from 'next/navigation'
import { useTranslation } from 'react-i18next'
import useSWR from 'swr'
import { useSearchParams } from 'next/navigation'
import { useContext } from 'use-context-selector'
import I18n from '@/context/i18n'
import { ProviderEnum } from '@/app/components/header/account-setting/model-page/declarations'
import { fetchSparkFreeQuotaVerify } from '@/service/common'
import { fetchFreeQuotaVerify } from '@/service/common'
import type { ConfirmCommonProps } from '@/app/components/base/confirm/common'
import Confirm from '@/app/components/base/confirm/common'
export type ConfirmType = Pick<ConfirmCommonProps, 'type' | 'title' | 'desc'>
......@@ -32,40 +33,92 @@ export const useAnthropicCheckPay = () => {
}
const QUOTA_RECEIVE_STATUS = {
success: {
'en': 'Anthropic',
'zh-Hans': '领取成功,将在 5 分钟后自动增加配额',
[ProviderEnum.spark]: {
success: {
'en': 'Successful collection, the quota will be automatically increased after 5 minutes.',
'zh-Hans': '领取成功,将在 5 分钟后自动增加配额',
},
fail: {
'en': 'Failure to collect',
'zh-Hans': '领取失败',
},
},
fail: {
'en': 'Anthropic',
'zh-Hans': '领取失败',
[ProviderEnum.zhipuai]: {
success: {
'en': 'Successful collection',
'zh-Hans': '领取成功',
},
fail: {
'en': 'Failure to collect',
'zh-Hans': '领取失败',
},
},
}
export const useSparkCheckQuota = () => {
const FREE_CHECK_PROVIDER = [ProviderEnum.spark, ProviderEnum.zhipuai]
export const useCheckFreeQuota = () => {
const { locale } = useContext(I18n)
const router = useRouter()
const [shouldVerify, setShouldVerify] = useState(false)
const { data } = useSWR(
shouldVerify
? `/workspaces/current/model-providers/${ProviderEnum.spark}/free-quota-qualification-verify`
: null,
fetchSparkFreeQuotaVerify,
)
const searchParams = useSearchParams()
const type = searchParams.get('type')
const provider = searchParams.get('provider')
const provider = searchParams.get('provider') as (ProviderEnum.spark | ProviderEnum.zhipuai)
const result = searchParams.get('result')
const token = searchParams.get('token')
const { data, error } = useSWR(
shouldVerify
? `/workspaces/current/model-providers/${provider}/free-quota-qualification-verify?token=${token}`
: null,
fetchFreeQuotaVerify,
)
useEffect(() => {
if (error)
router.replace('/', { forceOptimisticNavigation: false })
}, [error, router])
useEffect(() => {
if (type === 'provider_apply_callback' && provider === ProviderEnum.spark && result === 'success')
if (type === 'provider_apply_callback' && FREE_CHECK_PROVIDER.includes(provider) && result === 'success')
setShouldVerify(true)
}, [type, provider, result])
return data
return (data && provider)
? {
type: data.flag ? 'success' : 'danger',
title: data.flag ? QUOTA_RECEIVE_STATUS.success[locale] : QUOTA_RECEIVE_STATUS.fail[locale],
title: data.flag ? QUOTA_RECEIVE_STATUS[provider].success[locale] : QUOTA_RECEIVE_STATUS[provider].fail[locale],
desc: !data.flag ? data.reason : undefined,
}
: null
}
export const CheckModal = () => {
const router = useRouter()
const { t } = useTranslation()
const [showPayStatusModal, setShowPayStatusModal] = useState(true)
const anthropicConfirmInfo = useAnthropicCheckPay()
const freeQuotaConfirmInfo = useCheckFreeQuota()
const handleCancelShowPayStatusModal = useCallback(() => {
setShowPayStatusModal(false)
router.replace('/', { forceOptimisticNavigation: false })
}, [router])
const confirmInfo = anthropicConfirmInfo || freeQuotaConfirmInfo
if (!confirmInfo || !showPayStatusModal)
return null
return (
<Confirm
isShow
onCancel={handleCancelShowPayStatusModal}
onConfirm={handleCancelShowPayStatusModal}
type={confirmInfo.type}
title={confirmInfo.title}
desc={confirmInfo.desc}
showOperateCancel={false}
confirmText={(confirmInfo.type === 'danger' && t('common.operation.ok')) || ''}
/>
)
}
......@@ -185,6 +185,6 @@ export const fetchDocumentsLimit: Fetcher<DocumentsLimitResponse, string> = (url
return get<DocumentsLimitResponse>(url)
}
export const fetchSparkFreeQuotaVerify: Fetcher<{ result: string; flag: boolean; reason: string }, string> = (url) => {
export const fetchFreeQuotaVerify: Fetcher<{ result: string; flag: boolean; reason: string }, string> = (url) => {
return get(url) as Promise<{ result: string; flag: boolean; reason: string }>
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment